Bug 1363520 - Run CleanupAction from DownloadContentService. r?grisha draft
authorSebastian Kaspari <s.kaspari@gmail.com>
Wed, 10 May 2017 09:59:31 -0700
changeset 642602 c0b4d988d92963e8cc6e64cc3b3ad80c3869f2ec
parent 641632 47248637eafa9a38dade8dc3aa6c4736177c8d8d
child 725052 80b327ff63b5e311cf631aa00c79a9b307a051ca
push id72816
push users.kaspari@gmail.com
push dateTue, 08 Aug 2017 15:17:02 +0000
reviewersgrisha
bugs1363520
milestone57.0a1
Bug 1363520 - Run CleanupAction from DownloadContentService. r?grisha MozReview-Commit-ID: FvnIYStD25u
mobile/android/base/java/org/mozilla/gecko/dlc/CleanupAction.java
mobile/android/base/java/org/mozilla/gecko/dlc/DownloadContentService.java
mobile/android/base/moz.build
mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestCleanupAction.java
--- a/mobile/android/base/java/org/mozilla/gecko/dlc/CleanupAction.java
+++ b/mobile/android/base/java/org/mozilla/gecko/dlc/CleanupAction.java
@@ -1,16 +1,17 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.dlc;
 
 import android.content.Context;
