1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.download.home.list; 6 7 import static org.mockito.ArgumentMatchers.anyString; 8 import static org.mockito.ArgumentMatchers.eq; 9 import static org.mockito.Mockito.times; 10 import static org.mockito.Mockito.verify; 11 import static org.mockito.Mockito.when; 12 13 import org.junit.After; 14 import org.junit.Assert; 15 import org.junit.Before; 16 import org.junit.Rule; 17 import org.junit.Test; 18 import org.junit.runner.RunWith; 19 import org.mockito.Mock; 20 import org.mockito.junit.MockitoJUnit; 21 import org.mockito.junit.MockitoRule; 22 import org.robolectric.annotation.Config; 23 24 import org.chromium.base.test.BaseRobolectricTestRunner; 25 import org.chromium.base.test.util.JniMocker; 26 import org.chromium.chrome.browser.download.home.DownloadManagerUiConfig; 27 import org.chromium.chrome.browser.download.home.JustNowProvider; 28 import org.chromium.chrome.browser.download.home.StableIds; 29 import org.chromium.chrome.browser.download.home.filter.Filters; 30 import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource; 31 import org.chromium.chrome.browser.download.home.filter.TypeOfflineItemFilter; 32 import org.chromium.chrome.browser.download.home.list.ListItem.OfflineItemListItem; 33 import org.chromium.chrome.browser.download.home.list.ListItem.SectionHeaderListItem; 34 import org.chromium.chrome.browser.download.home.list.ListItem.SectionHeaderType; 35 import org.chromium.chrome.browser.download.home.list.mutator.DateOrderedListMutator; 36 import org.chromium.chrome.browser.download.home.list.mutator.ListMutationController; 37 import org.chromium.components.browser_ui.util.date.CalendarFactory; 38 import org.chromium.components.offline_items_collection.LegacyHelpers; 39 import org.chromium.components.offline_items_collection.OfflineItem; 40 import org.chromium.components.offline_items_collection.OfflineItemFilter; 41 import org.chromium.components.offline_items_collection.OfflineItemSchedule; 42 import org.chromium.components.offline_items_collection.OfflineItemState; 43 import org.chromium.components.url_formatter.SchemeDisplay; 44 import org.chromium.components.url_formatter.UrlFormatter; 45 import org.chromium.components.url_formatter.UrlFormatterJni; 46 import org.chromium.ui.modelutil.ListObservable.ListObserver; 47 48 import java.util.Arrays; 49 import java.util.Calendar; 50 import java.util.Collections; 51 import java.util.Date; 52 53 /** Unit tests for the DateOrderedListMutator class. */ 54 @RunWith(BaseRobolectricTestRunner.class) 55 @Config(manifest = Config.NONE) 56 public class DateOrderedListMutatorTest { 57 @Mock 58 private OfflineItemFilterSource mSource; 59 60 @Mock 61 private ListObserver<Void> mObserver; 62 @Rule 63 public JniMocker mJniMocker = new JniMocker(); 64 @Mock 65 private UrlFormatter.Natives mUrlFormatterJniMock; 66 67 private ListItemModel mModel; 68 69 @Rule 70 public MockitoRule mMockitoRule = MockitoJUnit.rule(); 71 72 private ListMutationController mMutationController; 73 74 @Before setUp()75 public void setUp() { 76 mModel = new ListItemModel(); 77 mJniMocker.mock(UrlFormatterJni.TEST_HOOKS, mUrlFormatterJniMock); 78 when(mUrlFormatterJniMock.formatStringUrlForSecurityDisplay( 79 anyString(), eq(SchemeDisplay.OMIT_HTTP_AND_HTTPS))) 80 .then(inv -> inv.getArgument(0)); 81 } 82 83 @After tearDown()84 public void tearDown() { 85 mModel = null; 86 } 87 88 /** 89 * Action List 90 * 1. Set() [ ] 91 */ 92 @Test testNoItemsAndSetup()93 public void testNoItemsAndSetup() { 94 when(mSource.getItems()).thenReturn(Collections.emptySet()); 95 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 96 verify(mSource, times(1)).addObserver(list); 97 98 Assert.assertEquals(0, mModel.size()); 99 } 100 101 /** 102 * Action List 103 * 1. Set(item1 @ 1:00 1/1/2018) [ DATE @ 0:00 1/1/2018, 104 * item1 @ 1:00 1/1/2018 ] 105 */ 106 @Test testSingleItem()107 public void testSingleItem() { 108 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); 109 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 110 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 111 112 Assert.assertEquals(2, mModel.size()); 113 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 114 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1); 115 } 116 117 /** 118 * Action List 119 * 1. Set(item1 @ 2:00 1/1/2018, [ DATE @ 0:00 1/1/2018, 120 * item2 @ 1:00 1/1/2018) 121 * item1 @ 2:00 1/1/2018, 122 * item2 @ 1:00 1/1/2018 ] 123 */ 124 @Test testTwoItemsSameDay()125 public void testTwoItemsSameDay() { 126 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.VIDEO); 127 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); 128 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 129 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 130 131 Assert.assertEquals(3, mModel.size()); 132 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 133 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1); 134 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 1), item2); 135 } 136 137 /** 138 * Action List 139 * 1. Set(item1 @ 2:00 1/1/2018 Video, [ DATE @ 0:00 1/1/2018, 140 * item2 @ 1:00 1/1/2018 Audio) item1 @ 2:00 1/1/2018, 141 * item2 @ 1:00 1/1/2018 ] 142 */ 143 @Test testTwoItemsSameDayDifferentSection()144 public void testTwoItemsSameDayDifferentSection() { 145 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 2), OfflineItemFilter.VIDEO); 146 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.AUDIO); 147 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 148 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 149 150 Assert.assertEquals(3, mModel.size()); 151 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 152 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 2), item1); 153 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 1), item2); 154 } 155 156 /** 157 * Action List 158 * 1. Set(item1 @ 1:00 1/1/2018 [ DATE Just Now, 159 * IN_PROGRESS) 160 * item1 @ 1:00 1/1/2018 ] 161 */ 162 @Test testSingleItemInJustNowSection()163 public void testSingleItemInJustNowSection() { 164 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); 165 item1.state = OfflineItemState.IN_PROGRESS; 166 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 167 DateOrderedListMutator list = createMutatorWithJustNowProvider(); 168 169 Assert.assertEquals(2, mModel.size()); 170 assertJustNowSection(mModel.get(0)); 171 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1); 172 } 173 174 /** 175 * Action List 176 * 1. Set(item1 @ 1:00 1/1/2018 [ DATE Just Now, 177 * Video IN_PROGRESS, 178 * item2 @ 1:00 1/1/2018 item1 @ 1:00 1/1/2018, 179 * Audio COMPLETE Recent) 180 * item2 @ 1:00 1/1/2018 ] 181 */ 182 @Test testMultipleSectionsInJustNowSection()183 public void testMultipleSectionsInJustNowSection() { 184 Calendar calendar = buildCalendar(2018, 1, 1, 1); 185 OfflineItem item1 = buildItem("1", calendar, OfflineItemFilter.VIDEO); 186 OfflineItem item2 = buildItem("2", calendar, OfflineItemFilter.AUDIO); 187 item1.state = OfflineItemState.IN_PROGRESS; 188 item2.state = OfflineItemState.COMPLETE; 189 item2.completionTimeMs = item2.creationTimeMs; 190 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 191 DateOrderedListMutator list = 192 createMutatorWithJustNowProvider(buildJustNowProvider(calendar.getTime())); 193 194 Assert.assertEquals(3, mModel.size()); 195 assertJustNowSection(mModel.get(0)); 196 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1); 197 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 1), item2); 198 } 199 200 /** 201 * Action List 202 * 1. Set(item1 @ 0:10 1/2/2018 [ DATE Just Now, 203 * Video COMPLETE, 204 * item2 @ 23:55 1/1/2018 [ DATE Just Now, 205 * Video COMPLETE, 206 * 207 * item3 @ 10:00 1/1/2018 DATE 1/1/2018 208 * Audio COMPLETE) item3 @ 1:00 1/1/2018 ] 209 */ 210 @Test testRecentItemBeforeMidnightShowsInJustNowSection()211 public void testRecentItemBeforeMidnightShowsInJustNowSection() { 212 Calendar calendar1 = CalendarFactory.get(); 213 calendar1.set(2018, 1, 2, 0, 10); 214 Calendar calendar2 = CalendarFactory.get(); 215 calendar2.set(2018, 1, 1, 23, 50); 216 OfflineItem item1 = buildItem("1", calendar1, OfflineItemFilter.VIDEO); 217 OfflineItem item2 = buildItem("2", calendar2, OfflineItemFilter.AUDIO); 218 OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 1, 10), OfflineItemFilter.AUDIO); 219 item1.completionTimeMs = item1.creationTimeMs; 220 item2.completionTimeMs = item2.creationTimeMs; 221 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2, item3)); 222 223 Calendar now = CalendarFactory.get(); 224 now.set(2018, 1, 2, 0, 15); 225 createMutatorWithJustNowProvider(buildJustNowProvider(now.getTime())); 226 Assert.assertEquals(5, mModel.size()); 227 assertJustNowSection(mModel.get(0)); 228 assertOfflineItem(mModel.get(1), calendar1, item1); 229 assertOfflineItem(mModel.get(2), calendar2, item2); 230 assertSectionHeader(mModel.get(3), buildCalendar(2018, 1, 1, 0), true); 231 assertOfflineItem(mModel.get(4), buildCalendar(2018, 1, 1, 10), item3); 232 } 233 234 /** 235 * Action List 236 * 1. Set(item1 @ 1:00 1/1/2018 [ DATE Just Now, 237 * PAUSED) 238 * item1 @ 1:00 1/1/2018 ] 239 * 2. Update(item1 @ 1:00 1/1/2018 [ DATE Just Now, 240 * Resume --> IN_PROGRESS) 241 * item1 @ 1:00 1/1/2018 ] 242 * 3. Update(item1 @ 1:00 1/1/2018 [ DATE Just Now, 243 * COMPLETE, completion time now) 244 * item1 @ 1:00 1/1/2018 ] 245 * 4. Update(item1 @ 1:00 1/1/2018 [ DATE Just Now, 246 * COMPLETE, completion time 1/1/2017) 247 * item1 @ 1:00 1/1/2018 ] 248 */ 249 @Test testItemDoesNotMoveOutOfJustNowSection()250 public void testItemDoesNotMoveOutOfJustNowSection() { 251 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); 252 item1.state = OfflineItemState.PAUSED; 253 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 254 DateOrderedListMutator list = createMutatorWithJustNowProvider(); 255 mModel.addObserver(mObserver); 256 257 Assert.assertEquals(2, mModel.size()); 258 assertJustNowSection(mModel.get(0)); 259 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), item1); 260 261 // Resume the download. 262 OfflineItem update1 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); 263 update1.state = OfflineItemState.IN_PROGRESS; 264 when(mSource.getItems()).thenReturn(Collections.singletonList(update1)); 265 list.onItemUpdated(item1, update1); 266 267 Assert.assertEquals(2, mModel.size()); 268 assertJustNowSection(mModel.get(0)); 269 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), update1); 270 271 // Complete the download. 272 OfflineItem update2 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); 273 update2.state = OfflineItemState.COMPLETE; 274 update2.completionTimeMs = new Date().getTime(); 275 when(mSource.getItems()).thenReturn(Collections.singletonList(update2)); 276 list.onItemUpdated(update1, update2); 277 278 Assert.assertEquals(2, mModel.size()); 279 assertJustNowSection(mModel.get(0)); 280 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), update2); 281 282 // Too much time has passed since completion of the download. 283 OfflineItem update3 = buildItem("1", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.VIDEO); 284 update3.state = OfflineItemState.COMPLETE; 285 update3.completionTimeMs = buildCalendar(2017, 1, 1, 1).getTimeInMillis(); 286 when(mSource.getItems()).thenReturn(Collections.singletonList(update3)); 287 list.onItemUpdated(update2, update3); 288 289 Assert.assertEquals(2, mModel.size()); 290 assertJustNowSection(mModel.get(0)); 291 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 1), update3); 292 } 293 294 /** 295 * Action List 296 * 1. Set(item1 @ 1:00 2/1/2018 [ DATE Just Now, 297 * Video IN_PROGRESS, 298 * item2 @ 1:00 1/1/2018 item1 @ 1:00 2/1/2018, 299 * Audio COMPLETE) DATE 1/1/2018 300 * item2 @ 1:00 1/1/2018 ] 301 */ 302 @Test testJustNowSectionWithOtherDates()303 public void testJustNowSectionWithOtherDates() { 304 OfflineItem item1 = buildItem("1", buildCalendar(2018, 2, 1, 1), OfflineItemFilter.VIDEO); 305 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.AUDIO); 306 item1.state = OfflineItemState.IN_PROGRESS; 307 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 308 DateOrderedListMutator list = createMutatorWithJustNowProvider(); 309 310 Assert.assertEquals(4, mModel.size()); 311 assertJustNowSection(mModel.get(0)); 312 assertOfflineItem(mModel.get(1), buildCalendar(2018, 2, 1, 1), item1); 313 assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), true); 314 assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 1), item2); 315 } 316 317 /** 318 * Action List 319 * 1. Set(item1 @ 1:00 1/1/2018 [ Header Scheduled for later, 320 * Scheduled for later, item1 @ 1:00 1/1/2018 321 * item2 @ 1:00 2/1/2018 Header Just Now 322 * Video IN_PROGRESS item2 @ 1:00 2/1/2018 323 * item3 @ 1:00 1/1/2018 DATE 1/1/2018 324 * Audio COMPLETE) item3 @ 1:00 1/1/2018 ] 325 */ 326 @Test testScheduledForLaterSection()327 public void testScheduledForLaterSection() { 328 Calendar calendar = buildCalendar(2018, 1, 1, 1); 329 OfflineItem item1 = buildItem("1", calendar, OfflineItemFilter.VIDEO); 330 item1.schedule = new OfflineItemSchedule(false, calendar.getTimeInMillis() + 1000); 331 OfflineItem item2 = buildItem("1", buildCalendar(2018, 2, 1, 1), OfflineItemFilter.VIDEO); 332 OfflineItem item3 = buildItem("2", buildCalendar(2018, 1, 1, 1), OfflineItemFilter.AUDIO); 333 item2.state = OfflineItemState.IN_PROGRESS; 334 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2, item3)); 335 DateOrderedListMutator list = createMutatorWithJustNowProvider(); 336 337 Assert.assertEquals(6, mModel.size()); 338 assertScheduledLaterHeader(mModel.get(0)); 339 assertOfflineItem(mModel.get(1), calendar, item1); 340 assertJustNowSection(mModel.get(2)); 341 assertOfflineItem(mModel.get(3), buildCalendar(2018, 2, 1, 1), item2); 342 assertSectionHeader(mModel.get(4), buildCalendar(2018, 1, 1, 0), true); 343 assertOfflineItem(mModel.get(5), buildCalendar(2018, 1, 1, 1), item3); 344 } 345 346 /** 347 * Action List 348 * 1. Set(item1 @ 0:00 1/2/2018, [ DATE @ 0:00 1/2/2018, 349 * item2 @ 0:00 1/1/2018) 350 * item1 @ 0:00 1/2/2018, 351 * DATE @ 0:00 1/1/2018, 352 * 353 * item2 @ 0:00 1/1/2018 ] 354 */ 355 @Test testTwoItemsDifferentDayMatchHeader()356 public void testTwoItemsDifferentDayMatchHeader() { 357 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 0), OfflineItemFilter.VIDEO); 358 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 0), OfflineItemFilter.AUDIO); 359 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 360 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 361 362 Assert.assertEquals(4, mModel.size()); 363 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), false); 364 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 0), item1); 365 assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), true); 366 assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 0), item2); 367 } 368 369 /** 370 * Action List 371 * 1. Set(item1 @ 4:00 1/1/2018, [ DATE @ 0:00 1/1/2018, 372 * item2 @ 5:00 1/1/2018) 373 * item2 @ 5:00 1/1/2018, 374 * item1 @ 4:00 1/1/2018 ] 375 */ 376 @Test testTwoItemsSameDayOutOfOrder()377 public void testTwoItemsSameDayOutOfOrder() { 378 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 379 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO); 380 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 381 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 382 383 Assert.assertEquals(3, mModel.size()); 384 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 385 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 5), item2); 386 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 4), item1); 387 } 388 389 /** 390 * Action List 391 * 1. Set(item1 @ 4:00 1/2/2018 Video, [ DATE @ 0:00 1/2/2018, 392 * item2 @ 5:00 1/1/2018 Video) 393 * item2 @ 4:00 1/2/2018, 394 * DATE @ 0:00 1/1/2018, 395 * item1 @ 5:00 1/1/2018 ] 396 */ 397 @Test testTwoItemsDifferentDaySameSection()398 public void testTwoItemsDifferentDaySameSection() { 399 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO); 400 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO); 401 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 402 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 403 404 Assert.assertEquals(4, mModel.size()); 405 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), false); 406 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 4), item1); 407 assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), true); 408 assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 5), item2); 409 } 410 411 /** 412 * Action List 413 * 1. Set(item1 @ 4:00 1/2/2018 Video, [ DATE @ 0:00 1/2/2018, 414 * item2 @ 5:00 1/1/2018 Page ) 415 * item2 @ 4:00 1/2/2018, 416 * DATE @ 0:00 1/1/2018, 417 * item1 @ 5:00 1/1/2018 ] 418 */ 419 @Test testTwoItemsDifferentDayDifferentSection()420 public void testTwoItemsDifferentDayDifferentSection() { 421 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO); 422 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.PAGE); 423 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 424 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 425 426 Assert.assertEquals(4, mModel.size()); 427 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), false); 428 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 4), item1); 429 assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), true); 430 assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 5), item2); 431 } 432 433 /** 434 * Action List 435 * 1. Set(item1 @ 4:00 1/1/2018, [ DATE @ 0:00 1/2/2018, 436 * item2 @ 3:00 1/2/2018) 437 * item2 @ 3:00 1/2/2018, 438 * DATE @ 0:00 1/1/2018, 439 * item1 @ 4:00 1/1/2018 ] 440 */ 441 @Test testTwoItemsDifferentDayOutOfOrder()442 public void testTwoItemsDifferentDayOutOfOrder() { 443 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 444 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO); 445 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 446 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 447 448 Assert.assertEquals(4, mModel.size()); 449 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), false); 450 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 3), item2); 451 assertSectionHeader(mModel.get(2), buildCalendar(2018, 1, 1, 0), true); 452 assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 4), item1); 453 } 454 455 /** 456 * Action List 457 * 1. Set() [ ] 458 * 459 * 2. Add(item1 @ 4:00 1/1/2018) [ DATE @ 0:00 1/1/2018, 460 * item1 @ 4:00 1/1/2018 ] 461 */ 462 @Test testAddItemToEmptyList()463 public void testAddItemToEmptyList() { 464 when(mSource.getItems()).thenReturn(Collections.emptySet()); 465 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 466 mModel.addObserver(mObserver); 467 468 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 469 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 470 list.onItemsAdded(Collections.singletonList(item1)); 471 472 Assert.assertEquals(2, mModel.size()); 473 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item1); 474 } 475 476 /** 477 * Action List 478 * 1. Set(item1 @ 1:00 1/2/2018) [ DATE @ 0:00 1/2/2018, 479 * item1 @ 1:00 1/2/2018 ] 480 * 2. Add(item2 @ 2:00 1/2/2018) [ DATE @ 0:00 1/2/2018, 481 * item2 @ 2:00 1/2/2018 482 * item1 @ 1:00 1/2/2018 ] 483 * 3. Add(item3 @ 2:00 1/3/2018) [ DATE @ 0:00 1/3/2018, 484 * item3 @ 2:00 1/3/2018 485 * DATE @ 0:00 1/2/2018, 486 * item2 @ 2:00 1/2/2018 487 * item1 @ 1:00 1/2/2018 ] 488 */ 489 @Test testAddFirstItemToList()490 public void testAddFirstItemToList() { 491 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 1), OfflineItemFilter.VIDEO); 492 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 493 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 494 mModel.addObserver(mObserver); 495 496 // Add an item on the same day that will be placed first. 497 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); 498 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 499 list.onItemsAdded(Collections.singletonList(item2)); 500 501 Assert.assertEquals(3, mModel.size()); 502 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 2), item2); 503 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 1), item1); 504 505 // Add an item on an earlier day that will be placed first. 506 OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 3, 2), OfflineItemFilter.VIDEO); 507 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2, item3)); 508 list.onItemsAdded(Collections.singletonList(item3)); 509 510 Assert.assertEquals(5, mModel.size()); 511 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 3, 2), item3); 512 assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 2, 2), item2); 513 assertOfflineItem(mModel.get(4), buildCalendar(2018, 1, 2, 1), item1); 514 } 515 516 /** 517 * Action List 518 * 1. Set(item1 @ 4:00 1/2/2018) [ DATE @ 0:00 1/2/2018, 519 * item1 @ 4:00 1/2/2018 ] 520 * 521 * 2. Add(item2 @ 3:00 1/2/2018) [ DATE @ 0:00 1/2/2018, 522 * item1 @ 4:00 1/2/2018 523 * item2 @ 3:00 1/2/2018 ] 524 * 525 * 3. Add(item3 @ 4:00 1/1/2018) [ DATE @ 0:00 1/2/2018, 526 * item1 @ 4:00 1/2/2018 527 * item2 @ 3:00 1/2/2018, 528 * DATE @ 0:00 1/1/2018, 529 * item3 @ 4:00 1/1/2018 530 */ 531 @Test testAddLastItemToList()532 public void testAddLastItemToList() { 533 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO); 534 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 535 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 536 mModel.addObserver(mObserver); 537 538 // Add an item on the same day that will be placed last. 539 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO); 540 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 541 list.onItemsAdded(Collections.singletonList(item2)); 542 543 Assert.assertEquals(3, mModel.size()); 544 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 4), item1); 545 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 3), item2); 546 547 // Add an item on a later day that will be placed last. 548 OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 549 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2, item3)); 550 list.onItemsAdded(Collections.singletonList(item3)); 551 552 Assert.assertEquals(5, mModel.size()); 553 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 4), item1); 554 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 3), item2); 555 assertOfflineItem(mModel.get(4), buildCalendar(2018, 1, 1, 4), item3); 556 } 557 558 /** 559 * Action List 560 * 1. Set(item1 @ 2:00 1/2/2018) [ DATE @ 0:00 1/2/2018, 561 * 562 * item1 @ 2:00 1/2/2018 ] 563 * 564 * 2. Remove(item1) [ ] 565 */ 566 @Test testRemoveOnlyItemInList()567 public void testRemoveOnlyItemInList() { 568 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); 569 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 570 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 571 mModel.addObserver(mObserver); 572 573 when(mSource.getItems()).thenReturn(Collections.emptySet()); 574 list.onItemsRemoved(Collections.singletonList(item1)); 575 576 Assert.assertEquals(0, mModel.size()); 577 } 578 579 /** 580 * Action List 581 * 1. Set(item1 @ 3:00 1/2/2018, [ DATE @ 0:00 1/2/2018, 582 * item2 @ 2:00 1/2/2018) 583 * item1 @ 3:00 1/2/2018, 584 * item2 @ 2:00 1/2/2018 ] 585 * 586 * 2. Remove(item1) [ DATE @ 0:00 1/2/2018, 587 * item2 @ 2:00 1/2/2018 ] 588 */ 589 @Test testRemoveFirstItemInListSameDay()590 public void testRemoveFirstItemInListSameDay() { 591 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO); 592 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); 593 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 594 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 595 mModel.addObserver(mObserver); 596 597 when(mSource.getItems()).thenReturn(Collections.singletonList(item2)); 598 list.onItemsRemoved(Collections.singletonList(item1)); 599 600 Assert.assertEquals(2, mModel.size()); 601 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), false); 602 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 2), item2); 603 } 604 605 /** 606 * Action List 607 * 1. Set(item1 @ 3:00 1/2/2018, [ DATE @ 0:00 1/2/2018, 608 * item2 @ 2:00 1/2/2018) 609 * item1 @ 3:00 1/2/2018, 610 * item2 @ 2:00 1/2/2018 ] 611 * 612 * 2. Remove(item2) [ DATE @ 0:00 1/2/2018, 613 * item1 @ 3:00 1/2/2018 ] 614 */ 615 @Test testRemoveLastItemInListSameDay()616 public void testRemoveLastItemInListSameDay() { 617 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 3), OfflineItemFilter.VIDEO); 618 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); 619 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 620 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 621 mModel.addObserver(mObserver); 622 623 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 624 list.onItemsRemoved(Collections.singletonList(item2)); 625 626 Assert.assertEquals(2, mModel.size()); 627 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), false); 628 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 3), item1); 629 } 630 631 /** 632 * Action List 633 * 1. Set(item1 @ 3:00 1/3/2018, [ DATE @ 0:00 1/3/2018, 634 * item2 @ 2:00 1/2/2018) 635 * item1 @ 3:00 1/3/2018, 636 * DATE @ 0:00 1/2/2018, 637 * item2 @ 2:00 1/2/2018 ] 638 * 639 * 2. Remove(item2) [ DATE @ 0:00 1/3/2018, 640 * item1 @ 3:00 1/3/2018 ] 641 */ 642 @Test testRemoveLastItemInListWithMultipleDays()643 public void testRemoveLastItemInListWithMultipleDays() { 644 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 3, 3), OfflineItemFilter.VIDEO); 645 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 2), OfflineItemFilter.VIDEO); 646 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 647 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 648 mModel.addObserver(mObserver); 649 650 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 651 list.onItemsRemoved(Collections.singletonList(item2)); 652 653 Assert.assertEquals(2, mModel.size()); 654 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 3, 0), false); 655 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 3, 3), item1); 656 } 657 658 /** 659 * Action List 660 * 1. Set() [ ] 661 * 662 * 2. Add(item1 @ 6:00 1/1/2018, [ DATE @ 0:00 1/2/2018, 663 * item2 @ 4:00 1/1/2018, 664 * item3 @ 10:00 1/2/2018, item4 @ 12:00 1/2/2018, 665 * item4 @ 12:00 1/2/2018) item3 @ 10:00 1/2/2018 666 * DATE @ 0:00 1/1/2018, 667 * 668 * item1 @ 6:00 1/1/2018, 669 * item2 @ 4:00 1/1/2018 ] 670 */ 671 @Test testAddMultipleItems()672 public void testAddMultipleItems() { 673 when(mSource.getItems()).thenReturn(Collections.emptySet()); 674 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 675 mModel.addObserver(mObserver); 676 677 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO); 678 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 679 OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.VIDEO); 680 OfflineItem item4 = buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.VIDEO); 681 682 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2, item3, item4)); 683 list.onItemsAdded(Arrays.asList(item1, item2, item3, item4)); 684 685 Assert.assertEquals(6, mModel.size()); 686 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), false); 687 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 12), item4); 688 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 2, 10), item3); 689 assertSectionHeader(mModel.get(3), buildCalendar(2018, 1, 1, 0), true); 690 assertOfflineItem(mModel.get(4), buildCalendar(2018, 1, 1, 6), item1); 691 assertOfflineItem(mModel.get(5), buildCalendar(2018, 1, 1, 4), item2); 692 } 693 694 /** 695 * Action List 696 * 1. Set() [ ] 697 * 698 * 2. Add(item3 @ 4:00 1/1/2018) [ DATE @ 0:00 1/1/2018, 699 * item3 @ 4:00 1/1/2018 ] 700 * 701 * 3. Add(item1 @ 4:00 1/1/2018) [ DATE @ 0:00 1/1/2018, 702 * item1 @ 4:00 1/1/2018, 703 * item3 @ 4:00 1/1/2018 ] 704 * 705 * 4. Add(item2 @ 4:00 1/1/2018) [ DATE @ 0:00 1/1/2018, 706 * item1 @ 4:00 1/1/2018, 707 * item2 @ 4:00 1/1/2018, 708 * item3 @ 4:00 1/1/2018 ] 709 */ 710 @Test testAddMultipleItemsSameTimestamp()711 public void testAddMultipleItemsSameTimestamp() { 712 when(mSource.getItems()).thenReturn(Collections.emptySet()); 713 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 714 mModel.addObserver(mObserver); 715 716 OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 717 when(mSource.getItems()).thenReturn(Collections.singletonList(item3)); 718 list.onItemsAdded(Collections.singletonList(item3)); 719 720 Assert.assertEquals(2, mModel.size()); 721 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item3); 722 723 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 724 when(mSource.getItems()).thenReturn(Arrays.asList(item3, item1)); 725 list.onItemsAdded(Collections.singletonList(item1)); 726 727 Assert.assertEquals(3, mModel.size()); 728 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item1); 729 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 4), item3); 730 731 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 732 when(mSource.getItems()).thenReturn(Arrays.asList(item3, item1, item2)); 733 list.onItemsAdded(Collections.singletonList(item2)); 734 735 Assert.assertEquals(4, mModel.size()); 736 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item1); 737 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 4), item2); 738 assertOfflineItem(mModel.get(3), buildCalendar(2018, 1, 1, 4), item3); 739 } 740 741 /** 742 * Action List 743 * 1. Set(item1 @ 6:00 IN_PROGRESS 1/1/2018) [ DATE @ 0:00 1/1/2018, 744 * 745 * item1 @ 3:00 1/1/2018 IN_PROGRESS] 746 * 747 * 2. Update(item1 @ 6:00 COMPLETE 1/1/2018) 748 * 749 * 3. Add(item2 @ 4:00 IN_PROGRESS 1/1/2018) [ 750 * DATE @ 0:00 1/1/2018, 751 * item1 @ 6:00 1/1/2018 COMPLETE, 752 * item2 @ 4:00 1/1/2018 IN_PROGRESS] 753 */ 754 @Test testFirstItemUpdatedAfterSecondItemAdded()755 public void testFirstItemUpdatedAfterSecondItemAdded() { 756 when(mSource.getItems()).thenReturn(Collections.emptySet()); 757 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 758 mModel.addObserver(mObserver); 759 760 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO); 761 762 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 763 list.onItemsAdded(Collections.singletonList(item1)); 764 765 Assert.assertEquals(2, mModel.size()); 766 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 767 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 6), item1); 768 769 // Complete the download. 770 OfflineItem update1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO); 771 update1.state = OfflineItemState.COMPLETE; 772 when(mSource.getItems()).thenReturn(Collections.singletonList(update1)); 773 list.onItemUpdated(item1, update1); 774 775 // Add a new download. 776 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 777 list.onItemsAdded(Collections.singletonList(item2)); 778 779 Assert.assertEquals(3, mModel.size()); 780 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 781 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 6), update1); 782 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 4), item2); 783 } 784 785 /** 786 * Action List 787 * 2. Set(item1 @ 6:00 1/1/2018, [ DATE @ 0:00 1/2/2018, 788 * item2 @ 4:00 1/1/2018, 789 * item3 @ 10:00 1/2/2018, item4 @ 12:00 1/2/2018, 790 * item4 @ 12:00 1/2/2018) item3 @ 10:00 1/2/2018 791 * DATE @ 0:00 1/1/2018, 792 * 793 * item1 @ 6:00 1/1/2018, 794 * item2 @ 4:00 1/1/2018 ] 795 * 796 * 2. Remove(item2, [ DATE @ 0:00 1/1/2018, 797 * item3, 798 * item4) item1 @ 6:00 1/1/2018 ] 799 */ 800 @Test testRemoveMultipleItems()801 public void testRemoveMultipleItems() { 802 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 6), OfflineItemFilter.VIDEO); 803 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 804 OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 2, 10), OfflineItemFilter.VIDEO); 805 OfflineItem item4 = buildItem("4", buildCalendar(2018, 1, 2, 12), OfflineItemFilter.VIDEO); 806 807 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2, item3, item4)); 808 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 809 mModel.addObserver(mObserver); 810 811 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 812 list.onItemsRemoved(Arrays.asList(item2, item3, item4)); 813 814 Assert.assertEquals(2, mModel.size()); 815 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 816 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 6), item1); 817 } 818 819 /** 820 * Action List 821 * 1. Set(item1 @ 4:00 1/1/2018) [ DATE @ 0:00 1/1/2018, 822 * 823 * item1 @ 4:00 1/1/2018 ] 824 * 825 * 2. Update (item1, 826 * newItem1 @ 4:00 1/1/2018) [ DATE @ 0:00 1/1/2018, 827 * 828 * newItem1 @ 4:00 1/1/2018 ] 829 */ 830 @Test testItemUpdatedSameTimestamp()831 public void testItemUpdatedSameTimestamp() { 832 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 833 834 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 835 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 836 mModel.addObserver(mObserver); 837 838 // Update an item with the same timestamp. 839 OfflineItem newItem1 = 840 buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 841 when(mSource.getItems()).thenReturn(Collections.singletonList(newItem1)); 842 list.onItemUpdated(item1, newItem1); 843 844 Assert.assertEquals(2, mModel.size()); 845 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 846 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), newItem1); 847 } 848 849 /** 850 * Action List 851 * 1. Set(item1 @ 5:00 1/1/2018, [ DATE @ 0:00 1/1/2018, 852 * item2 @ 4:00 1/1/2018) 853 * item1 @ 5:00 1/1/2018, 854 * item2 @ 4:00 1/1/2018 855 * 2. Update (item1, 856 * newItem1 @ 3:00 1/1/2018) [ DATE @ 0:00 1/1/2018, 857 * 858 * item2 @ 4:00 1/1/2018, 859 * newItem1 @ 3:00 1/1/2018 ] 860 */ 861 @Test testItemUpdatedSameDay()862 public void testItemUpdatedSameDay() { 863 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO); 864 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 865 866 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 867 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 868 mModel.addObserver(mObserver); 869 870 // Update an item with the same timestamp. 871 OfflineItem newItem1 = 872 buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.VIDEO); 873 when(mSource.getItems()).thenReturn(Arrays.asList(newItem1, item2)); 874 list.onItemUpdated(item1, newItem1); 875 876 Assert.assertEquals(3, mModel.size()); 877 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 878 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item2); 879 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 3), newItem1); 880 } 881 882 /** 883 * Action List 884 * 1. Set(item1 @ 5:00 1/1/2018, [ DATE @ 0:00 1/1/2018, 885 * item2 @ 4:00 1/1/2018) 886 * item1 @ 5:00 1/1/2018, 887 * item2 @ 4:00 1/1/2018 888 * 2. Update (item1, 889 * newItem1 @ 3:00 1/1/2018 Image) [ DATE @ 0:00 1/1/2018, 890 * 891 * item2 @ 4:00 1/1/2018, 892 * newItem1 @ 3:00 1/1/2018 ] 893 */ 894 @Test testItemUpdatedSameDayDifferentSection()895 public void testItemUpdatedSameDayDifferentSection() { 896 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.VIDEO); 897 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 898 899 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 900 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 901 mModel.addObserver(mObserver); 902 903 // Update an item with the same timestamp. 904 OfflineItem newItem1 = 905 buildItem("1", buildCalendar(2018, 1, 1, 3), OfflineItemFilter.IMAGE); 906 when(mSource.getItems()).thenReturn(Arrays.asList(newItem1, item2)); 907 list.onItemUpdated(item1, newItem1); 908 909 Assert.assertEquals(3, mModel.size()); 910 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 1, 0), false); 911 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 4), item2); 912 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 3), newItem1); 913 } 914 915 /** 916 * Action List 917 * 1. Set(item1 @ 4:00 1/1/2018) [ DATE @ 0:00 1/1/2018, 918 * item1 @ 4:00 1/1/2018 ] 919 * 920 * 2. Update (item1, 921 * newItem1 @ 6:00 1/2/2018) [ DATE @ 0:00 1/2/2018, 922 * newItem1 @ 6:00 1/2/2018 ] 923 */ 924 @Test testItemUpdatedDifferentDay()925 public void testItemUpdatedDifferentDay() { 926 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 1, 4), OfflineItemFilter.VIDEO); 927 928 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 929 DateOrderedListMutator list = createMutatorWithoutJustNowProvider(); 930 mModel.addObserver(mObserver); 931 932 // Update an item with the same timestamp. 933 OfflineItem newItem1 = 934 buildItem("1", buildCalendar(2018, 1, 2, 6), OfflineItemFilter.VIDEO); 935 when(mSource.getItems()).thenReturn(Collections.singletonList(newItem1)); 936 list.onItemUpdated(item1, newItem1); 937 938 Assert.assertEquals(2, mModel.size()); 939 assertSectionHeader(mModel.get(0), buildCalendar(2018, 1, 2, 0), false); 940 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 2, 6), newItem1); 941 } 942 943 /** 944 * Action List 945 * 1. Set(item1 @ 4:00 1/2/2018 Video, 946 * item2 @ 4:00 1/2/2018 Video, 947 * item3 @ 4:00 1/2/2018 Image, 948 * item4 @ 4:00 1/2/2018 Image, 949 * item5 @ 4:00 1/2/2018 Image) 950 * 2. Select Video 951 * [Date - 1/2/2018, item1, item2 ] 952 * 2. Select Image 953 * [Date - 1/2/2018, item3, item4, item5 ] 954 * 2. Select ALL 955 * [Date - 1/2/2018, item1, item2, 956 * item3, item4, item5 ] 957 */ 958 @Test testSwitchChips()959 public void testSwitchChips() { 960 OfflineItem item1 = buildItem("1", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO); 961 OfflineItem item2 = buildItem("2", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.VIDEO); 962 OfflineItem item3 = buildItem("3", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.IMAGE); 963 OfflineItem item4 = buildItem("4", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.IMAGE); 964 OfflineItem item5 = buildItem("5", buildCalendar(2018, 1, 2, 4), OfflineItemFilter.IMAGE); 965 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2, item3, item4, item5)); 966 TypeOfflineItemFilter typeOfflineItemFilter = new TypeOfflineItemFilter(mSource); 967 DateOrderedListMutator mutator = createMutatorWithSource(typeOfflineItemFilter); 968 Assert.assertEquals(6, mModel.size()); 969 970 typeOfflineItemFilter.onFilterSelected(Filters.FilterType.VIDEOS); 971 mutator.reload(); 972 Assert.assertEquals(3, mModel.size()); 973 974 typeOfflineItemFilter.onFilterSelected(Filters.FilterType.IMAGES); 975 mutator.reload(); 976 Assert.assertEquals(4, mModel.size()); 977 978 typeOfflineItemFilter.onFilterSelected(Filters.FilterType.NONE); 979 mutator.reload(); 980 Assert.assertEquals(6, mModel.size()); 981 } 982 983 /** 984 * Action List 985 * 1. Set(item1 @ 4:00 1/2/2018 Prefetch, 986 * item2 @ 5:00 1/1/2018 Prefetch ) 987 * [item2 @ 4:00 1/2/2018, 988 * item1 @ 5:00 1/1/2018 ] 989 */ 990 @Test testPrefetchTabBasic()991 public void testPrefetchTabBasic() { 992 OfflineItem item1 = buildPrefetchItem("1", buildCalendar(2018, 1, 2, 4)); 993 OfflineItem item2 = buildPrefetchItem("2", buildCalendar(2018, 1, 1, 5)); 994 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2)); 995 DateOrderedListMutator mutator = createMutatorWithoutJustNowProvider(); 996 mMutationController.onFilterTypeSelected(Filters.FilterType.PREFETCHED); 997 mutator.reload(); 998 999 Assert.assertEquals(2, mModel.size()); 1000 assertOfflineItem(mModel.get(0), buildCalendar(2018, 1, 2, 4), item1); 1001 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 1, 5), item2); 1002 } 1003 1004 /** 1005 * Action List 1006 * 1. Set(item1 @ 5:00 1/1/2018 Content indexing) 1007 * [item1 1/1/2018] 1008 */ 1009 @Test testContentIndexingStandaloneCards()1010 public void testContentIndexingStandaloneCards() { 1011 OfflineItem item1 = 1012 buildContentIndexedItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.PAGE); 1013 when(mSource.getItems()).thenReturn(Collections.singletonList(item1)); 1014 DateOrderedListMutator mutator = createMutatorWithoutJustNowProvider(); 1015 mMutationController.onFilterTypeSelected(Filters.FilterType.PREFETCHED); 1016 mutator.reload(); 1017 1018 Assert.assertEquals(1, mModel.size()); 1019 assertOfflineItem(mModel.get(0), buildCalendar(2018, 1, 1, 5), item1); 1020 } 1021 1022 /** 1023 * Action List 1024 * 1. Set(item1 @ 5:00 1/1/2018 Content indexing, 1025 * item2 @ 5:00 1/2/2018 Content indexing, 1026 * item3 @ 5:00 1/3/2018 Content indexing) 1027 * [item3 1/3/2018, 1028 * item2 1/2/2018, 1029 * item1 1/1/2018] 1030 */ 1031 @Test testContentIndexingDoesNotGroupItemsFromDifferentDays()1032 public void testContentIndexingDoesNotGroupItemsFromDifferentDays() { 1033 OfflineItem item1 = 1034 buildContentIndexedItem("1", buildCalendar(2018, 1, 1, 5), OfflineItemFilter.AUDIO); 1035 OfflineItem item2 = 1036 buildContentIndexedItem("2", buildCalendar(2018, 1, 3, 4), OfflineItemFilter.AUDIO); 1037 OfflineItem item3 = 1038 buildContentIndexedItem("3", buildCalendar(2018, 1, 3, 5), OfflineItemFilter.AUDIO); 1039 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2, item3)); 1040 DateOrderedListMutator mutator = createMutatorWithoutJustNowProvider(); 1041 mMutationController.onFilterTypeSelected(Filters.FilterType.PREFETCHED); 1042 mutator.reload(); 1043 1044 Assert.assertEquals(3, mModel.size()); 1045 assertOfflineItem(mModel.get(0), buildCalendar(2018, 1, 3, 5), item3); 1046 assertOfflineItem(mModel.get(1), buildCalendar(2018, 1, 3, 4), item2); 1047 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 1, 5), item1); 1048 } 1049 1050 /** 1051 * Action List 1052 * 1. Set(item1 @ 1:00 1/4/2018 Content indexing Video, 1053 * item2 @ 2:00 1/4/2018 Content indexing Video, 1054 * item3 @ 3:00 1/4/2018 Content indexing Video, 1055 * item4 @ 4:00 1/4/2018 Content indexing Video) 1056 * [---rounded top divider ----- 1057 * HEADER (with domain) 1058 * item4 1/4/2018, 1059 * -----divider------ 1060 * item3 1/4/2018, 1061 * -----divider------ 1062 * item2 1/4/2018 1063 * -----divider------ 1064 * FOOTER (More button) 1065 * -----rounded footer divider ---] 1066 */ 1067 @Test testContentIndexingGroupCardWithAndWithoutFooterAndWithIsolatedCards()1068 public void testContentIndexingGroupCardWithAndWithoutFooterAndWithIsolatedCards() { 1069 OfflineItem item1 = 1070 buildContentIndexedItem("1", buildCalendar(2018, 1, 4, 1), OfflineItemFilter.VIDEO); 1071 OfflineItem item2 = 1072 buildContentIndexedItem("2", buildCalendar(2018, 1, 4, 2), OfflineItemFilter.VIDEO); 1073 OfflineItem item3 = 1074 buildContentIndexedItem("3", buildCalendar(2018, 1, 4, 3), OfflineItemFilter.VIDEO); 1075 OfflineItem item4 = 1076 buildContentIndexedItem("4", buildCalendar(2018, 1, 4, 4), OfflineItemFilter.VIDEO); 1077 when(mSource.getItems()).thenReturn(Arrays.asList(item1, item2, item3, item4)); 1078 DateOrderedListMutator mutator = createMutatorWithoutJustNowProvider(); 1079 mMutationController.onFilterTypeSelected(Filters.FilterType.PREFETCHED); 1080 mutator.reload(); 1081 1082 Assert.assertEquals(10, mModel.size()); 1083 assertDivider(mModel.get(0), ListItem.CardDividerListItem.Position.TOP); 1084 assertCardHeader(mModel.get(1), buildCalendar(2018, 1, 4, 0), "http://example.com/xyz"); 1085 assertOfflineItem(mModel.get(2), buildCalendar(2018, 1, 4, 4), item4); 1086 assertDivider(mModel.get(3), ListItem.CardDividerListItem.Position.MIDDLE); 1087 assertOfflineItem(mModel.get(4), buildCalendar(2018, 1, 4, 3), item3); 1088 assertDivider(mModel.get(5), ListItem.CardDividerListItem.Position.MIDDLE); 1089 assertOfflineItem(mModel.get(6), buildCalendar(2018, 1, 4, 2), item2); 1090 assertDivider(mModel.get(7), ListItem.CardDividerListItem.Position.MIDDLE); 1091 assertCardFooter(mModel.get(8)); 1092 assertDivider(mModel.get(9), ListItem.CardDividerListItem.Position.BOTTOM); 1093 1094 // Remove one item. Now there should be only 3 items. The footer should be gone and get 1095 // replaced by bottom curve. 1096 when(mSource.getItems()).thenReturn(Arrays.asList(item2, item3, item4)); 1097 mutator.onItemsRemoved(Collections.singletonList(item1)); 1098 1099 Assert.assertEquals(8, mModel.size()); 1100 assertOfflineItem(mModel.get(6), buildCalendar(2018, 1, 4, 2), item2); 1101 assertDivider(mModel.get(7), ListItem.CardDividerListItem.Position.BOTTOM); 1102 1103 // Remove one more item. Now there should be only 2 items. Now they shouldn't be grouped at 1104 // all. 1105 when(mSource.getItems()).thenReturn(Arrays.asList(item3, item4)); 1106 mutator.onItemsRemoved(Collections.singletonList(item2)); 1107 Assert.assertEquals(2, mModel.size()); 1108 } 1109 buildCalendar(int year, int month, int dayOfMonth, int hourOfDay)1110 private static Calendar buildCalendar(int year, int month, int dayOfMonth, int hourOfDay) { 1111 Calendar calendar = CalendarFactory.get(); 1112 calendar.set(year, month, dayOfMonth, hourOfDay, 0); 1113 return calendar; 1114 } 1115 buildItem( String id, Calendar calendar, @OfflineItemFilter int filter)1116 private static OfflineItem buildItem( 1117 String id, Calendar calendar, @OfflineItemFilter int filter) { 1118 OfflineItem item = new OfflineItem(); 1119 item.id.namespace = "test"; 1120 item.id.id = id; 1121 item.creationTimeMs = calendar.getTimeInMillis(); 1122 item.filter = filter; 1123 return item; 1124 } 1125 buildPrefetchItem(String id, Calendar calendar)1126 private static OfflineItem buildPrefetchItem(String id, Calendar calendar) { 1127 OfflineItem item = new OfflineItem(); 1128 item.id.namespace = "test"; 1129 item.id.id = id; 1130 item.creationTimeMs = calendar.getTimeInMillis(); 1131 item.filter = OfflineItemFilter.PAGE; 1132 item.isSuggested = true; 1133 return item; 1134 } 1135 buildContentIndexedItem( String id, Calendar calendar, @OfflineItemFilter int filter)1136 private static OfflineItem buildContentIndexedItem( 1137 String id, Calendar calendar, @OfflineItemFilter int filter) { 1138 OfflineItem item = new OfflineItem(); 1139 item.id.namespace = LegacyHelpers.LEGACY_CONTENT_INDEX_NAMESPACE; 1140 item.id.id = id; 1141 item.isSuggested = true; 1142 item.creationTimeMs = calendar.getTimeInMillis(); 1143 item.filter = filter; 1144 item.pageUrl = "http://example.com/xyz"; 1145 return item; 1146 } 1147 createConfig()1148 private DownloadManagerUiConfig createConfig() { 1149 return new DownloadManagerUiConfig.Builder() 1150 .setUseNewDownloadPath(true) 1151 .setSupportsGrouping(true) 1152 .build(); 1153 } 1154 buildJustNowProvider(Date overrideNow)1155 private JustNowProvider buildJustNowProvider(Date overrideNow) { 1156 JustNowProvider justNowProvider = new JustNowProvider(createConfig()) { 1157 @Override 1158 protected Date now() { 1159 return overrideNow; 1160 } 1161 }; 1162 return justNowProvider; 1163 } 1164 createMutatorWithSource(OfflineItemFilterSource source)1165 private DateOrderedListMutator createMutatorWithSource(OfflineItemFilterSource source) { 1166 DownloadManagerUiConfig config = createConfig(); 1167 JustNowProvider justNowProvider = new JustNowProvider(config); 1168 DateOrderedListMutator mutator = 1169 new DateOrderedListMutator(source, mModel, justNowProvider); 1170 mMutationController = new ListMutationController(config, justNowProvider, mutator, mModel); 1171 return mutator; 1172 } 1173 createMutatorWithoutJustNowProvider()1174 private DateOrderedListMutator createMutatorWithoutJustNowProvider() { 1175 DownloadManagerUiConfig config = createConfig(); 1176 JustNowProvider justNowProvider = new JustNowProvider(config) { 1177 @Override 1178 public boolean isJustNowItem(OfflineItem item) { 1179 return false; 1180 } 1181 }; 1182 DateOrderedListMutator mutator = 1183 new DateOrderedListMutator(mSource, mModel, justNowProvider); 1184 mMutationController = new ListMutationController(config, justNowProvider, mutator, mModel); 1185 return mutator; 1186 } 1187 createMutatorWithJustNowProvider()1188 private DateOrderedListMutator createMutatorWithJustNowProvider() { 1189 JustNowProvider justNowProvider = new JustNowProvider(createConfig()); 1190 return createMutatorWithJustNowProvider(justNowProvider); 1191 } 1192 createMutatorWithJustNowProvider( JustNowProvider justNowProvider)1193 private DateOrderedListMutator createMutatorWithJustNowProvider( 1194 JustNowProvider justNowProvider) { 1195 DateOrderedListMutator mutator = 1196 new DateOrderedListMutator(mSource, mModel, justNowProvider); 1197 mMutationController = 1198 new ListMutationController(createConfig(), justNowProvider, mutator, mModel); 1199 return mutator; 1200 } 1201 assertDatesAreEqual(Date date, Calendar calendar)1202 private static void assertDatesAreEqual(Date date, Calendar calendar) { 1203 Calendar calendar2 = CalendarFactory.get(); 1204 calendar2.setTime(date); 1205 Assert.assertEquals(calendar.getTimeInMillis(), calendar2.getTimeInMillis()); 1206 } 1207 assertOfflineItem( ListItem item, Calendar calendar, OfflineItem offlineItem)1208 private static void assertOfflineItem( 1209 ListItem item, Calendar calendar, OfflineItem offlineItem) { 1210 Assert.assertTrue(item instanceof OfflineItemListItem); 1211 assertDatesAreEqual(((OfflineItemListItem) item).date, calendar); 1212 Assert.assertEquals(OfflineItemListItem.generateStableId(offlineItem), item.stableId); 1213 Assert.assertEquals(offlineItem, ((OfflineItemListItem) item).item); 1214 } 1215 assertSectionHeader(ListItem item, Calendar calendar, boolean showDivider)1216 private static void assertSectionHeader(ListItem item, Calendar calendar, boolean showDivider) { 1217 Assert.assertTrue(item instanceof SectionHeaderListItem); 1218 SectionHeaderListItem sectionHeader = (SectionHeaderListItem) item; 1219 assertDatesAreEqual(sectionHeader.date, calendar); 1220 Assert.assertEquals(SectionHeaderListItem.generateStableId( 1221 SectionHeaderType.DATE, calendar.getTimeInMillis()), 1222 item.stableId); 1223 Assert.assertEquals(SectionHeaderListItem.generateStableId( 1224 SectionHeaderType.JUST_NOW, calendar.getTimeInMillis()), 1225 StableIds.JUST_NOW_SECTION); 1226 Assert.assertEquals(SectionHeaderListItem.generateStableId( 1227 SectionHeaderType.SCHEDULED_LATER, calendar.getTimeInMillis()), 1228 StableIds.SCHEDULE_LATER_SECTION); 1229 Assert.assertEquals(sectionHeader.showTopDivider, showDivider); 1230 } 1231 assertJustNowSection(ListItem item)1232 private static void assertJustNowSection(ListItem item) { 1233 Assert.assertTrue(item instanceof SectionHeaderListItem); 1234 SectionHeaderListItem sectionHeader = (SectionHeaderListItem) item; 1235 Assert.assertEquals(SectionHeaderType.JUST_NOW, sectionHeader.type); 1236 Assert.assertEquals(StableIds.JUST_NOW_SECTION, item.stableId); 1237 } 1238 assertScheduledLaterHeader(ListItem item)1239 private static void assertScheduledLaterHeader(ListItem item) { 1240 Assert.assertTrue(item instanceof SectionHeaderListItem); 1241 SectionHeaderListItem sectionHeader = (SectionHeaderListItem) item; 1242 Assert.assertEquals("Schedule for later section is at the top of the list", false, 1243 sectionHeader.showTopDivider); 1244 Assert.assertEquals(SectionHeaderType.SCHEDULED_LATER, sectionHeader.type); 1245 Assert.assertEquals(StableIds.SCHEDULE_LATER_SECTION, item.stableId); 1246 } 1247 assertCardHeader(ListItem item, Calendar calendar, String domain)1248 private static void assertCardHeader(ListItem item, Calendar calendar, String domain) { 1249 Assert.assertTrue(item instanceof ListItem.CardHeaderListItem); 1250 ListItem.CardHeaderListItem headerItem = (ListItem.CardHeaderListItem) item; 1251 assertDatesAreEqual(headerItem.dateAndDomain.first, calendar); 1252 Assert.assertEquals(headerItem.dateAndDomain.second, domain); 1253 } 1254 assertCardFooter(ListItem item)1255 private static void assertCardFooter(ListItem item) { 1256 Assert.assertTrue(item instanceof ListItem.CardFooterListItem); 1257 } 1258 assertDivider( ListItem item, ListItem.CardDividerListItem.Position position)1259 private static void assertDivider( 1260 ListItem item, ListItem.CardDividerListItem.Position position) { 1261 Assert.assertTrue(item instanceof ListItem.CardDividerListItem); 1262 Assert.assertEquals(((ListItem.CardDividerListItem) item).position, position); 1263 } 1264 } 1265