Add unit tests for the WebStorageSizemanager. Also reduce the default quota and quota increase step
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 3074d2b..79deb61 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -238,8 +238,10 @@
// Set the default value for the Application Caches path.
appCachePath = ctx.getDir("appcache", 0).getPath();
// Determine the maximum size of the application cache.
- webStorageSizeManager = WebStorageSizeManager.getInstance(appCachePath,
- ctx);
+ webStorageSizeManager = new WebStorageSizeManager(
+ ctx,
+ new WebStorageSizeManager.StatFsDiskInfo(appCachePath),
+ new WebStorageSizeManager.WebKitAppCacheInfo(appCachePath));
appCacheMaxSize = webStorageSizeManager.getAppCacheMaxSize();
// Set the default value for the Database path.
databasePath = ctx.getDir("databases", 0).getPath();
diff --git a/src/com/android/browser/WebStorageSizeManager.java b/src/com/android/browser/WebStorageSizeManager.java
index e524f4c..ff21ea9 100644
--- a/src/com/android/browser/WebStorageSizeManager.java
+++ b/src/com/android/browser/WebStorageSizeManager.java
@@ -92,34 +92,98 @@
private final static boolean LOGD_ENABLED = com.android.browser.Browser.LOGD_ENABLED;
private final static String LOGTAG = "browser";
// The default quota value for an origin.
- private final static long ORIGIN_DEFAULT_QUOTA = 4 * 1024 * 1024; // 4MB
+ public final static long ORIGIN_DEFAULT_QUOTA = 3 * 1024 * 1024; // 3MB
// The default value for quota increases.
- private final static long QUOTA_INCREASE_STEP = 2 * 1024 * 1024; // 2MB
- // The name of the application cache file. Keep in sync with
- // WebCore/loader/appcache/ApplicationCacheStorage.cpp
- private final static String APPCACHE_FILE = "ApplicationCache.db";
- // The WebStorageSizeManager singleton.
- private static WebStorageSizeManager mManager;
+ public final static long QUOTA_INCREASE_STEP = 1 * 1024 * 1024; // 1MB
// The application context.
- private Context mContext;
+ private final Context mContext;
// The global Web storage limit.
- private long mGlobalLimit;
+ private final long mGlobalLimit;
// The maximum size of the application cache file.
private long mAppCacheMaxSize;
/**
- * Factory method.
- * @param path is a path on the partition where the app cache data is kept.
- * @param ctx is the browser application context.
- * @param storage is the WebStorage singleton.
- *
+ * Interface used by the WebStorageSizeManager to obtain information
+ * about the underlying file system. This functionality is separated
+ * into its own interface mainly for testing purposes.
*/
- public static WebStorageSizeManager getInstance(String appCachePath,
- Context ctx) {
- if (mManager == null) {
- mManager = new WebStorageSizeManager(appCachePath, ctx);
- }
- return mManager;
+ public interface DiskInfo {
+ /**
+ * @return the size of the free space in the file system.
+ */
+ public long getFreeSpaceSizeBytes();
+
+ /**
+ * @return the total size of the file system.
+ */
+ public long getTotalSizeBytes();
+ };
+
+ private DiskInfo mDiskInfo;
+ // For convenience, we provide a DiskInfo implementation that uses StatFs.
+ public static class StatFsDiskInfo implements DiskInfo {
+ private StatFs mFs;
+
+ public StatFsDiskInfo(String path) {
+ mFs = new StatFs(path);
+ }
+
+ public long getFreeSpaceSizeBytes() {
+ return mFs.getAvailableBlocks() * mFs.getBlockSize();
+ }
+
+ public long getTotalSizeBytes() {
+ return mFs.getBlockCount() * mFs.getBlockSize();
+ }
+ };
+
+ /**
+ * Interface used by the WebStorageSizeManager to obtain information
+ * about the appcache file. This functionality is separated into its own
+ * interface mainly for testing purposes.
+ */
+ public interface AppCacheInfo {
+ /**
+ * @return the current size of the appcache file.
+ */
+ public long getAppCacheSizeBytes();
+ };
+
+ // For convenience, we provide an AppCacheInfo implementation.
+ public static class WebKitAppCacheInfo implements AppCacheInfo {
+ // The name of the application cache file. Keep in sync with
+ // WebCore/loader/appcache/ApplicationCacheStorage.cpp
+ private final static String APPCACHE_FILE = "ApplicationCache.db";
+ private String mAppCachePath;
+
+ public WebKitAppCacheInfo(String path) {
+ mAppCachePath = path;
+ }
+
+ public long getAppCacheSizeBytes() {
+ File file = new File(mAppCachePath
+ + File.separator
+ + APPCACHE_FILE);
+ return file.length();
+ }
+ };
+
+ /**
+ * Public ctor
+ * @param ctx is the application context
+ * @param diskInfo is the DiskInfo instance used to query the file system.
+ * @param appCacheInfo is the AppCacheInfo used to query info about the
+ * appcache file.
+ */
+ public WebStorageSizeManager(Context ctx, DiskInfo diskInfo,
+ AppCacheInfo appCacheInfo) {
+ mContext = ctx;
+ mDiskInfo = diskInfo;
+ mGlobalLimit = getGlobalLimit();
+ // The initial max size of the app cache is either 25% of the global
+ // limit or the current size of the app cache file, whichever is bigger.
+ mAppCacheMaxSize = Math.max(mGlobalLimit / 4,
+ appCacheInfo.getAppCacheSizeBytes());
}
/**
@@ -150,11 +214,13 @@
+ databaseIdentifier
+ "(current quota: "
+ currentQuota
+ + ", total used quota: "
+ + totalUsedQuota
+ ")");
}
long totalUnusedQuota = mGlobalLimit - totalUsedQuota - mAppCacheMaxSize;
- if (totalUnusedQuota < QUOTA_INCREASE_STEP) {
+ if (totalUnusedQuota <= 0) {
// There definitely isn't any more space. Fire notifications
// and exit.
scheduleOutOfSpaceNotification();
@@ -167,8 +233,7 @@
// We have enough space inside mGlobalLimit.
long newOriginQuota = currentQuota;
if (newOriginQuota == 0) {
- // This is a new origin. It wants an initial quota. It is guaranteed
- // to get at least ORIGIN_INCREASE_STEP bytes.
+ // This is a new origin. It wants an initial quota.
newOriginQuota =
Math.min(ORIGIN_DEFAULT_QUOTA, totalUnusedQuota);
} else {
@@ -225,21 +290,12 @@
// Computes the global limit as a function of the size of the data
// partition and the amount of free space on that partition.
- private long getGlobalLimit(String path) {
- StatFs dataPartition = new StatFs(path);
- long freeSpace = dataPartition.getAvailableBlocks()
- * dataPartition.getBlockSize();
- long fileSystemSize = dataPartition.getBlockCount()
- * dataPartition.getBlockSize();
+ private long getGlobalLimit() {
+ long freeSpace = mDiskInfo.getFreeSpaceSizeBytes();
+ long fileSystemSize = mDiskInfo.getTotalSizeBytes();
return calculateGlobalLimit(fileSystemSize, freeSpace);
}
- // Returns the current size (in bytes) of the application cache file.
- private long getCurrentAppCacheSize(String path) {
- File file = new File(path + File.separator + APPCACHE_FILE);
- return file.length();
- }
-
/*package*/ static long calculateGlobalLimit(long fileSystemSizeBytes,
long freeSpaceBytes) {
if (fileSystemSizeBytes <= 0
@@ -270,13 +326,4 @@
private void scheduleOutOfSpaceNotification() {
// TODO(andreip): implement.
}
- // Private ctor.
- private WebStorageSizeManager(String appCachePath, Context ctx) {
- mContext = ctx;
- mGlobalLimit = getGlobalLimit(appCachePath);
- // The initial max size of the app cache is either 25% of the global
- // limit or the current size of the app cache file, whichever is bigger.
- mAppCacheMaxSize = Math.max(mGlobalLimit / 4,
- getCurrentAppCacheSize(appCachePath));
- }
}
\ No newline at end of file
diff --git a/tests/src/com/android/browser/WebStorageSizeManagerUnitTests.java b/tests/src/com/android/browser/WebStorageSizeManagerUnitTests.java
index 4ba758d..a2604e0 100644
--- a/tests/src/com/android/browser/WebStorageSizeManagerUnitTests.java
+++ b/tests/src/com/android/browser/WebStorageSizeManagerUnitTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
+import android.webkit.WebStorage;
/**
* This is a series of unit tests for the WebStorageSizeManager class.
@@ -25,7 +26,138 @@
*/
@MediumTest
public class WebStorageSizeManagerUnitTests extends AndroidTestCase {
+ // Used for testing the out-of-space callbacks.
+ private long mNewQuota;
+ // Callback functor that sets a new quota in case of out-of-space scenarios.
+ private class MockQuotaUpdater implements WebStorage.QuotaUpdater {
+ public void updateQuota(long newQuota) {
+ mNewQuota = newQuota;
+ }
+ }
+ // Mock the DiskInfo.
+ private class MockDiskInfo implements WebStorageSizeManager.DiskInfo {
+ private long mFreeSize;
+ private long mTotalSize;
+
+ public long getFreeSpaceSizeBytes() {
+ return mFreeSize;
+ }
+
+ public long getTotalSizeBytes() {
+ return mTotalSize;
+ }
+
+ public void setFreeSpaceSizeBytes(long freeSize) {
+ mFreeSize = freeSize;
+ }
+
+ public void setTotalSizeBytes(long totalSize) {
+ mTotalSize = totalSize;
+ }
+ }
+
+ // Mock the AppCacheInfo
+ public class MockAppCacheInfo implements WebStorageSizeManager.AppCacheInfo {
+ private long mAppCacheSize;
+
+ public long getAppCacheSizeBytes() {
+ return mAppCacheSize;
+ }
+
+ public void setAppCacheSizeBytes(long appCacheSize) {
+ mAppCacheSize = appCacheSize;
+ }
+ }
+
+ private MockQuotaUpdater mQuotaUpdater = new MockQuotaUpdater();
+ private final MockDiskInfo mDiskInfo = new MockDiskInfo();
+ private final MockAppCacheInfo mAppCacheInfo = new MockAppCacheInfo();
+ // Utility for making size computations easier to read.
+ private long bytes(double megabytes) {
+ return (new Double(megabytes * 1024 * 1024)).longValue();
+ }
+ /**
+ * Test the onExceededDatabaseQuota and onReachedMaxAppCacheSize callbacks
+ */
+ public void testCallbacks() {
+ long totalUsedQuota = 0;
+ final long defaultQuota = WebStorageSizeManager.ORIGIN_DEFAULT_QUOTA; // 3MB
+ final long quotaIncrease = WebStorageSizeManager.QUOTA_INCREASE_STEP; // 1MB
+ // We have 75 MB total, 24MB free so the global limit will be 12 MB.
+ mDiskInfo.setTotalSizeBytes(bytes(75));
+ mDiskInfo.setFreeSpaceSizeBytes(bytes(24));
+ // We have an appcache file size of 0 MB.
+ mAppCacheInfo.setAppCacheSizeBytes(0);
+ // Create the manager.
+ WebStorageSizeManager manager = new WebStorageSizeManager(null, mDiskInfo, mAppCacheInfo);
+ // We add origin 1.
+ long origin1Quota = 0;
+ manager.onExceededDatabaseQuota("1", "1", origin1Quota, totalUsedQuota, mQuotaUpdater);
+ assertEquals(defaultQuota, mNewQuota);
+ origin1Quota = mNewQuota;
+ totalUsedQuota += origin1Quota;
+
+ // We add origin 2.
+ long origin2Quota = 0;
+ manager.onExceededDatabaseQuota("2", "2", origin2Quota, totalUsedQuota, mQuotaUpdater);
+ assertEquals(defaultQuota, mNewQuota);
+ origin2Quota = mNewQuota;
+ totalUsedQuota += origin2Quota;
+
+ // Origin 1 runs out of space.
+ manager.onExceededDatabaseQuota("1", "1", origin1Quota, totalUsedQuota, mQuotaUpdater);
+ assertEquals(defaultQuota + quotaIncrease, mNewQuota);
+ totalUsedQuota -= origin1Quota;
+ origin1Quota = mNewQuota;
+ totalUsedQuota += origin1Quota;
+
+ // Origin 2 runs out of space.
+ manager.onExceededDatabaseQuota("2", "2", origin2Quota, totalUsedQuota, mQuotaUpdater);
+ assertEquals(defaultQuota + quotaIncrease, mNewQuota);
+ totalUsedQuota -= origin2Quota;
+ origin2Quota = mNewQuota;
+ totalUsedQuota += origin2Quota;
+
+ // We add origin 3. TotalUsedQuota is 8 (3 + 3 + 1 + 1). AppCacheMaxSize is 3 (12 / 4).
+ // So we have 1 MB free.
+ long origin3Quota = 0;
+ manager.onExceededDatabaseQuota("3", "3", origin3Quota, totalUsedQuota, mQuotaUpdater);
+ assertEquals(bytes(1), mNewQuota); // 1MB
+ origin3Quota = mNewQuota;
+ totalUsedQuota += origin3Quota;
+
+ // Origin 1 runs out of space again. We're also out of space so we can't give it more.
+ manager.onExceededDatabaseQuota("1", "1", origin1Quota, totalUsedQuota, mQuotaUpdater);
+ assertEquals(origin1Quota, mNewQuota);
+
+ // We try adding a new origin. Which will fail.
+ manager.onExceededDatabaseQuota("4", "4", 0, totalUsedQuota, mQuotaUpdater);
+ assertEquals(0, mNewQuota);
+
+ // AppCache size increases to 2MB...
+ mAppCacheInfo.setAppCacheSizeBytes(bytes(2));
+ // ... and wants 2MB more. Fail.
+ manager.onReachedMaxAppCacheSize(bytes(2), totalUsedQuota, mQuotaUpdater);
+ assertEquals(0, mNewQuota);
+
+ // The user nukes origin 2
+ totalUsedQuota -= origin2Quota;
+ origin2Quota = 0;
+ // TotalUsedQuota is 5 (9 - 4). AppCacheMaxSize is 3. AppCacheSize is 2.
+ // AppCache wants 1.5MB more
+ manager.onReachedMaxAppCacheSize(bytes(1.5), totalUsedQuota, mQuotaUpdater);
+ mAppCacheInfo.setAppCacheSizeBytes(mAppCacheInfo.getAppCacheSizeBytes() + bytes(2.5));
+ assertEquals(mAppCacheInfo.getAppCacheSizeBytes(), mNewQuota);
+
+ // We try adding a new origin. This time we succeed.
+ // TotalUsedQuota is 5. AppCacheMaxSize is 4.5. So we have 12 - 9.5 = 2.5 available.
+ long origin4Quota = 0;
+ manager.onExceededDatabaseQuota("4", "4", origin4Quota, totalUsedQuota, mQuotaUpdater);
+ assertEquals(bytes(2.5), mNewQuota);
+ origin4Quota = mNewQuota;
+ totalUsedQuota += origin4Quota;
+ }
/**
* Test the application caches max size calculator.
*/