1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.browser;
18
19import android.test.AndroidTestCase;
20import android.test.suitebuilder.annotation.MediumTest;
21import android.webkit.WebStorage;
22
23/**
24 * This is a series of unit tests for the WebStorageSizeManager class.
25 *
26 */
27@MediumTest
28public class WebStorageSizeManagerUnitTests extends AndroidTestCase {
29    // Used for testing the out-of-space callbacks.
30    private long mNewQuota;
31    // Callback functor that sets a new quota in case of out-of-space scenarios.
32    private class MockQuotaUpdater implements WebStorage.QuotaUpdater {
33        public void updateQuota(long newQuota) {
34            mNewQuota = newQuota;
35        }
36    }
37
38    // Mock the DiskInfo.
39    private class MockDiskInfo implements WebStorageSizeManager.DiskInfo {
40        private long mFreeSize;
41        private long mTotalSize;
42
43        public long getFreeSpaceSizeBytes() {
44            return mFreeSize;
45        }
46
47        public long getTotalSizeBytes() {
48            return mTotalSize;
49        }
50
51        public void setFreeSpaceSizeBytes(long freeSize) {
52            mFreeSize = freeSize;
53        }
54
55        public void setTotalSizeBytes(long totalSize) {
56            mTotalSize = totalSize;
57        }
58    }
59
60    // Mock the AppCacheInfo
61    public class MockAppCacheInfo implements WebStorageSizeManager.AppCacheInfo {
62        private long mAppCacheSize;
63
64        public long getAppCacheSizeBytes() {
65            return mAppCacheSize;
66        }
67
68        public void setAppCacheSizeBytes(long appCacheSize) {
69            mAppCacheSize = appCacheSize;
70        }
71    }
72
73    private MockQuotaUpdater mQuotaUpdater = new MockQuotaUpdater();
74    private final MockDiskInfo mDiskInfo = new MockDiskInfo();
75    private final MockAppCacheInfo mAppCacheInfo = new MockAppCacheInfo();
76    // Utility for making size computations easier to read.
77    private long bytes(double megabytes) {
78        return (new Double(megabytes * 1024 * 1024)).longValue();
79    }
80    /**
81     * Test the onExceededDatabaseQuota and onReachedMaxAppCacheSize callbacks
82     */
83    public void testCallbacks() {
84        long totalUsedQuota = 0;
85        final long quotaIncrease = WebStorageSizeManager.QUOTA_INCREASE_STEP;  // 1MB
86
87        // We have 75 MB total, 24MB free so the global limit will be 12 MB.
88        mDiskInfo.setTotalSizeBytes(bytes(75));
89        mDiskInfo.setFreeSpaceSizeBytes(bytes(24));
90        // We have an appcache file size of 0 MB.
91        mAppCacheInfo.setAppCacheSizeBytes(0);
92        // Create the manager.
93        WebStorageSizeManager manager = new WebStorageSizeManager(getContext(), mDiskInfo,
94                mAppCacheInfo);
95        // We add origin 1.
96        long origin1Quota = 0;
97        long origin1EstimatedSize = bytes(3.5);
98        manager.onExceededDatabaseQuota("1", "1", origin1Quota, origin1EstimatedSize, totalUsedQuota, mQuotaUpdater);
99        assertEquals(origin1EstimatedSize, mNewQuota);
100        origin1Quota = mNewQuota;
101        totalUsedQuota += origin1Quota;
102
103        // We add origin 2.
104        long origin2Quota = 0;
105        long origin2EstimatedSize = bytes(2.5);
106        manager.onExceededDatabaseQuota("2", "2", origin2Quota, origin2EstimatedSize, totalUsedQuota, mQuotaUpdater);
107        assertEquals(origin2EstimatedSize, mNewQuota);
108        origin2Quota = mNewQuota;
109        totalUsedQuota += origin2Quota;
110
111        // Origin 1 runs out of space.
112        manager.onExceededDatabaseQuota("1", "1", origin1Quota, 0, totalUsedQuota, mQuotaUpdater);
113        assertEquals(origin1EstimatedSize + quotaIncrease, mNewQuota);
114        totalUsedQuota -= origin1Quota;
115        origin1Quota = mNewQuota;
116        totalUsedQuota += origin1Quota;
117
118        // Origin 2 runs out of space.
119        manager.onExceededDatabaseQuota("2", "2", origin2Quota, 0, totalUsedQuota, mQuotaUpdater);
120        assertEquals(origin2EstimatedSize + quotaIncrease, mNewQuota);
121        totalUsedQuota -= origin2Quota;
122        origin2Quota = mNewQuota;
123        totalUsedQuota += origin2Quota;
124
125        // We add origin 3. TotalUsedQuota is 8 (3.5 + 2.5 + 1 + 1). AppCacheMaxSize is 3 (12 / 4).
126        // So we have 1 MB free.
127        long origin3Quota = 0;
128        long origin3EstimatedSize = bytes(5);
129        manager.onExceededDatabaseQuota("3", "3", origin3Quota, origin3EstimatedSize, totalUsedQuota, mQuotaUpdater);
130        assertEquals(0, mNewQuota);  // We cannot satisfy the estimatedSize
131        origin3Quota = mNewQuota;
132        totalUsedQuota += origin3Quota;
133
134        // Origin 1 runs out of space again. It should increase it's quota to take the last 1MB.
135        manager.onExceededDatabaseQuota("1", "1", origin1Quota, 0, totalUsedQuota, mQuotaUpdater);
136        assertEquals(origin1Quota + quotaIncrease, mNewQuota);
137        totalUsedQuota -= origin1Quota;
138        origin1Quota = mNewQuota;
139        totalUsedQuota += origin1Quota;
140
141        // Origin 1 runs out of space again. It should inow fail to increase in size.
142        manager.onExceededDatabaseQuota("1", "1", origin1Quota, 0, totalUsedQuota, mQuotaUpdater);
143        assertEquals(origin1Quota, mNewQuota);
144
145        // We try adding a new origin. Which will fail.
146        manager.onExceededDatabaseQuota("4", "4", 0, bytes(1), totalUsedQuota, mQuotaUpdater);
147        assertEquals(0, mNewQuota);
148
149        // AppCache size increases to 2MB...
150        mAppCacheInfo.setAppCacheSizeBytes(bytes(2));
151        // ... and wants 2MB more. Fail.
152        manager.onReachedMaxAppCacheSize(bytes(2), totalUsedQuota, mQuotaUpdater);
153        assertEquals(0, mNewQuota);
154
155        // The user nukes origin 2
156        totalUsedQuota -= origin2Quota;
157        origin2Quota = 0;
158        // TotalUsedQuota is 5.5 (9 - 3.5). AppCacheMaxSize is 3. AppCacheSize is 2.
159        // AppCache wants 1.5MB more
160        manager.onReachedMaxAppCacheSize(bytes(1.5), totalUsedQuota, mQuotaUpdater);
161        mAppCacheInfo.setAppCacheSizeBytes(mAppCacheInfo.getAppCacheSizeBytes() + bytes(2.5));
162        assertEquals(mAppCacheInfo.getAppCacheSizeBytes(), mNewQuota - WebStorageSizeManager.APPCACHE_MAXSIZE_PADDING);
163
164        // We try adding a new origin. This time we succeed.
165        // TotalUsedQuota is 5.5. AppCacheMaxSize is 5.0. So we have 12 - 10.5 = 1.5 available.
166        long origin4Quota = 0;
167        long origin4EstimatedSize = bytes(1.5);
168        manager.onExceededDatabaseQuota("4", "4", origin4Quota, origin4EstimatedSize, totalUsedQuota, mQuotaUpdater);
169        assertEquals(bytes(1.5), mNewQuota);
170        origin4Quota = mNewQuota;
171        totalUsedQuota += origin4Quota;
172    }
173    /**
174     * Test the application caches max size calculator.
175     */
176    public void testCalculateGlobalLimit() {
177        long fileSystemSize = 78643200;  // 75 MB
178        long freeSpaceSize = 25165824;  // 24 MB
179        long maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
180        assertEquals(12582912, maxSize);  // 12MB
181
182        fileSystemSize = 78643200;  // 75 MB
183        freeSpaceSize = 60 * 1024 * 1024;  // 60MB
184        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
185        assertEquals(19922944, maxSize);  // 19MB
186
187        fileSystemSize = 8589934592L;  // 8 GB
188        freeSpaceSize = 4294967296L;  // 4 GB
189        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
190        assertEquals(536870912L, maxSize);  // 512 MB
191
192        fileSystemSize = -14;
193        freeSpaceSize = 21;
194        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
195        assertEquals(0, maxSize);
196
197        fileSystemSize = 100;
198        freeSpaceSize = 101;
199        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
200        assertEquals(0, maxSize);
201
202        fileSystemSize = 3774873; // ~4.2 MB
203        freeSpaceSize = 2560000;  // ~2.4 MB
204        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
205        assertEquals(2097152, maxSize);  // 2 MB
206
207        fileSystemSize = 4404019; // ~4.2 MB
208        freeSpaceSize = 3774873;  // ~3.6 MB
209        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
210        assertEquals(2097152, maxSize);  // 2 MB
211
212        fileSystemSize = 4404019; // ~4.2 MB
213        freeSpaceSize = 4404019;  // ~4.2 MB
214        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
215        assertEquals(3145728, maxSize);  // 3 MB
216
217        fileSystemSize = 1048576; // 1 MB
218        freeSpaceSize = 1048575;  // 1 MB - 1 byte
219        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
220        assertEquals(0, maxSize);
221
222        fileSystemSize = 3774873; // ~3.6 MB
223        freeSpaceSize = 2097151;  // 2 MB - 1 byte
224        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
225        assertEquals(0, maxSize);
226
227        fileSystemSize = 3774873; // ~3.6 MB
228        freeSpaceSize = 2097151;  // 2 MB
229        maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
230        assertEquals(0, maxSize);
231    }
232
233    public void testManyDatabasesOnOneOrigin() {
234        // This test ensures that if an origin creates more than one database, the quota that is
235        // assigned to the origin after the second creation is enough to satisfy all databases
236        // under that origin.
237        // See b/2417477.
238
239        long totalUsedQuota = 0;
240        mDiskInfo.setTotalSizeBytes(bytes(100));
241        mDiskInfo.setFreeSpaceSizeBytes(bytes(100));
242        // This should give us a storage area of 13MB, with 3.25MB for appcache and 9.75MB for
243        // databases.
244        assertEquals(bytes(13), WebStorageSizeManager.calculateGlobalLimit(
245                mDiskInfo.getTotalSizeBytes(), mDiskInfo.getFreeSpaceSizeBytes()));
246
247        // We have an appcache file size of 0 MB.
248        mAppCacheInfo.setAppCacheSizeBytes(0);
249
250        // Create the manager.
251        WebStorageSizeManager manager = new WebStorageSizeManager(getContext(), mDiskInfo,
252                mAppCacheInfo);
253
254        // We add an origin.
255        long originQuota = 0;
256        long database1EstimatedSize = bytes(2);
257        manager.onExceededDatabaseQuota("1", "1", originQuota, database1EstimatedSize,
258                totalUsedQuota, mQuotaUpdater);
259        assertEquals(database1EstimatedSize, mNewQuota);
260        originQuota = mNewQuota;
261        totalUsedQuota = originQuota;
262
263        // Now try to create a new database under the origin, by invoking onExceededDatabaseQuota
264        // again. This time, request more space than the old quota + the quota increase step.
265        long database2EstimatedSize = bytes(3.5);
266        manager.onExceededDatabaseQuota("1", "2", originQuota, database2EstimatedSize,
267                totalUsedQuota, mQuotaUpdater);
268        assertEquals(database1EstimatedSize + database2EstimatedSize, mNewQuota);
269        originQuota = mNewQuota;
270        totalUsedQuota = originQuota;
271
272        // Create another database, but this time use a size that will overflow the space on the
273        // device. It should be denied.
274        long database3EstimatedSize = bytes(50);
275        manager.onExceededDatabaseQuota("1", "3", originQuota, database3EstimatedSize,
276                totalUsedQuota, mQuotaUpdater);
277        assertEquals(originQuota, mNewQuota);
278
279        // Create another database. This time, request less than the old quota.
280        long database4EstimatedSize = bytes(2);
281        manager.onExceededDatabaseQuota("1", "4", originQuota, database4EstimatedSize,
282                totalUsedQuota, mQuotaUpdater);
283        assertEquals(database1EstimatedSize + database2EstimatedSize + database4EstimatedSize,
284                mNewQuota);
285        originQuota = mNewQuota;
286        totalUsedQuota = originQuota;
287
288        // Now have the first database overflow it's quota. We should get 1 more MB.
289        manager.onExceededDatabaseQuota("1", "1", originQuota, 0, totalUsedQuota, mQuotaUpdater);
290        assertEquals(database1EstimatedSize + database2EstimatedSize + database4EstimatedSize +
291                bytes(1), mNewQuota);
292        originQuota = mNewQuota;
293        totalUsedQuota = originQuota;
294
295        // Create a db under the origin that uses a quota less than the usual quota increase step.
296        long database5EstimatedSize = bytes(0.5);
297        manager.onExceededDatabaseQuota("1", "5", originQuota, database5EstimatedSize,
298                totalUsedQuota, mQuotaUpdater);
299        assertEquals(database1EstimatedSize + database2EstimatedSize + database4EstimatedSize +
300                bytes(1) + database5EstimatedSize, mNewQuota);
301    }
302}
303