+import android.support.annotation.VisibleForTesting;
 
 import org.mozilla.gecko.dlc.catalog.DownloadContent;
 import org.mozilla.gecko.dlc.catalog.DownloadContentCatalog;
 
 import java.io.File;
 
 /**
  * CleanupAction: Remove content that is no longer needed.
@@ -19,31 +20,35 @@ public class CleanupAction extends BaseA
     @Override
     public void perform(Context context, DownloadContentCatalog catalog) {
         for (DownloadContent content : catalog.getContentToDelete()) {
             if (!content.isAssetArchive()) {
                 continue; // We do not know how to clean up this content. But this means we didn't
                           // download it anyways.
             }
 
-            try {
-                File file = getDestinationFile(context, content);
+            cleanupContent(context, catalog, content);
+        }
+    }
 
-                if (!file.exists()) {
-                    // File does not exist. As good as deleting.
-                    catalog.remove(content);
-                    return;
-                }
+    @VisibleForTesting void cleanupContent(Context context, DownloadContentCatalog catalog, DownloadContent content) {
+        try {
+            final File file = getDestinationFile(context, content);
 
-                if (file.delete()) {
-                    // File has been deleted. Now remove it from the catalog.
-                    catalog.remove(content);
-                }
-            } catch (UnrecoverableDownloadContentException e) {
-                // We can't recover. Pretend the content is removed. It probably never existed in
-                // the first place.
+            if (!file.exists()) {
+                // File does not exist. As good as deleting.
                 catalog.remove(content);
-            } catch (RecoverableDownloadContentException e) {
-                // Try again next time.
+                return;
+            }
+
+            if (file.delete()) {
+                // File has been deleted. Now remove it from the catalog.
+                catalog.remove(content);
             }
+        } catch (UnrecoverableDownloadContentException e) {
+            // We can't recover. Pretend the content is removed. It probably never existed in
+            // the first place.
+            catalog.remove(content);
+        } catch (RecoverableDownloadContentException e) {
+            // Try again next time.
         }
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/dlc/DownloadContentService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/dlc/DownloadContentService.java
@@ -128,16 +128,20 @@ public class DownloadContentService exte
             case ACTION_VERIFY_CONTENT:
                 action = new VerifyAction();
                 break;
 
             case ACTION_SYNCHRONIZE_CATALOG:
                 action = new SyncAction();
                 break;
 
+            case ACTION_CLEANUP_FILES:
+                action = new CleanupAction();
+                break;
+
             default:
                 Log.e(LOGTAG, "Unknown action: " + intent.getAction());
                 return;
         }
 
         action.perform(this, catalog);
         catalog.persistChanges();
     }
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -600,16 +600,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'distribution/PartnerBookmarksProviderProxy.java',
     'distribution/PartnerBrowserCustomizationsClient.java',
     'distribution/ReferrerDescriptor.java',
     'distribution/ReferrerReceiver.java',
     'dlc/BaseAction.java',
     'dlc/catalog/DownloadContent.java',
     'dlc/catalog/DownloadContentBuilder.java',
     'dlc/catalog/DownloadContentCatalog.java',
+    'dlc/CleanupAction.java',
     'dlc/DownloadAction.java',
     'dlc/DownloadContentService.java',
     'dlc/DownloadContentTelemetry.java',
     'dlc/StudyAction.java',
     'dlc/SyncAction.java',
     'dlc/VerifyAction.java',
     'DoorHangerPopup.java',
     'DownloadsIntegration.java',
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestCleanupAction.java
@@ -0,0 +1,134 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.dlc;
+
+import android.content.Context;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mozilla.gecko.dlc.catalog.DownloadContent;
+import org.mozilla.gecko.dlc.catalog.DownloadContentBuilder;
+import org.mozilla.gecko.dlc.catalog.DownloadContentCatalog;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.io.File;
+
+import dalvik.annotation.TestTarget;
+import edu.emory.mathcs.backport.java.util.Collections;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+@RunWith(RobolectricTestRunner.class)
+public class TestCleanupAction {
+    @Test
+    public void testEmptyCatalog() {
+        final DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
+        doReturn(Collections.emptyList()).when(catalog).getContentToDelete();
+
+        final CleanupAction action = spy(new CleanupAction());
+        action.perform(RuntimeEnvironment.application, catalog);
+
+        verify(catalog).getContentToDelete();
+        verify(action, never()).cleanupContent(any(Context.class), any(DownloadContentCatalog.class), any(DownloadContent.class));
+    }
+
+    @Test
+    public void testWithUnknownType() {
+        final DownloadContent content = new DownloadContentBuilder()
+                .setType("RandomType")
+                .build();
+
+        final DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
+        doReturn(Collections.singletonList(content)).when(catalog).getContentToDelete();
+
+        final CleanupAction action = spy(new CleanupAction());
+        action.perform(RuntimeEnvironment.application, catalog);
+
+        verify(catalog).getContentToDelete();
+        verify(action, never()).cleanupContent(any(Context.class), any(DownloadContentCatalog.class), any(DownloadContent.class));
+    }
+
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    @Test
+    public void testWithContentThatDoesNotExistAnymore() throws Exception {
+        final DownloadContent content = new DownloadContentBuilder()
+                .setType(DownloadContent.TYPE_ASSET_ARCHIVE)
+                .build();
+
+        final DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
+        doReturn(Collections.singletonList(content)).when(catalog).getContentToDelete();
+
+        final File file = mock(File.class);
+        doReturn(false).when(file).exists();
+
+        final CleanupAction action = spy(new CleanupAction());
+        doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
+
+        action.perform(RuntimeEnvironment.application, catalog);
+
+        verify(catalog).getContentToDelete();
+        verify(action).cleanupContent(RuntimeEnvironment.application, catalog, content);
+        verify(file).exists();
+        verify(file, never()).delete();
+        verify(catalog).remove(content);
+    }
+
+    @Test
+    public void testWithDeletableContent() throws Exception {
+        final DownloadContent content = new DownloadContentBuilder()
+                .setType(DownloadContent.TYPE_ASSET_ARCHIVE)
+                .build();
+
+        final DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
+        doReturn(Collections.singletonList(content)).when(catalog).getContentToDelete();
+
+        final File file = mock(File.class);
+        doReturn(true).when(file).exists();
+        doReturn(true).when(file).delete();
+
+        final CleanupAction action = spy(new CleanupAction());
+        doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
+
+        action.perform(RuntimeEnvironment.application, catalog);
+
+        verify(catalog).getContentToDelete();
+        verify(action).cleanupContent(RuntimeEnvironment.application, catalog, content);
+        verify(file).exists();
+        verify(file).delete();
+        verify(catalog).remove(content);
+    }
+
+    @Test
+    public void testWithFileThatCannotBeDeleted() throws Exception {
+        final DownloadContent content = new DownloadContentBuilder()
+                .setType(DownloadContent.TYPE_ASSET_ARCHIVE)
+                .build();
+
+        final DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
+        doReturn(Collections.singletonList(content)).when(catalog).getContentToDelete();
+
+        final File file = mock(File.class);
+        doReturn(true).when(file).exists();
+        doReturn(false).when(file).delete();
+
+        final CleanupAction action = spy(new CleanupAction());
+        doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
+
+        action.perform(RuntimeEnvironment.application, catalog);
+
+        verify(catalog).getContentToDelete();
+        verify(action).cleanupContent(RuntimeEnvironment.application, catalog, content);
+        verify(file).exists();
+        verify(file).delete();
+        verify(catalog, never()).remove(content);
+    }
+}