1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 package org.mozilla.gecko.dlc.catalog; 7 8 import android.support.v4.util.ArrayMap; 9 import android.support.v4.util.AtomicFile; 10 11 import org.junit.Assert; 12 import org.junit.Assume; 13 import org.junit.Test; 14 import org.junit.runner.RunWith; 15 import org.mozilla.gecko.AppConstants; 16 import org.mozilla.gecko.background.testhelpers.TestRunner; 17 18 import java.io.FileNotFoundException; 19 import java.io.FileOutputStream; 20 21 import static org.mockito.Mockito.doReturn; 22 import static org.mockito.Mockito.doThrow; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.spy; 26 import static org.mockito.Mockito.verify; 27 28 @RunWith(TestRunner.class) 29 public class TestDownloadContentCatalog { 30 /** 31 * Scenario: Create a new, fresh catalog. 32 * 33 * Verify that: 34 * * Catalog has not changed 35 * * Unchanged catalog will not be saved to disk 36 */ 37 @Test testUntouchedCatalogHasNotChangedAndWillNotBePersisted()38 public void testUntouchedCatalogHasNotChangedAndWillNotBePersisted() throws Exception { 39 AtomicFile file = mock(AtomicFile.class); 40 doReturn("{content:[]}".getBytes("UTF-8")).when(file).readFully(); 41 42 DownloadContentCatalog catalog = spy(new DownloadContentCatalog(file)); 43 catalog.loadFromDisk(); 44 45 Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged()); 46 47 catalog.writeToDisk(); 48 49 Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged()); 50 51 verify(file, never()).startWrite(); 52 } 53 54 /** 55 * Scenario: Create a new, fresh catalog. 56 * 57 * Verify that: 58 * * Catalog is created empty 59 */ 60 @Test testCatalogIsBootstrappedIfFileDoesNotExist()61 public void testCatalogIsBootstrappedIfFileDoesNotExist() throws Exception { 62 // The catalog is only bootstrapped if fonts are excluded from the build. If this is a build 63 // with fonts included then ignore this test. 64 Assume.assumeTrue("Fonts are excluded from build", AppConstants.MOZ_ANDROID_EXCLUDE_FONTS); 65 66 AtomicFile file = mock(AtomicFile.class); 67 doThrow(FileNotFoundException.class).when(file).readFully(); 68 69 DownloadContentCatalog catalog = spy(new DownloadContentCatalog(file)); 70 catalog.loadFromDisk(); 71 72 Assert.assertEquals("Catalog is empty", 0, catalog.getContentToStudy().size()); 73 } 74 75 /** 76 * Scenario: Schedule downloading an item from the catalog. 77 * 78 * Verify that: 79 * * Catalog has changed 80 */ 81 @Test testCatalogHasChangedWhenDownloadIsScheduled()82 public void testCatalogHasChangedWhenDownloadIsScheduled() throws Exception { 83 DownloadContentCatalog catalog = spy(new DownloadContentCatalog(mock(AtomicFile.class))); 84 DownloadContent content = new DownloadContentBuilder().build(); 85 catalog.onCatalogLoaded(createMapOfContent(content)); 86 87 Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged()); 88 89 catalog.scheduleDownload(content); 90 91 Assert.assertTrue("Catalog has changed", catalog.hasCatalogChanged()); 92 } 93 94 /** 95 * Scenario: Mark an item in the catalog as downloaded. 96 * 97 * Verify that: 98 * * Catalog has changed 99 */ 100 @Test testCatalogHasChangedWhenContentIsDownloaded()101 public void testCatalogHasChangedWhenContentIsDownloaded() throws Exception { 102 DownloadContentCatalog catalog = spy(new DownloadContentCatalog(mock(AtomicFile.class))); 103 DownloadContent content = new DownloadContentBuilder().build(); 104 catalog.onCatalogLoaded(createMapOfContent(content)); 105 106 Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged()); 107 108 catalog.markAsDownloaded(content); 109 110 Assert.assertTrue("Catalog has changed", catalog.hasCatalogChanged()); 111 } 112 113 /** 114 * Scenario: Mark an item in the catalog as permanently failed. 115 * 116 * Verify that: 117 * * Catalog has changed 118 */ 119 @Test testCatalogHasChangedIfDownloadHasFailedPermanently()120 public void testCatalogHasChangedIfDownloadHasFailedPermanently() throws Exception { 121 DownloadContentCatalog catalog = spy(new DownloadContentCatalog(mock(AtomicFile.class))); 122 DownloadContent content = new DownloadContentBuilder().build(); 123 catalog.onCatalogLoaded(createMapOfContent(content)); 124 125 Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged()); 126 127 catalog.markAsPermanentlyFailed(content); 128 129 Assert.assertTrue("Catalog has changed", catalog.hasCatalogChanged()); 130 } 131 132 /** 133 * Scenario: A changed catalog is written to disk. 134 * 135 * Verify that: 136 * * Before write: Catalog has changed 137 * * After write: Catalog has not changed. 138 */ 139 @Test testCatalogHasNotChangedAfterWritingToDisk()140 public void testCatalogHasNotChangedAfterWritingToDisk() throws Exception { 141 AtomicFile file = mock(AtomicFile.class); 142 doReturn(mock(FileOutputStream.class)).when(file).startWrite(); 143 144 DownloadContentCatalog catalog = spy(new DownloadContentCatalog(file)); 145 DownloadContent content = new DownloadContentBuilder().build(); 146 catalog.onCatalogLoaded(createMapOfContent(content)); 147 148 catalog.scheduleDownload(content); 149 150 Assert.assertTrue("Catalog has changed", catalog.hasCatalogChanged()); 151 152 catalog.writeToDisk(); 153 154 Assert.assertFalse("Catalog has not changed", catalog.hasCatalogChanged()); 155 } 156 157 /** 158 * Scenario: A catalog with multiple items in different states. 159 * 160 * Verify that: 161 * * getContentWithoutState(), getDownloadedContent() and getScheduledDownloads() returns 162 * the correct items depenending on their state. 163 */ 164 @Test testContentClassification()165 public void testContentClassification() { 166 DownloadContentCatalog catalog = spy(new DownloadContentCatalog(mock(AtomicFile.class))); 167 168 DownloadContent content1 = new DownloadContentBuilder().setId("A").setState(DownloadContent.STATE_NONE).build(); 169 DownloadContent content2 = new DownloadContentBuilder().setId("B").setState(DownloadContent.STATE_NONE).build(); 170 DownloadContent content3 = new DownloadContentBuilder().setId("C").setState(DownloadContent.STATE_SCHEDULED).build(); 171 DownloadContent content4 = new DownloadContentBuilder().setId("D").setState(DownloadContent.STATE_SCHEDULED).build(); 172 DownloadContent content5 = new DownloadContentBuilder().setId("E").setState(DownloadContent.STATE_SCHEDULED).build(); 173 DownloadContent content6 = new DownloadContentBuilder().setId("F").setState(DownloadContent.STATE_DOWNLOADED).build(); 174 DownloadContent content7 = new DownloadContentBuilder().setId("G").setState(DownloadContent.STATE_FAILED).build(); 175 DownloadContent content8 = new DownloadContentBuilder().setId("H").setState(DownloadContent.STATE_UPDATED).build(); 176 DownloadContent content9 = new DownloadContentBuilder().setId("I").setState(DownloadContent.STATE_DELETED).build(); 177 DownloadContent content10 = new DownloadContentBuilder().setId("J").setState(DownloadContent.STATE_DELETED).build(); 178 179 catalog.onCatalogLoaded(createMapOfContent(content1, content2, content3, content4, content5, content6, 180 content7, content8, content9, content10)); 181 182 Assert.assertTrue(catalog.hasScheduledDownloads()); 183 184 Assert.assertEquals(3, catalog.getContentToStudy().size()); 185 Assert.assertEquals(1, catalog.getDownloadedContent().size()); 186 Assert.assertEquals(3, catalog.getScheduledDownloads().size()); 187 Assert.assertEquals(2, catalog.getContentToDelete().size()); 188 189 Assert.assertTrue(catalog.getContentToStudy().contains(content1)); 190 Assert.assertTrue(catalog.getContentToStudy().contains(content2)); 191 Assert.assertTrue(catalog.getContentToStudy().contains(content8)); 192 193 Assert.assertTrue(catalog.getDownloadedContent().contains(content6)); 194 195 Assert.assertTrue(catalog.getScheduledDownloads().contains(content3)); 196 Assert.assertTrue(catalog.getScheduledDownloads().contains(content4)); 197 Assert.assertTrue(catalog.getScheduledDownloads().contains(content5)); 198 199 Assert.assertTrue(catalog.getContentToDelete().contains(content9)); 200 Assert.assertTrue(catalog.getContentToDelete().contains(content10)); 201 } 202 203 /** 204 * Scenario: Calling rememberFailure() on a catalog with varying values 205 */ 206 @Test testRememberingFailures()207 public void testRememberingFailures() { 208 DownloadContentCatalog catalog = new DownloadContentCatalog(mock(AtomicFile.class)); 209 Assert.assertFalse(catalog.hasCatalogChanged()); 210 211 DownloadContent content = new DownloadContentBuilder().build(); 212 Assert.assertEquals(0, content.getFailures()); 213 214 catalog.rememberFailure(content, 42); 215 Assert.assertEquals(1, content.getFailures()); 216 Assert.assertTrue(catalog.hasCatalogChanged()); 217 218 catalog.rememberFailure(content, 42); 219 Assert.assertEquals(2, content.getFailures()); 220 221 // Failure counter is reset if different failure has been reported 222 catalog.rememberFailure(content, 23); 223 Assert.assertEquals(1, content.getFailures()); 224 225 // Failure counter is reset after successful download 226 catalog.markAsDownloaded(content); 227 Assert.assertEquals(0, content.getFailures()); 228 } 229 230 /** 231 * Scenario: Content has failed multiple times with the same failure type. 232 * 233 * Verify that: 234 * * Content is marked as permanently failed 235 */ 236 @Test testContentWillBeMarkedAsPermanentlyFailedAfterMultipleFailures()237 public void testContentWillBeMarkedAsPermanentlyFailedAfterMultipleFailures() { 238 DownloadContentCatalog catalog = new DownloadContentCatalog(mock(AtomicFile.class)); 239 240 DownloadContent content = new DownloadContentBuilder().build(); 241 Assert.assertEquals(DownloadContent.STATE_NONE, content.getState()); 242 243 for (int i = 0; i < 10; i++) { 244 catalog.rememberFailure(content, 42); 245 246 Assert.assertEquals(i + 1, content.getFailures()); 247 Assert.assertEquals(DownloadContent.STATE_NONE, content.getState()); 248 } 249 250 catalog.rememberFailure(content, 42); 251 Assert.assertEquals(10, content.getFailures()); 252 Assert.assertEquals(DownloadContent.STATE_FAILED, content.getState()); 253 } 254 createMapOfContent(DownloadContent... content)255 private ArrayMap<String, DownloadContent> createMapOfContent(DownloadContent... content) { 256 ArrayMap<String, DownloadContent> map = new ArrayMap<>(); 257 for (DownloadContent currentContent : content) { 258 map.put(currentContent.getId(), currentContent); 259 } 260 return map; 261 } 262 } 263