diff --git a/app/src/androidTest/java/org/fdroid/fdroid/work/CleanCacheWorkerTest.java b/app/src/androidTest/java/org/fdroid/fdroid/work/CleanCacheWorkerTest.java index b57cd1034..e084fe6db 100644 --- a/app/src/androidTest/java/org/fdroid/fdroid/work/CleanCacheWorkerTest.java +++ b/app/src/androidTest/java/org/fdroid/fdroid/work/CleanCacheWorkerTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertTrue; /** * This test cannot run on Robolectric unfortunately since it does not support + * getting the timestamps from the files completely. *

* This is marked with {@link LargeTest} because it always fails on the emulator * tests on GitLab CI. That excludes it from the test run there. @@ -86,4 +87,24 @@ public class CleanCacheWorkerTest { CleanCacheWorker.clearOldFiles(nonexistent, 1); CleanCacheWorker.clearOldFiles(null, 1); } + + /* + // TODO enable this once getImageCacheDir() can be mocked or provide a writable dir in the test + @Test + public void testDeleteOldIcons() throws IOException { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + File imageCacheDir = Utils.getImageCacheDir(context); + imageCacheDir.mkdirs(); + assertTrue(imageCacheDir.isDirectory()); + File oldIcon = new File(imageCacheDir, "old.png"); + assertTrue(oldIcon.createNewFile()); + Assume.assumeTrue("test environment must be able to set LastModified time", + oldIcon.setLastModified(System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 370))); + File currentIcon = new File(imageCacheDir, "current.png"); + assertTrue(currentIcon.createNewFile()); + CleanCacheWorker.deleteOldIcons(context); + assertTrue(currentIcon.exists()); + assertFalse(oldIcon.exists()); + } + */ } diff --git a/app/src/main/java/org/fdroid/fdroid/work/CleanCacheWorker.java b/app/src/main/java/org/fdroid/fdroid/work/CleanCacheWorker.java index 6c84bbcf1..584b01afa 100644 --- a/app/src/main/java/org/fdroid/fdroid/work/CleanCacheWorker.java +++ b/app/src/main/java/org/fdroid/fdroid/work/CleanCacheWorker.java @@ -90,10 +90,11 @@ public class CleanCacheWorker extends Worker { public Result doWork() { Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); try { - deleteExpiredApksFromCache(); - deleteStrayIndexFiles(); - deleteOldInstallerFiles(); - deleteOldIcons(); + final Context context = getApplicationContext(); + deleteExpiredApksFromCache(context); + deleteStrayIndexFiles(context); + deleteOldInstallerFiles(context); + deleteOldIcons(context); return Result.success(); } catch (Exception e) { return Result.failure(); @@ -105,8 +106,8 @@ public class CleanCacheWorker extends Worker { * specified by the user in the "Keep Cache Time" preference. This removes * any APK in the cache that is older than that preference specifies. */ - private void deleteExpiredApksFromCache() { - File cacheDir = ApkCache.getApkCacheDir(getApplicationContext()); + static void deleteExpiredApksFromCache(@NonNull Context context) { + File cacheDir = ApkCache.getApkCacheDir(context); clearOldFiles(cacheDir, Preferences.get().getKeepCacheTime()); } @@ -117,8 +118,8 @@ public class CleanCacheWorker extends Worker { * also avoids deleting the nearby swap repo files since that might be * actively in use. */ - private void deleteOldInstallerFiles() { - File filesDir = getApplicationContext().getFilesDir(); + static void deleteOldInstallerFiles(@NonNull Context context) { + File filesDir = context.getFilesDir(); if (filesDir == null) { Utils.debugLog(TAG, "The files directory doesn't exist."); return; @@ -150,8 +151,8 @@ public class CleanCacheWorker extends Worker { * This also deletes temp files that are created by * {@link org.fdroid.fdroid.net.DownloaderFactory#create(Context, String)}, e.g. "dl-*" */ - private void deleteStrayIndexFiles() { - File cacheDir = getApplicationContext().getCacheDir(); + static void deleteStrayIndexFiles(@NonNull Context context) { + File cacheDir = context.getCacheDir(); if (cacheDir == null) { Utils.debugLog(TAG, "The cache directory doesn't exist."); return; @@ -176,8 +177,8 @@ public class CleanCacheWorker extends Worker { /** * Delete cached icons that have not been accessed in over a year. */ - private void deleteOldIcons() { - clearOldFiles(Utils.getImageCacheDir(getApplicationContext()), TimeUnit.DAYS.toMillis(365)); + static void deleteOldIcons(@NonNull Context context) { + clearOldFiles(Utils.getImageCacheDir(context), TimeUnit.DAYS.toMillis(365)); } /** diff --git a/app/src/test/java/org/fdroid/fdroid/TestUtils.java b/app/src/test/java/org/fdroid/fdroid/TestUtils.java index a24c8d7b0..478c8ca1f 100644 --- a/app/src/test/java/org/fdroid/fdroid/TestUtils.java +++ b/app/src/test/java/org/fdroid/fdroid/TestUtils.java @@ -8,6 +8,7 @@ import android.content.ContextWrapper; import android.content.pm.ProviderInfo; import android.net.Uri; import androidx.test.core.app.ApplicationProvider; +import org.apache.commons.io.IOUtils; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.App; @@ -190,4 +191,17 @@ public class TestUtils { modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); } + + public static void ls(File dir) { + Process p = null; + try { + p = Runtime.getRuntime().exec("ls -l " + dir.getAbsolutePath()); + p.waitFor(); + for (String line : IOUtils.readLines(p.getInputStream())) { + System.out.println(line); + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/app/src/test/java/org/fdroid/fdroid/work/CleanCacheWorkerTest.java b/app/src/test/java/org/fdroid/fdroid/work/CleanCacheWorkerTest.java new file mode 100644 index 000000000..97783d7f7 --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/work/CleanCacheWorkerTest.java @@ -0,0 +1,93 @@ +package org.fdroid.fdroid.work; + +import android.content.Context; +import androidx.test.core.app.ApplicationProvider; +import org.fdroid.fdroid.BuildConfig; +import org.fdroid.fdroid.Preferences; +import org.fdroid.fdroid.nearby.LocalRepoManager; +import org.fdroid.fdroid.shadows.ShadowLog; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test non-time-based cache deletion methods. Robolectric is lacking full + * support for getting time from files, so methods that rely on time must be + * tested in {@code CleanCacheWorkerTest} in {@code androidTest}. + */ +@RunWith(RobolectricTestRunner.class) +public class CleanCacheWorkerTest { + public static final String TAG = "CleanCacheWorkerTest"; + + private static final Context CONTEXT = ApplicationProvider.getApplicationContext(); + private static final File FILES_DIR = CONTEXT.getFilesDir(); + + @Before + public void setUp() { + ShadowLog.stream = System.out; + Preferences.setupForTests(CONTEXT); + } + + @Test + public void testDeleteOldInstallerFiles() throws IOException { + Assume.assumeTrue(BuildConfig.FLAVOR.startsWith("full")); + ArrayList webRootAssetFiles = new ArrayList<>(); + File indexHtml = new File(FILES_DIR, "index.html"); + assertTrue(indexHtml.createNewFile()); + webRootAssetFiles.add(indexHtml); + for (String name : LocalRepoManager.WEB_ROOT_ASSET_FILES) { + File f = new File(FILES_DIR, name); + assertTrue(f.createNewFile()); + webRootAssetFiles.add(f); + } + File apk = new File(FILES_DIR, "fake.apk"); + assertTrue(apk.createNewFile()); + File giantblob = new File(FILES_DIR, "giantblob"); + assertTrue(giantblob.createNewFile()); + File obf = new File(FILES_DIR, "fake.obf"); + assertTrue(obf.createNewFile()); + File zip = new File(FILES_DIR, "fake.zip"); + assertTrue(zip.createNewFile()); + CleanCacheWorker.deleteOldInstallerFiles(CONTEXT); + assertFalse(apk.exists()); + assertFalse(giantblob.exists()); + assertFalse(obf.exists()); + assertFalse(zip.exists()); + for (File f : webRootAssetFiles) { + assertTrue(f.exists()); + } + } + + /** + * Pure smoke check, Robolectric does not support file times fully. + */ + @Test + public void testDeleteExpiredApksFromCache() { + CleanCacheWorker.deleteExpiredApksFromCache(CONTEXT); + } + + /** + * Pure smoke check, Robolectric does not support file times fully. + */ + @Test + public void testDeleteStrayIndexFiles() { + CleanCacheWorker.deleteStrayIndexFiles(CONTEXT); + } + + /** + * Pure smoke check, Robolectric does not support file times fully. + */ + @Test + public void testDeleteOldIcons() { + CleanCacheWorker.deleteOldIcons(CONTEXT); + } +}