summaryrefslogtreecommitdiffstats
path: root/src/tests/src/com/android/browser
diff options
context:
space:
mode:
authorAbhisek Devkota <ciwrl@cyanogenmod.com>2015-08-28 17:43:17 -0700
committerAbhisek Devkota <ciwrl@cyanogenmod.com>2015-08-28 17:43:17 -0700
commitf6ebac34baf99592f414a228490c2625a0fa8e37 (patch)
tree8f15ab75baa1eb3572309ac639c062a61de92705 /src/tests/src/com/android/browser
parent8b6a361c4b0d45d3e3e01019db1900d86ccba3b1 (diff)
downloadandroid_packages_apps_Gello-f6ebac34baf99592f414a228490c2625a0fa8e37.tar.gz
android_packages_apps_Gello-f6ebac34baf99592f414a228490c2625a0fa8e37.tar.bz2
android_packages_apps_Gello-f6ebac34baf99592f414a228490c2625a0fa8e37.zip
Combine source & prebuilt to single project
Todo: Makefile logic
Diffstat (limited to 'src/tests/src/com/android/browser')
-rw-r--r--src/tests/src/com/android/browser/BrowserLaunchPerformance.java26
-rw-r--r--src/tests/src/com/android/browser/BrowserProviderTests.java177
-rw-r--r--src/tests/src/com/android/browser/IntentHandlerTests.java164
-rw-r--r--src/tests/src/com/android/browser/PopularUrlsTest.java569
-rw-r--r--src/tests/src/com/android/browser/TestWebChromeClient.java204
-rw-r--r--src/tests/src/com/android/browser/TestWebViewClient.java128
-rw-r--r--src/tests/src/com/android/browser/WebStorageSizeManagerUnitTests.java302
-rw-r--r--src/tests/src/com/android/browser/tests/BP1to2UpgradeTests.java148
-rw-r--r--src/tests/src/com/android/browser/tests/BP2ProviderTests.java139
-rw-r--r--src/tests/src/com/android/browser/tests/BP2UriObserverTests.java105
-rw-r--r--src/tests/src/com/android/browser/tests/BookmarksTests.java64
-rw-r--r--src/tests/src/com/android/browser/tests/utils/BP2TestCaseHelper.java233
-rw-r--r--src/tests/src/com/android/browser/tests/utils/MockContentResolver2.java116
-rw-r--r--src/tests/src/com/android/browser/tests/utils/MockObserverNode.java169
-rw-r--r--src/tests/src/com/android/browser/tests/utils/ProviderTestCase3.java180
15 files changed, 2724 insertions, 0 deletions
diff --git a/src/tests/src/com/android/browser/BrowserLaunchPerformance.java b/src/tests/src/com/android/browser/BrowserLaunchPerformance.java
new file mode 100644
index 00000000..c5d4279f
--- /dev/null
+++ b/src/tests/src/com/android/browser/BrowserLaunchPerformance.java
@@ -0,0 +1,26 @@
+package com.android.browser;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.test.LaunchPerformanceBase;
+
+public class BrowserLaunchPerformance extends LaunchPerformanceBase {
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ super.onCreate(arguments);
+
+ mIntent.setClassName(getTargetContext(), "com.android.browser.BrowserActivity");
+ start();
+ }
+
+ /**
+ * Calls LaunchApp and finish.
+ */
+ @Override
+ public void onStart() {
+ super.onStart();
+ LaunchApp();
+ finish(Activity.RESULT_OK, mResults);
+ }
+}
diff --git a/src/tests/src/com/android/browser/BrowserProviderTests.java b/src/tests/src/com/android/browser/BrowserProviderTests.java
new file mode 100644
index 00000000..c63cad17
--- /dev/null
+++ b/src/tests/src/com/android/browser/BrowserProviderTests.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import com.android.browser.provider.BrowserProvider;
+import com.android.browser.provider.BrowserProvider2;
+import com.android.browser.tests.utils.ProviderTestCase3;
+
+import android.app.SearchManager;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.BrowserContract;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link BrowserProvider}.
+ */
+@MediumTest
+public class BrowserProviderTests extends ProviderTestCase3<BrowserProvider2> {
+
+ private ArrayList<Uri> mDeleteUris;
+
+ public BrowserProviderTests() {
+ super(BrowserProvider2.class,
+ BrowserContract.AUTHORITY, BrowserProvider2.LEGACY_AUTHORITY);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ mDeleteUris = new ArrayList<Uri>();
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ for (Uri uri : mDeleteUris) {
+ deleteUri(uri);
+ }
+ super.tearDown();
+ }
+
+ public void testHasDefaultBookmarks() {
+ Cursor c = getBookmarksSuggest("");
+ try {
+ assertTrue("No default bookmarks", c.getCount() > 0);
+ } finally {
+ c.close();
+ }
+ }
+
+ public void testPartialFirstTitleWord() {
+ assertInsertQuery("http://www.example.com/rasdfe", "nfgjra sdfywe", "nfgj");
+ }
+
+ public void testFullFirstTitleWord() {
+ assertInsertQuery("http://www.example.com/", "nfgjra dfger", "nfgjra");
+ }
+
+ public void testFullFirstTitleWordPartialSecond() {
+ assertInsertQuery("http://www.example.com/", "nfgjra dfger", "nfgjra df");
+ }
+
+ public void testFullTitle() {
+ assertInsertQuery("http://www.example.com/", "nfgjra dfger", "nfgjra dfger");
+ }
+
+// Not implemented in BrowserProvider
+// public void testFullSecondTitleWord() {
+// assertInsertQuery("http://www.example.com/rasdfe", "nfgjra sdfywe", "sdfywe");
+// }
+
+ public void testFullTitleJapanese() {
+ String title = "\u30ae\u30e3\u30e9\u30ea\u30fc\u30fcGoogle\u691c\u7d22";
+ assertInsertQuery("http://www.example.com/sdaga", title, title);
+ }
+
+ public void testPartialTitleJapanese() {
+ String title = "\u30ae\u30e3\u30e9\u30ea\u30fc\u30fcGoogle\u691c\u7d22";
+ String query = "\u30ae\u30e3\u30e9\u30ea\u30fc";
+ assertInsertQuery("http://www.example.com/sdaga", title, query);
+ }
+
+ // Test for http://b/issue?id=2152749
+ public void testSoundmarkTitleJapanese() {
+ String title = "\u30ae\u30e3\u30e9\u30ea\u30fc\u30fcGoogle\u691c\u7d22";
+ String query = "\u30ad\u30e3\u30e9\u30ea\u30fc";
+ assertInsertQuery("http://www.example.com/sdaga", title, query);
+ }
+
+ //
+ // Utilities
+ //
+
+ private void assertInsertQuery(String url, String title, String query) {
+ addBookmark(url, title);
+ assertQueryReturns(url, title, query);
+ }
+
+ private void assertQueryReturns(String url, String title, String query) {
+ Cursor c = getBookmarksSuggest(query);
+ try {
+ assertTrue(title + " not matched by " + query, c.getCount() > 0);
+ assertTrue("More than one result for " + query, c.getCount() == 1);
+ while (c.moveToNext()) {
+ String text1 = getCol(c, SearchManager.SUGGEST_COLUMN_TEXT_1);
+ assertNotNull(text1);
+ assertEquals("Bad title", title, text1);
+ String text2 = getCol(c, SearchManager.SUGGEST_COLUMN_TEXT_2);
+ assertNotNull(text2);
+ String data = getCol(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
+ assertNotNull(data);
+ assertEquals("Bad URL", url, data);
+ }
+ } finally {
+ c.close();
+ }
+ }
+
+ private Cursor getBookmarksSuggest(String query) {
+ Uri suggestUri = Uri.parse("content://browser/bookmarks/search_suggest_query");
+ String[] selectionArgs = { query };
+ Cursor c = getMockContentResolver().query(suggestUri, null, "url LIKE ?",
+ selectionArgs, null);
+ assertNotNull(c);
+ return c;
+ }
+
+ private void addBookmark(String url, String title) {
+ Uri uri = insertBookmark(url, title);
+ assertNotNull(uri);
+ assertFalse(android.provider.Browser.BOOKMARKS_URI.equals(uri));
+ mDeleteUris.add(uri);
+ }
+
+ private Uri insertBookmark(String url, String title) {
+ ContentValues values = new ContentValues();
+ values.put("title", title);
+ values.put("url", url);
+ values.put("visits", 0);
+ values.put("date", 0);
+ values.put("created", 0);
+ values.put("bookmark", 1);
+ return getMockContentResolver().insert(android.provider.Browser.BOOKMARKS_URI,
+ values);
+ }
+
+ private void deleteUri(Uri uri) {
+ int count = getMockContentResolver().delete(uri, null, null);
+ assertEquals("Failed to delete " + uri, 1, count);
+ }
+
+ private static String getCol(Cursor c, String name) {
+ int col = c.getColumnIndex(name);
+ String msg = "Column " + name + " not found, columns: "
+ + Arrays.toString(c.getColumnNames());
+ assertTrue(msg, col >= 0);
+ return c.getString(col);
+ }
+}
diff --git a/src/tests/src/com/android/browser/IntentHandlerTests.java b/src/tests/src/com/android/browser/IntentHandlerTests.java
new file mode 100644
index 00000000..50cf5029
--- /dev/null
+++ b/src/tests/src/com/android/browser/IntentHandlerTests.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.browser;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.Browser;
+import android.test.ActivityInstrumentationTestCase2;
+import android.text.TextUtils;
+import org.codeaurora.swe.WebView;
+
+public class IntentHandlerTests extends ActivityInstrumentationTestCase2<BrowserActivity> {
+
+ // How long to wait to receive onPageStarted
+ static final int START_LOAD_TIMEOUT = 20000; // ms
+ static final int POLL_INTERVAL = 50; // ms
+ boolean mHasStarted = false;
+
+ public IntentHandlerTests() {
+ super(BrowserActivity.class);
+ }
+
+ public void testSwitchToTabWithUrl() throws Throwable {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse("http://google.com/"));
+ sendIntent(intent);
+ Controller controller = getActivity().getController();
+ Tab tabGoogle = controller.getCurrentTab();
+ assertNotNull("Current tab (google.com", tabGoogle);
+ assertEquals("http://google.com/", tabGoogle.getOriginalUrl());
+ assertEquals(1, controller.getTabs().size());
+ intent.setData(Uri.parse("http://maps.google.com/"));
+ sendIntent(intent);
+ Tab tabMaps = controller.getCurrentTab();
+ assertNotSame(tabGoogle, tabMaps);
+ assertNotNull("Current tab (maps.google.com)", tabMaps);
+ assertEquals(2, controller.getTabs().size());
+ intent.setData(Uri.parse("http://google.com/"));
+ sendIntent(intent);
+ assertEquals(tabGoogle, controller.getCurrentTab());
+ assertEquals(2, controller.getTabs().size());
+ }
+
+ public void testShortcut() throws Throwable {
+ Intent intent = BookmarkUtils.createShortcutIntent("http://google.com/");
+ sendIntent(intent);
+ Controller controller = getActivity().getController();
+ Tab tabGoogle = controller.getCurrentTab();
+ assertEquals("http://google.com/", tabGoogle.getOriginalUrl());
+ assertEquals(1, controller.getTabs().size());
+ sendIntent(intent);
+ assertEquals(1, controller.getTabs().size());
+ assertEquals(tabGoogle, controller.getCurrentTab());
+ directlyLoadUrl(tabGoogle, "http://maps.google.com/");
+ sendIntent(intent);
+ if (BrowserActivity.isTablet(getActivity())) {
+ assertEquals(2, controller.getTabs().size());
+ assertNotSame(tabGoogle, controller.getCurrentTab());
+ assertEquals("http://maps.google.com/", tabGoogle.getOriginalUrl());
+ Tab currentTab = controller.getCurrentTab();
+ assertEquals("http://google.com/", currentTab.getOriginalUrl());
+ } else {
+ assertEquals(1, controller.getTabs().size());
+ assertEquals(tabGoogle, controller.getCurrentTab());
+ assertEquals("http://google.com/", tabGoogle.getOriginalUrl());
+ }
+ }
+
+ public void testApplication() throws Throwable {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse("http://google.com/"));
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID, getClass().getName());
+ sendIntent(intent);
+ Controller controller = getActivity().getController();
+ Tab tabGoogle = controller.getCurrentTab();
+ assertNotNull("Current tab (google.com", tabGoogle);
+ assertEquals("http://google.com/", tabGoogle.getOriginalUrl());
+ assertEquals(1, controller.getTabs().size());
+ intent.setData(Uri.parse("http://maps.google.com/"));
+ sendIntent(intent);
+ Tab tabMaps = controller.getCurrentTab();
+ assertEquals("http://maps.google.com/", tabMaps.getOriginalUrl());
+ if (BrowserActivity.isTablet(getActivity())) {
+ assertEquals(2, controller.getTabs().size());
+ assertNotSame(tabGoogle, tabMaps);
+ assertEquals("http://google.com/", tabGoogle.getOriginalUrl());
+ } else {
+ assertEquals(1, controller.getTabs().size());
+ assertEquals(tabGoogle, tabMaps);
+ }
+ }
+
+ /**
+ * Simulate clicking a link by loading a URL directly on the WebView,
+ * bypassing Tab, Controller, etc..
+ * @throws Throwable
+ */
+ private void directlyLoadUrl(final Tab tab, final String url) throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ WebView web = tab.getWebView();
+ web.loadUrl(url);
+ }
+ });
+ waitForLoadStart(tab, url);
+ }
+
+ void waitForLoadStart(final Tab tab, final String url) throws InterruptedException {
+ long start = System.currentTimeMillis();
+ while (!TextUtils.equals(tab.getOriginalUrl(), url)) {
+ if (start + START_LOAD_TIMEOUT < System.currentTimeMillis()) {
+ throw new RuntimeException("Didn't receive onPageStarted!");
+ }
+ Thread.sleep(POLL_INTERVAL);
+ }
+ }
+
+ private void sendIntent(final Intent intent) throws Throwable {
+ sendIntent(intent, true);
+ }
+
+ private void sendIntent(final Intent intent, boolean waitForLoadStart) throws Throwable {
+ if (!mHasStarted) {
+ // Prevent crash recovery from happening
+ intent.putExtra(Controller.NO_CRASH_RECOVERY, true);
+ setActivityIntent(intent);
+ getActivity();
+ } else {
+ final Activity activity = getActivity();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ getInstrumentation().callActivityOnNewIntent(activity, intent);
+ }
+ });
+ }
+ if (waitForLoadStart) {
+ String url = intent.getDataString();
+ Tab tab = getActivity().getController().getCurrentTab();
+ waitForLoadStart(tab, url);
+ }
+ }
+
+ @Override
+ public BrowserActivity getActivity() {
+ mHasStarted = true;
+ return super.getActivity();
+ }
+}
diff --git a/src/tests/src/com/android/browser/PopularUrlsTest.java b/src/tests/src/com/android/browser/PopularUrlsTest.java
new file mode 100644
index 00000000..0070948e
--- /dev/null
+++ b/src/tests/src/com/android/browser/PopularUrlsTest.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.net.Uri;
+import android.net.http.SslError;
+import android.os.Environment;
+import android.provider.Browser;
+import android.test.ActivityInstrumentationTestCase2;
+import android.text.TextUtils;
+import android.util.Log;
+import android.webkit.DownloadListener;
+import android.webkit.HttpAuthHandler;
+import android.webkit.JsPromptResult;
+import android.webkit.JsResult;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebViewClassic
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.codeaurora.swe.WebView;
+/**
+ *
+ * Iterates over a list of URLs from a file and outputs the time to load each.
+ */
+public class PopularUrlsTest extends ActivityInstrumentationTestCase2<BrowserActivity> {
+
+ private final static String TAG = "PopularUrlsTest";
+ private final static String newLine = System.getProperty("line.separator");
+ private final static String sInputFile = "popular_urls.txt";
+ private final static String sOutputFile = "test_output.txt";
+ private final static String sStatusFile = "test_status.txt";
+ private final static File sExternalStorage = Environment.getExternalStorageDirectory();
+
+ private final static int PERF_LOOPCOUNT = 10;
+ private final static int STABILITY_LOOPCOUNT = 1;
+ private final static int PAGE_LOAD_TIMEOUT = 120000; // 2 minutes
+
+ private BrowserActivity mActivity = null;
+ private Controller mController = null;
+ private Instrumentation mInst = null;
+ private CountDownLatch mLatch = new CountDownLatch(1);
+ private RunStatus mStatus;
+ private boolean pageLoadFinishCalled, pageProgressFull;
+
+ public PopularUrlsTest() {
+ super(BrowserActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("about:blank"));
+ i.putExtra(Controller.NO_CRASH_RECOVERY, true);
+ setActivityIntent(i);
+ mActivity = getActivity();
+ mController = mActivity.getController();
+ mInst = getInstrumentation();
+ mInst.waitForIdleSync();
+
+ mStatus = RunStatus.load();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mStatus != null) {
+ mStatus.cleanUp();
+ }
+
+ super.tearDown();
+ }
+
+ BufferedReader getInputStream() throws FileNotFoundException {
+ return getInputStream(sInputFile);
+ }
+
+ BufferedReader getInputStream(String inputFile) throws FileNotFoundException {
+ FileReader fileReader = new FileReader(new File(sExternalStorage, inputFile));
+ BufferedReader bufferedReader = new BufferedReader(fileReader);
+
+ return bufferedReader;
+ }
+
+ OutputStreamWriter getOutputStream() throws IOException {
+ return getOutputStream(sOutputFile);
+ }
+
+ OutputStreamWriter getOutputStream(String outputFile) throws IOException {
+ return new FileWriter(new File(sExternalStorage, outputFile), mStatus.getIsRecovery());
+ }
+
+ /**
+ * Gets the browser ready for testing by starting the application
+ * and wrapping the WebView's helper clients.
+ */
+ void setUpBrowser() {
+ mInst.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ setupBrowserInternal();
+ }
+ });
+ }
+
+ void setupBrowserInternal() {
+ Tab tab = mController.getTabControl().getCurrentTab();
+ WebView webView = tab.getWebView();
+
+ webView.setWebChromeClient(new TestWebChromeClient(
+ WebViewClassic.fromWebView(webView).getWebChromeClient()) {
+
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ super.onProgressChanged(view, newProgress);
+ if (newProgress >= 100) {
+ if (!pageProgressFull) {
+ // void duplicate calls
+ pageProgressFull = true;
+ if (pageLoadFinishCalled) {
+ //reset latch and move forward only if both indicators are true
+ resetLatch();
+ }
+ }
+ }
+ }
+
+ /**
+ * Dismisses and logs Javascript alerts.
+ */
+ @Override
+ public boolean onJsAlert(WebView view, String url, String message,
+ JsResult result) {
+ String logMsg = String.format("JS Alert '%s' received from %s", message, url);
+ Log.w(TAG, logMsg);
+ result.confirm();
+
+ return true;
+ }
+
+ /**
+ * Confirms and logs Javascript alerts.
+ */
+ @Override
+ public boolean onJsConfirm(WebView view, String url, String message,
+ JsResult result) {
+ String logMsg = String.format("JS Confirmation '%s' received from %s",
+ message, url);
+ Log.w(TAG, logMsg);
+ result.confirm();
+
+ return true;
+ }
+
+ /**
+ * Confirms and logs Javascript alerts, providing the default value.
+ */
+ @Override
+ public boolean onJsPrompt(WebView view, String url, String message,
+ String defaultValue, JsPromptResult result) {
+ String logMsg = String.format("JS Prompt '%s' received from %s; " +
+ "Giving default value '%s'", message, url, defaultValue);
+ Log.w(TAG, logMsg);
+ result.confirm(defaultValue);
+
+ return true;
+ }
+
+ /*
+ * Skip the unload confirmation
+ */
+ @Override
+ public boolean onJsBeforeUnload(
+ WebView view, String url, String message, JsResult result) {
+ result.confirm();
+ return true;
+ }
+ });
+
+ webView.setWebViewClient(new TestWebViewClient(
+ WebViewClassic.fromWebView(webView).getWebViewClient()) {
+
+ /**
+ * Bypasses and logs errors.
+ */
+ @Override
+ public void onReceivedError(WebView view, int errorCode,
+ String description, String failingUrl) {
+ String message = String.format("Error '%s' (%d) loading url: %s",
+ description, errorCode, failingUrl);
+ Log.w(TAG, message);
+ }
+
+ /**
+ * Ignores and logs SSL errors.
+ */
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler,
+ SslError error) {
+ Log.w(TAG, "SSL error: " + error);
+ handler.proceed();
+ }
+
+ /**
+ * Ignores http auth with dummy username and password
+ */
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
+ String host, String realm) {
+ handler.proceed("user", "passwd");
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ if (!pageLoadFinishCalled) {
+ pageLoadFinishCalled = true;
+ if (pageProgressFull) {
+ //reset latch and move forward only if both indicators are true
+ resetLatch();
+ }
+ }
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if (!(url.startsWith("http://") || url.startsWith("https://"))) {
+ Log.v(TAG, String.format("suppressing non-http url scheme: %s", url));
+ return true;
+ }
+ return super.shouldOverrideUrlLoading(view, url);
+ }
+ });
+
+ webView.setDownloadListener(new DownloadListener() {
+
+ @Override
+ public void onDownloadStart(String url, String userAgent, String contentDisposition,
+ String mimetype, long contentLength) {
+ Log.v(TAG, String.format("Download request ignored: %s", url));
+ }
+ });
+ }
+
+ void resetLatch() {
+ if (mLatch.getCount() != 1) {
+ Log.w(TAG, "Expecting latch to be 1, but it's not!");
+ } else {
+ mLatch.countDown();
+ }
+ }
+
+ void resetForNewPage() {
+ mLatch = new CountDownLatch(1);
+ pageLoadFinishCalled = false;
+ pageProgressFull = false;
+ }
+
+ void waitForLoad() throws InterruptedException {
+ boolean timedout = !mLatch.await(PAGE_LOAD_TIMEOUT, TimeUnit.MILLISECONDS);
+ if (timedout) {
+ Log.w(TAG, "page timeout. trying to stop.");
+ // try to stop page load
+ mInst.runOnMainSync(new Runnable(){
+ public void run() {
+ mController.getTabControl().getCurrentTab().getWebView().stopLoading();
+ }
+ });
+ // try to wait for count down latch again
+ timedout = !mLatch.await(5000, TimeUnit.MILLISECONDS);
+ if (timedout) {
+ throw new RuntimeException("failed to stop timedout site, is browser pegged?");
+ }
+ }
+ }
+
+ private static class RunStatus {
+ private File mFile;
+ private int iteration;
+ private int page;
+ private String url;
+ private boolean isRecovery;
+ private boolean allClear;
+
+ private RunStatus(File file) throws IOException {
+ mFile = file;
+ FileReader input = null;
+ BufferedReader reader = null;
+ isRecovery = false;
+ allClear = false;
+ iteration = 0;
+ page = 0;
+ try {
+ input = new FileReader(mFile);
+ isRecovery = true;
+ reader = new BufferedReader(input);
+ String line = reader.readLine();
+ if (line == null)
+ return;
+ iteration = Integer.parseInt(line);
+ line = reader.readLine();
+ if (line == null)
+ return;
+ page = Integer.parseInt(line);
+ } catch (FileNotFoundException ex) {
+ return;
+ } catch (NumberFormatException nfe) {
+ Log.wtf(TAG, "unexpected data in status file, will start from begining");
+ return;
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } finally {
+ if (input != null) {
+ input.close();
+ }
+ }
+ }
+ }
+
+ public static RunStatus load() throws IOException {
+ return load(sStatusFile);
+ }
+
+ public static RunStatus load(String file) throws IOException {
+ return new RunStatus(new File(sExternalStorage, file));
+ }
+
+ public void write() throws IOException {
+ FileWriter output = null;
+ if (mFile.exists()) {
+ mFile.delete();
+ }
+ try {
+ output = new FileWriter(mFile);
+ output.write(iteration + newLine);
+ output.write(page + newLine);
+ output.write(url + newLine);
+ } finally {
+ if (output != null) {
+ output.close();
+ }
+ }
+ }
+
+ public void cleanUp() {
+ // only perform cleanup when allClear flag is set
+ // i.e. when the test was not interrupted by a Java crash
+ if (mFile.exists() && allClear) {
+ mFile.delete();
+ }
+ }
+
+ public void resetPage() {
+ page = 0;
+ }
+
+ public void incrementPage() {
+ ++page;
+ allClear = true;
+ }
+
+ public void incrementIteration() {
+ ++iteration;
+ }
+
+ public int getPage() {
+ return page;
+ }
+
+ public int getIteration() {
+ return iteration;
+ }
+
+ public boolean getIsRecovery() {
+ return isRecovery;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ allClear = false;
+ }
+ }
+
+ /**
+ * Loops over a list of URLs, points the browser to each one, and records the time elapsed.
+ *
+ * @param input the reader from which to get the URLs.
+ * @param writer the writer to which to output the results.
+ * @param clearCache determines whether the cache is cleared before loading each page
+ * @param loopCount the number of times to loop through the list of pages
+ * @throws IOException unable to read from input or write to writer.
+ * @throws InterruptedException the thread was interrupted waiting for the page to load.
+ */
+ void loopUrls(BufferedReader input, OutputStreamWriter writer,
+ boolean clearCache, int loopCount)
+ throws IOException, InterruptedException {
+ Tab tab = mController.getTabControl().getCurrentTab();
+ WebView webView = tab.getWebView();
+
+ List<String> pages = new LinkedList<String>();
+
+ String page;
+ while (null != (page = input.readLine())) {
+ if (!TextUtils.isEmpty(page)) {
+ pages.add(page);
+ }
+ }
+
+ Iterator<String> iterator = pages.iterator();
+ for (int i = 0; i < mStatus.getPage(); ++i) {
+ iterator.next();
+ }
+
+ if (mStatus.getIsRecovery()) {
+ Log.e(TAG, "Recovering after crash: " + iterator.next());
+ mStatus.incrementPage();
+ }
+
+ while (mStatus.getIteration() < loopCount) {
+ if (clearCache) {
+ clearCacheUiThread(webView, true);
+ }
+ while(iterator.hasNext()) {
+ page = iterator.next();
+ mStatus.setUrl(page);
+ mStatus.write();
+ Log.i(TAG, "start: " + page);
+ Uri uri = Uri.parse(page);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID,
+ getInstrumentation().getTargetContext().getPackageName());
+
+ long startTime = System.currentTimeMillis();
+ resetForNewPage();
+ mInst.runOnMainSync(new Runnable() {
+
+ public void run() {
+ mActivity.onNewIntent(intent);
+ }
+
+ });
+ waitForLoad();
+ long stopTime = System.currentTimeMillis();
+
+ String url = getUrlUiThread(webView);
+ Log.i(TAG, "finish: " + url);
+
+ if (writer != null) {
+ writer.write(page + "|" + (stopTime - startTime) + newLine);
+ writer.flush();
+ }
+
+ mStatus.incrementPage();
+ }
+ mStatus.incrementIteration();
+ mStatus.resetPage();
+ iterator = pages.iterator();
+ }
+ }
+
+ public void testLoadPerformance() throws IOException, InterruptedException {
+ setUpBrowser();
+
+ OutputStreamWriter writer = getOutputStream();
+ try {
+ BufferedReader bufferedReader = getInputStream();
+ try {
+ loopUrls(bufferedReader, writer, true, PERF_LOOPCOUNT);
+ } finally {
+ if (bufferedReader != null) {
+ bufferedReader.close();
+ }
+ }
+ } catch (FileNotFoundException fnfe) {
+ Log.e(TAG, fnfe.getMessage(), fnfe);
+ fail("Test environment not setup correctly");
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ }
+
+ public void testStability() throws IOException, InterruptedException {
+ setUpBrowser();
+
+ BufferedReader bufferedReader = getInputStream();
+ try {
+ loopUrls(bufferedReader, null, true, STABILITY_LOOPCOUNT);
+ } catch (FileNotFoundException fnfe) {
+ Log.e(TAG, fnfe.getMessage(), fnfe);
+ fail("Test environment not setup correctly");
+ } finally {
+ if (bufferedReader != null) {
+ bufferedReader.close();
+ }
+ }
+ }
+
+ private void clearCacheUiThread(final WebView webView, final boolean includeDiskFiles) {
+ Runnable runner = new Runnable() {
+
+ @Override
+ public void run() {
+ webView.clearCache(includeDiskFiles);
+ }
+ };
+ getInstrumentation().runOnMainSync(runner);
+ }
+
+ private String getUrlUiThread(final WebView webView) {
+ WebViewUrlGetter urlGetter = new WebViewUrlGetter(webView);
+ getInstrumentation().runOnMainSync(urlGetter);
+ return urlGetter.getUrl();
+ }
+
+ private class WebViewUrlGetter implements Runnable {
+
+ private WebView mWebView;
+ private String mUrl;
+
+ public WebViewUrlGetter(WebView webView) {
+ mWebView = webView;
+ }
+
+ @Override
+ public void run() {
+ mUrl = null;
+ mUrl = mWebView.getUrl();
+ }
+
+ public String getUrl() {
+ if (mUrl != null) {
+ return mUrl;
+ } else
+ throw new IllegalStateException("url has not been fetched yet");
+ }
+ }
+}
diff --git a/src/tests/src/com/android/browser/TestWebChromeClient.java b/src/tests/src/com/android/browser/TestWebChromeClient.java
new file mode 100644
index 00000000..f10452f2
--- /dev/null
+++ b/src/tests/src/com/android/browser/TestWebChromeClient.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Message;
+import android.view.View;
+import android.webkit.ConsoleMessage;
+import android.webkit.GeolocationPermissions;
+import android.webkit.ValueCallback;
+import android.webkit.WebStorage;
+
+import org.codeaurora.swe.JsPromptResult;
+import org.codeaurora.swe.JsResult;
+import org.codeaurora.swe.WebChromeClient;
+import org.codeaurora.swe.WebView;
+
+/**
+ *
+ * WebChromeClient for browser tests.
+ * Wraps around existing client so that specific methods can be overridden if needed.
+ *
+ */
+abstract class TestWebChromeClient extends WebChromeClient {
+
+ private WebChromeClient mWrappedClient;
+
+ protected TestWebChromeClient(WebChromeClient wrappedClient) {
+ mWrappedClient = wrappedClient;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ mWrappedClient.onProgressChanged(view, newProgress);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onReceivedTitle(WebView view, String title) {
+ mWrappedClient.onReceivedTitle(view, title);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onReceivedIcon(WebView view, Bitmap icon) {
+ mWrappedClient.onReceivedIcon(view, icon);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onReceivedTouchIconUrl(WebView view, String url,
+ boolean precomposed) {
+ mWrappedClient.onReceivedTouchIconUrl(view, url, precomposed);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onShowCustomView(View view, CustomViewCallback callback) {
+ mWrappedClient.onShowCustomView(view, callback);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onHideCustomView() {
+ mWrappedClient.onHideCustomView();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onCreateWindow(WebView view, boolean dialog,
+ boolean userGesture, Message resultMsg) {
+ // do not open any new pop-ups
+ resultMsg.sendToTarget();
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onRequestFocus(WebView view) {
+ mWrappedClient.onRequestFocus(view);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onCloseWindow(WebView window) {
+ mWrappedClient.onCloseWindow(window);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onJsAlert(WebView view, String url, String message,
+ JsResult result) {
+ return mWrappedClient.onJsAlert(view, url, message, result);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onJsConfirm(WebView view, String url, String message,
+ JsResult result) {
+ return mWrappedClient.onJsConfirm(view, url, message, result);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onJsPrompt(WebView view, String url, String message,
+ String defaultValue, JsPromptResult result) {
+ return mWrappedClient.onJsPrompt(view, url, message, defaultValue, result);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onJsBeforeUnload(WebView view, String url, String message,
+ JsResult result) {
+ return mWrappedClient.onJsBeforeUnload(view, url, message, result);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onExceededDatabaseQuota(String url, String databaseIdentifier,
+ long currentQuota, long estimatedSize, long totalUsedQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ mWrappedClient.onExceededDatabaseQuota(url, databaseIdentifier, currentQuota,
+ estimatedSize, totalUsedQuota, quotaUpdater);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ mWrappedClient.onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota, quotaUpdater);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onGeolocationPermissionsShowPrompt(String origin,
+ GeolocationPermissions.Callback callback) {
+ mWrappedClient.onGeolocationPermissionsShowPrompt(origin, callback);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onGeolocationPermissionsHidePrompt() {
+ mWrappedClient.onGeolocationPermissionsHidePrompt();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onJsTimeout() {
+ return mWrappedClient.onJsTimeout();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Deprecated
+ public void onConsoleMessage(String message, int lineNumber, String sourceID) {
+ mWrappedClient.onConsoleMessage(message, lineNumber, sourceID);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+ return mWrappedClient.onConsoleMessage(consoleMessage);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Bitmap getDefaultVideoPoster() {
+ return mWrappedClient.getDefaultVideoPoster();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View getVideoLoadingProgressView() {
+ return mWrappedClient.getVideoLoadingProgressView();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void getVisitedHistory(ValueCallback<String[]> callback) {
+ mWrappedClient.getVisitedHistory(callback);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
+ mWrappedClient.openFileChooser(uploadFile, acceptType, capture);
+ }
+}
diff --git a/src/tests/src/com/android/browser/TestWebViewClient.java b/src/tests/src/com/android/browser/TestWebViewClient.java
new file mode 100644
index 00000000..c092c96e
--- /dev/null
+++ b/src/tests/src/com/android/browser/TestWebViewClient.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.graphics.Bitmap;
+import android.net.http.SslError;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.webkit.WebViewClientClassicExt;
+
+import org.codeaurora.swe.HttpAuthHandler;
+import org.codeaurora.swe.SslErrorHandler;
+import org.codeaurora.swe.WebView;
+import org.codeaurora.swe.WebViewClient;
+/**
+ *
+ *
+ * WebViewClient for browser tests.
+ * Wraps around existing client so that specific methods can be overridden if needed.
+ *
+ */
+abstract class TestWebViewClient extends WebViewClientClassicExt {
+
+ private WebViewClient mWrappedClient;
+
+ protected TestWebViewClient(WebViewClient wrappedClient) {
+ mWrappedClient = wrappedClient;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ return mWrappedClient.shouldOverrideUrlLoading(view, url);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ mWrappedClient.onPageStarted(view, url, favicon);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ mWrappedClient.onPageFinished(view, url);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onLoadResource(WebView view, String url) {
+ mWrappedClient.onLoadResource(view, url);
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ @Override
+ public void onTooManyRedirects(WebView view, Message cancelMsg,
+ Message continueMsg) {
+ mWrappedClient.onTooManyRedirects(view, cancelMsg, continueMsg);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onReceivedError(WebView view, int errorCode,
+ String description, String failingUrl) {
+ mWrappedClient.onReceivedError(view, errorCode, description, failingUrl);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onFormResubmission(WebView view, Message dontResend,
+ Message resend) {
+ mWrappedClient.onFormResubmission(view, dontResend, resend);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void doUpdateVisitedHistory(WebView view, String url,
+ boolean isReload) {
+ mWrappedClient.doUpdateVisitedHistory(view, url, isReload);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler,
+ SslError error) {
+ mWrappedClient.onReceivedSslError(view, handler, error);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view,
+ HttpAuthHandler handler, String host, String realm) {
+ mWrappedClient.onReceivedHttpAuthRequest(view, handler, host, realm);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
+ return mWrappedClient.shouldOverrideKeyEvent(view, event);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
+ mWrappedClient.onUnhandledKeyEvent(view, event);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onScaleChanged(WebView view, float oldScale, float newScale) {
+ mWrappedClient.onScaleChanged(view, oldScale, newScale);
+ }
+}
diff --git a/src/tests/src/com/android/browser/WebStorageSizeManagerUnitTests.java b/src/tests/src/com/android/browser/WebStorageSizeManagerUnitTests.java
new file mode 100644
index 00000000..2beedf8b
--- /dev/null
+++ b/src/tests/src/com/android/browser/WebStorageSizeManagerUnitTests.java
@@ -0,0 +1,302 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+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.
+ *
+ */
+@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 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(getContext(), mDiskInfo,
+ mAppCacheInfo);
+ // We add origin 1.
+ long origin1Quota = 0;
+ long origin1EstimatedSize = bytes(3.5);
+ manager.onExceededDatabaseQuota("1", "1", origin1Quota, origin1EstimatedSize, totalUsedQuota, mQuotaUpdater);
+ assertEquals(origin1EstimatedSize, mNewQuota);
+ origin1Quota = mNewQuota;
+ totalUsedQuota += origin1Quota;
+
+ // We add origin 2.
+ long origin2Quota = 0;
+ long origin2EstimatedSize = bytes(2.5);
+ manager.onExceededDatabaseQuota("2", "2", origin2Quota, origin2EstimatedSize, totalUsedQuota, mQuotaUpdater);
+ assertEquals(origin2EstimatedSize, mNewQuota);
+ origin2Quota = mNewQuota;
+ totalUsedQuota += origin2Quota;
+
+ // Origin 1 runs out of space.
+ manager.onExceededDatabaseQuota("1", "1", origin1Quota, 0, totalUsedQuota, mQuotaUpdater);
+ assertEquals(origin1EstimatedSize + quotaIncrease, mNewQuota);
+ totalUsedQuota -= origin1Quota;
+ origin1Quota = mNewQuota;
+ totalUsedQuota += origin1Quota;
+
+ // Origin 2 runs out of space.
+ manager.onExceededDatabaseQuota("2", "2", origin2Quota, 0, totalUsedQuota, mQuotaUpdater);
+ assertEquals(origin2EstimatedSize + quotaIncrease, mNewQuota);
+ totalUsedQuota -= origin2Quota;
+ origin2Quota = mNewQuota;
+ totalUsedQuota += origin2Quota;
+
+ // We add origin 3. TotalUsedQuota is 8 (3.5 + 2.5 + 1 + 1). AppCacheMaxSize is 3 (12 / 4).
+ // So we have 1 MB free.
+ long origin3Quota = 0;
+ long origin3EstimatedSize = bytes(5);
+ manager.onExceededDatabaseQuota("3", "3", origin3Quota, origin3EstimatedSize, totalUsedQuota, mQuotaUpdater);
+ assertEquals(0, mNewQuota); // We cannot satisfy the estimatedSize
+ origin3Quota = mNewQuota;
+ totalUsedQuota += origin3Quota;
+
+ // Origin 1 runs out of space again. It should increase it's quota to take the last 1MB.
+ manager.onExceededDatabaseQuota("1", "1", origin1Quota, 0, totalUsedQuota, mQuotaUpdater);
+ assertEquals(origin1Quota + quotaIncrease, mNewQuota);
+ totalUsedQuota -= origin1Quota;
+ origin1Quota = mNewQuota;
+ totalUsedQuota += origin1Quota;
+
+ // Origin 1 runs out of space again. It should inow fail to increase in size.
+ manager.onExceededDatabaseQuota("1", "1", origin1Quota, 0, totalUsedQuota, mQuotaUpdater);
+ assertEquals(origin1Quota, mNewQuota);
+
+ // We try adding a new origin. Which will fail.
+ manager.onExceededDatabaseQuota("4", "4", 0, bytes(1), 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.5 (9 - 3.5). 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 - WebStorageSizeManager.APPCACHE_MAXSIZE_PADDING);
+
+ // We try adding a new origin. This time we succeed.
+ // TotalUsedQuota is 5.5. AppCacheMaxSize is 5.0. So we have 12 - 10.5 = 1.5 available.
+ long origin4Quota = 0;
+ long origin4EstimatedSize = bytes(1.5);
+ manager.onExceededDatabaseQuota("4", "4", origin4Quota, origin4EstimatedSize, totalUsedQuota, mQuotaUpdater);
+ assertEquals(bytes(1.5), mNewQuota);
+ origin4Quota = mNewQuota;
+ totalUsedQuota += origin4Quota;
+ }
+ /**
+ * Test the application caches max size calculator.
+ */
+ public void testCalculateGlobalLimit() {
+ long fileSystemSize = 78643200; // 75 MB
+ long freeSpaceSize = 25165824; // 24 MB
+ long maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(12582912, maxSize); // 12MB
+
+ fileSystemSize = 78643200; // 75 MB
+ freeSpaceSize = 60 * 1024 * 1024; // 60MB
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(19922944, maxSize); // 19MB
+
+ fileSystemSize = 8589934592L; // 8 GB
+ freeSpaceSize = 4294967296L; // 4 GB
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(536870912L, maxSize); // 512 MB
+
+ fileSystemSize = -14;
+ freeSpaceSize = 21;
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(0, maxSize);
+
+ fileSystemSize = 100;
+ freeSpaceSize = 101;
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(0, maxSize);
+
+ fileSystemSize = 3774873; // ~4.2 MB
+ freeSpaceSize = 2560000; // ~2.4 MB
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(2097152, maxSize); // 2 MB
+
+ fileSystemSize = 4404019; // ~4.2 MB
+ freeSpaceSize = 3774873; // ~3.6 MB
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(2097152, maxSize); // 2 MB
+
+ fileSystemSize = 4404019; // ~4.2 MB
+ freeSpaceSize = 4404019; // ~4.2 MB
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(3145728, maxSize); // 3 MB
+
+ fileSystemSize = 1048576; // 1 MB
+ freeSpaceSize = 1048575; // 1 MB - 1 byte
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(0, maxSize);
+
+ fileSystemSize = 3774873; // ~3.6 MB
+ freeSpaceSize = 2097151; // 2 MB - 1 byte
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(0, maxSize);
+
+ fileSystemSize = 3774873; // ~3.6 MB
+ freeSpaceSize = 2097151; // 2 MB
+ maxSize = WebStorageSizeManager.calculateGlobalLimit(fileSystemSize, freeSpaceSize);
+ assertEquals(0, maxSize);
+ }
+
+ public void testManyDatabasesOnOneOrigin() {
+ // This test ensures that if an origin creates more than one database, the quota that is
+ // assigned to the origin after the second creation is enough to satisfy all databases
+ // under that origin.
+ // See b/2417477.
+
+ long totalUsedQuota = 0;
+ mDiskInfo.setTotalSizeBytes(bytes(100));
+ mDiskInfo.setFreeSpaceSizeBytes(bytes(100));
+ // This should give us a storage area of 13MB, with 3.25MB for appcache and 9.75MB for
+ // databases.
+ assertEquals(bytes(13), WebStorageSizeManager.calculateGlobalLimit(
+ mDiskInfo.getTotalSizeBytes(), mDiskInfo.getFreeSpaceSizeBytes()));
+
+ // We have an appcache file size of 0 MB.
+ mAppCacheInfo.setAppCacheSizeBytes(0);
+
+ // Create the manager.
+ WebStorageSizeManager manager = new WebStorageSizeManager(getContext(), mDiskInfo,
+ mAppCacheInfo);
+
+ // We add an origin.
+ long originQuota = 0;
+ long database1EstimatedSize = bytes(2);
+ manager.onExceededDatabaseQuota("1", "1", originQuota, database1EstimatedSize,
+ totalUsedQuota, mQuotaUpdater);
+ assertEquals(database1EstimatedSize, mNewQuota);
+ originQuota = mNewQuota;
+ totalUsedQuota = originQuota;
+
+ // Now try to create a new database under the origin, by invoking onExceededDatabaseQuota
+ // again. This time, request more space than the old quota + the quota increase step.
+ long database2EstimatedSize = bytes(3.5);
+ manager.onExceededDatabaseQuota("1", "2", originQuota, database2EstimatedSize,
+ totalUsedQuota, mQuotaUpdater);
+ assertEquals(database1EstimatedSize + database2EstimatedSize, mNewQuota);
+ originQuota = mNewQuota;
+ totalUsedQuota = originQuota;
+
+ // Create another database, but this time use a size that will overflow the space on the
+ // device. It should be denied.
+ long database3EstimatedSize = bytes(50);
+ manager.onExceededDatabaseQuota("1", "3", originQuota, database3EstimatedSize,
+ totalUsedQuota, mQuotaUpdater);
+ assertEquals(originQuota, mNewQuota);
+
+ // Create another database. This time, request less than the old quota.
+ long database4EstimatedSize = bytes(2);
+ manager.onExceededDatabaseQuota("1", "4", originQuota, database4EstimatedSize,
+ totalUsedQuota, mQuotaUpdater);
+ assertEquals(database1EstimatedSize + database2EstimatedSize + database4EstimatedSize,
+ mNewQuota);
+ originQuota = mNewQuota;
+ totalUsedQuota = originQuota;
+
+ // Now have the first database overflow it's quota. We should get 1 more MB.
+ manager.onExceededDatabaseQuota("1", "1", originQuota, 0, totalUsedQuota, mQuotaUpdater);
+ assertEquals(database1EstimatedSize + database2EstimatedSize + database4EstimatedSize +
+ bytes(1), mNewQuota);
+ originQuota = mNewQuota;
+ totalUsedQuota = originQuota;
+
+ // Create a db under the origin that uses a quota less than the usual quota increase step.
+ long database5EstimatedSize = bytes(0.5);
+ manager.onExceededDatabaseQuota("1", "5", originQuota, database5EstimatedSize,
+ totalUsedQuota, mQuotaUpdater);
+ assertEquals(database1EstimatedSize + database2EstimatedSize + database4EstimatedSize +
+ bytes(1) + database5EstimatedSize, mNewQuota);
+ }
+}
diff --git a/src/tests/src/com/android/browser/tests/BP1to2UpgradeTests.java b/src/tests/src/com/android/browser/tests/BP1to2UpgradeTests.java
new file mode 100644
index 00000000..ea5cfaa5
--- /dev/null
+++ b/src/tests/src/com/android/browser/tests/BP1to2UpgradeTests.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser.tests;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.net.Uri;
+import android.provider.Browser;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.browser.platformsupport.BookmarkColumns;
+import com.android.browser.platformsupport.BrowserContract;
+import com.android.browser.platformsupport.BrowserContract.Bookmarks;
+import com.android.browser.platformsupport.BrowserContract.History;
+import com.android.browser.platformsupport.BrowserContract.Images;
+import com.android.browser.provider.BrowserProvider;
+import com.android.browser.tests.utils.BP2TestCaseHelper;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+
+@SmallTest
+public class BP1to2UpgradeTests extends BP2TestCaseHelper {
+
+ BrowserProvider mBp1;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mBp1 = new BrowserProvider();
+ mBp1.attachInfo(getMockContext(), null);
+ }
+
+ /**
+ * Test that simply makes sure BP1->BP2 with no changes works as intended
+ */
+ public void testStockUpgrade() {
+ Cursor c = mBp1.query(Browser.BOOKMARKS_URI,
+ new String[] { BookmarkColumns.URL }, null, null,
+ BookmarkColumns.URL + " DESC");
+ ArrayList<String> urls = new ArrayList<String>(c.getCount());
+ while (c.moveToNext()) {
+ urls.add(c.getString(0));
+ }
+ c.close();
+ // First, test the public API (which will hit BP2)
+ c = getMockContentResolver().query(Browser.BOOKMARKS_URI,
+ new String[] { BookmarkColumns.URL }, null, null,
+ BookmarkColumns.URL + " DESC");
+ assertEquals(urls.size(), c.getCount());
+ int i = 0;
+ while (c.moveToNext()) {
+ assertEquals(urls.get(i++), c.getString(0));
+ }
+ c.close();
+ // Next, test BP2's new API (not a public API)
+ c = getMockContentResolver().query(Bookmarks.CONTENT_URI,
+ new String[] { Bookmarks.URL }, null, null,
+ Bookmarks.URL + " DESC");
+ assertEquals(urls.size(), c.getCount());
+ i = 0;
+ while (c.moveToNext()) {
+ assertEquals(urls.get(i++), c.getString(0));
+ }
+ c.close();
+ }
+
+ public void testPreserveHistory() {
+ ContentValues values = new ContentValues();
+ values.put(BookmarkColumns.URL, "http://slashdot.org/");
+ values.put(BookmarkColumns.BOOKMARK, 0);
+ values.put(BookmarkColumns.DATE, 123456);
+ mBp1.insert(Browser.BOOKMARKS_URI, values);
+ // First, test internal API
+ Cursor c = getMockContentResolver().query(History.CONTENT_URI,
+ new String[] { History.URL, History.DATE_LAST_VISITED },
+ null, null, null);
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals("http://slashdot.org/", c.getString(0));
+ assertEquals(123456, c.getInt(1));
+ c.close();
+ // Next, test public API
+ c = getMockContentResolver().query(Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION, BookmarkColumns.BOOKMARK + " = 0",
+ null, null);
+ assertEquals("public API", 1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals("http://slashdot.org/",
+ c.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
+ assertEquals(123456, c.getInt(Browser.HISTORY_PROJECTION_DATE_INDEX));
+ c.close();
+ }
+
+ public void testPreserveBookmarks() {
+ // First, nuke 'er (deletes stock bookmarks)
+ mBp1.delete(Browser.BOOKMARKS_URI, null, null);
+ ContentValues values = new ContentValues();
+ values.put(BookmarkColumns.URL, "http://slashdot.org/");
+ values.put(BookmarkColumns.BOOKMARK, 1);
+ values.put(BookmarkColumns.CREATED, 123456);
+ mBp1.insert(Browser.BOOKMARKS_URI, values);
+ // First, test internal API
+ Cursor c = getMockContentResolver().query(Bookmarks.CONTENT_URI,
+ new String[] { Bookmarks.URL, Bookmarks.DATE_CREATED },
+ null, null, null);
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals("http://slashdot.org/", c.getString(0));
+ assertEquals(123456, c.getInt(1));
+ c.close();
+ // Next, test public API
+ c = getMockContentResolver().query(Browser.BOOKMARKS_URI,
+ new String[] { BookmarkColumns.URL, BookmarkColumns.CREATED },
+ BookmarkColumns.BOOKMARK + " = 1", null, null);
+ assertEquals("public API", 1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals("http://slashdot.org/", c.getString(0));
+ assertEquals(123456, c.getInt(1));
+ c.close();
+ }
+
+ public void testEmptyUpgrade() {
+ mBp1.delete(Browser.BOOKMARKS_URI, null, null);
+ Cursor c = getMockContentResolver().query(Bookmarks.CONTENT_URI,
+ null, null, null, null);
+ assertEquals(0, c.getCount());
+ c.close();
+ }
+
+}
diff --git a/src/tests/src/com/android/browser/tests/BP2ProviderTests.java b/src/tests/src/com/android/browser/tests/BP2ProviderTests.java
new file mode 100644
index 00000000..23b75d0e
--- /dev/null
+++ b/src/tests/src/com/android/browser/tests/BP2ProviderTests.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser.tests;
+
+import com.android.browser.tests.utils.BP2TestCaseHelper;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.net.Uri;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.browser.platformsupport.BrowserContract;
+import com.android.browser.platformsupport.BrowserContract.Images;
+
+import java.io.ByteArrayOutputStream;
+
+@SmallTest
+public class BP2ProviderTests extends BP2TestCaseHelper {
+
+ static final String[] PROJECTION = new String[] {
+ BrowserContract.Bookmarks.PARENT,
+ BrowserContract.Bookmarks.ACCOUNT_NAME,
+ BrowserContract.Bookmarks.ACCOUNT_TYPE,
+ };
+ static final int INDEX_PARENT = 0;
+ static final int INDEX_ACCOUNT_NAME = 1;
+ static final int INDEX_ACCOUNT_TYPE = 2;
+
+ public void testUpdateImage() {
+ String url = "http://stub1.com";
+ insertBookmark(url, "stub 1");
+ ContentValues values = new ContentValues();
+ values.put(Images.URL, url);
+ Bitmap bitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
+ values.put(Images.THUMBNAIL, os.toByteArray());
+ // Use updateBookmarks because the bookmarks URI observer should
+ // be triggered, even though we aren't giving it a bookmarks URI
+ assertTrue(updateBookmark(Images.CONTENT_URI, values));
+ }
+
+ public void testIsValidParentNullAccount() {
+ doTestIsValidParent(null, null);
+ }
+
+ public void testIsValidParentWithAccount() {
+ doTestIsValidParent("test@gmail.com", "com.google");
+ }
+
+ private void doTestIsValidParent(String accountName, String accountType) {
+ // Create the folder
+ ContentValues values = new ContentValues();
+ values.put(BrowserContract.Bookmarks.TITLE, "New Folder");
+ values.put(BrowserContract.Bookmarks.IS_FOLDER, 1);
+ values.put(BrowserContract.Bookmarks.ACCOUNT_NAME, accountName);
+ values.put(BrowserContract.Bookmarks.ACCOUNT_TYPE, accountType);
+ Uri folderUri = insertBookmark(values);
+ assertNotNull(folderUri);
+ long folderId = ContentUris.parseId(folderUri);
+ assertTrue("Failed to parse folder id!", folderId > 0);
+ // Insert a bookmark with the same ACCOUNT_* info as parent
+ values.put(BrowserContract.Bookmarks.TITLE, "google");
+ values.put(BrowserContract.Bookmarks.URL, "http://google.com");
+ values.put(BrowserContract.Bookmarks.IS_FOLDER, 0);
+ values.put(BrowserContract.Bookmarks.PARENT, folderId);
+ Uri insertedUri = insertBookmark(values);
+ assertNotNull(insertedUri);
+ Cursor c = getMockContentResolver().query(insertedUri,
+ PROJECTION, null, null, null);
+ try {
+ assertNotNull(c);
+ assertTrue(c.moveToFirst());
+ long insertedParentId = c.getLong(INDEX_PARENT);
+ String insertedAccountName = c.getString(INDEX_ACCOUNT_NAME);
+ String insertedAccountType = c.getString(INDEX_ACCOUNT_TYPE);
+ assertEquals(folderId, insertedParentId);
+ assertEquals(accountName, insertedAccountName);
+ assertEquals(accountType, insertedAccountType);
+
+ // Insert a bookmark with no ACCOUNT_* set, BUT with a valid parent
+ // The inserted should end up with the ACCOUNT_* of the parent
+ values.remove(BrowserContract.Bookmarks.ACCOUNT_NAME);
+ values.remove(BrowserContract.Bookmarks.ACCOUNT_TYPE);
+ insertedUri = insertBookmark(values);
+ assertNotNull(insertedUri);
+ c.close();
+ c = getMockContentResolver().query(insertedUri,
+ PROJECTION, null, null, null);
+ assertNotNull(c);
+ assertTrue(c.moveToFirst());
+ insertedParentId = c.getLong(INDEX_PARENT);
+ insertedAccountName = c.getString(INDEX_ACCOUNT_NAME);
+ insertedAccountType = c.getString(INDEX_ACCOUNT_TYPE);
+ assertEquals(folderId, insertedParentId);
+ assertEquals(accountName, insertedAccountName);
+ assertEquals(accountType, insertedAccountType);
+
+ // Insert a bookmark with a different ACCOUNT_* than it's parent
+ // ACCOUNT_* should override parent
+ accountName = accountName + "@something.else";
+ accountType = "com.google";
+ values.put(BrowserContract.Bookmarks.ACCOUNT_NAME, accountName);
+ values.put(BrowserContract.Bookmarks.ACCOUNT_TYPE, accountType);
+ insertedUri = insertBookmark(values);
+ assertNotNull(insertedUri);
+ c.close();
+ c = getMockContentResolver().query(insertedUri,
+ PROJECTION, null, null, null);
+ assertNotNull(c);
+ assertTrue(c.moveToFirst());
+ insertedParentId = c.getLong(INDEX_PARENT);
+ insertedAccountName = c.getString(INDEX_ACCOUNT_NAME);
+ insertedAccountType = c.getString(INDEX_ACCOUNT_TYPE);
+ assertNotSame(folderId, insertedParentId);
+ assertEquals(accountName, insertedAccountName);
+ assertEquals(accountType, insertedAccountType);
+ } finally {
+ c.close();
+ }
+ }
+}
diff --git a/src/tests/src/com/android/browser/tests/BP2UriObserverTests.java b/src/tests/src/com/android/browser/tests/BP2UriObserverTests.java
new file mode 100644
index 00000000..a3b001aa
--- /dev/null
+++ b/src/tests/src/com/android/browser/tests/BP2UriObserverTests.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser.tests;
+
+import android.content.ContentValues;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.net.Uri;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.browser.platformsupport.BrowserContract.Bookmarks;
+import com.android.browser.platformsupport.BrowserContract.History;
+import com.android.browser.tests.utils.BP2TestCaseHelper;
+
+import java.io.ByteArrayOutputStream;
+
+@SmallTest
+public class BP2UriObserverTests extends BP2TestCaseHelper {
+
+ public void testInsertBookmark() {
+ Uri insertedUri = insertBookmark("http://stub1.com", "Stub1");
+ TriggeredObserver stubObs = new TriggeredObserver(insertedUri);
+ assertObserversTriggered(false, stubObs);
+ insertBookmark("http://stub2.com", "Stub2");
+ perfIdeallyUntriggered(stubObs);
+ }
+
+ public void testUpdateBookmark() {
+ Uri toUpdate = insertBookmark("http://stub1.com", "Stub1");
+ Uri unchanged = insertBookmark("http://stub2.com", "Stub2");
+ TriggeredObserver updateObs = new TriggeredObserver(toUpdate);
+ TriggeredObserver unchangedObs = new TriggeredObserver(unchanged);
+ assertObserversTriggered(false, updateObs, unchangedObs);
+ assertTrue(updateBookmark(toUpdate, "http://stub1.com", "Stub1: Revenge of the stubs"));
+ assertTrue("Update observer not notified!", updateObs.checkTriggered());
+ perfIdeallyUntriggered(unchangedObs);
+ }
+
+ public void testUpdateBookmarkImages() {
+ Uri toUpdate = insertBookmark("http://stub1.com", "Stub1");
+ Uri unchanged = insertBookmark("http://stub2.com", "Stub2");
+ Bitmap favicon = Bitmap.createBitmap(16, 16, Config.ARGB_8888);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ favicon.compress(Bitmap.CompressFormat.PNG, 100, os);
+ byte[] rawFavicon = os.toByteArray();
+ ContentValues values = new ContentValues();
+ values.put(Bookmarks.FAVICON, rawFavicon);
+ values.put(Bookmarks.TITLE, "Stub1");
+ TriggeredObserver updateObs = new TriggeredObserver(toUpdate);
+ TriggeredObserver unchangedObs = new TriggeredObserver(unchanged);
+ assertTrue(updateBookmark(toUpdate, values));
+ assertTrue("Update observer not notified!", updateObs.checkTriggered());
+ perfIdeallyUntriggered(unchangedObs);
+ }
+
+ public void testInsertHistory() {
+ Uri insertedUri = insertHistory("http://stub1.com", "Stub1");
+ TriggeredObserver stubObs = new TriggeredObserver(insertedUri);
+ assertObserversTriggered(false, stubObs);
+ insertHistory("http://stub2.com", "Stub2");
+ perfIdeallyUntriggered(stubObs);
+ }
+
+ public void testUpdateHistory() {
+ Uri toUpdate = insertHistory("http://stub1.com", "Stub1");
+ Uri unchanged = insertHistory("http://stub2.com", "Stub2");
+ TriggeredObserver updateObs = new TriggeredObserver(toUpdate);
+ TriggeredObserver unchangedObs = new TriggeredObserver(unchanged);
+ assertObserversTriggered(false, updateObs, unchangedObs);
+ assertTrue(updateHistory(toUpdate, "http://stub1.com", "Stub1: Revenge of the stubs"));
+ assertTrue("Update observer not notified!", updateObs.checkTriggered());
+ perfIdeallyUntriggered(unchangedObs);
+ }
+
+ public void testUpdateHistoryImages() {
+ Uri toUpdate = insertHistory("http://stub1.com", "Stub1");
+ Uri unchanged = insertHistory("http://stub2.com", "Stub2");
+ Bitmap favicon = Bitmap.createBitmap(16, 16, Config.ARGB_8888);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ favicon.compress(Bitmap.CompressFormat.PNG, 100, os);
+ byte[] rawFavicon = os.toByteArray();
+ ContentValues values = new ContentValues();
+ values.put(History.FAVICON, rawFavicon);
+ values.put(History.TITLE, "Stub1");
+ TriggeredObserver updateObs = new TriggeredObserver(toUpdate);
+ TriggeredObserver unchangedObs = new TriggeredObserver(unchanged);
+ assertTrue(updateHistory(toUpdate, values));
+ assertTrue("Update observer not notified!", updateObs.checkTriggered());
+ perfIdeallyUntriggered(unchangedObs);
+ }
+}
diff --git a/src/tests/src/com/android/browser/tests/BookmarksTests.java b/src/tests/src/com/android/browser/tests/BookmarksTests.java
new file mode 100644
index 00000000..f4b91093
--- /dev/null
+++ b/src/tests/src/com/android/browser/tests/BookmarksTests.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser.tests;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.browser.Bookmarks;
+import com.android.browser.tests.utils.BP2TestCaseHelper;
+
+/**
+ * Extends from BP2TestCaseHelper for the helper methods
+ * and to get the mock database
+ */
+@SmallTest
+public class BookmarksTests extends BP2TestCaseHelper {
+
+ public void testQueryCombinedForUrl() {
+ // First, add some bookmarks
+ assertNotNull(insertBookmark(
+ "http://google.com/search?q=test", "Test search"));
+ assertNotNull(insertBookmark(
+ "http://google.com/search?q=mustang", "Mustang search"));
+ assertNotNull(insertBookmark(
+ "http://google.com/search?q=aliens", "Aliens search"));
+ ContentResolver cr = getMockContentResolver();
+
+ Cursor c = null;
+ try {
+ // First, search for a match
+ String url = "http://google.com/search?q=test";
+ c = Bookmarks.queryCombinedForUrl(cr, null, url);
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals(url, c.getString(0));
+ c.close();
+
+ // Next, search for no match
+ url = "http://google.com/search";
+ c = Bookmarks.queryCombinedForUrl(cr, null, url);
+ assertEquals(0, c.getCount());
+ assertFalse(c.moveToFirst());
+ c.close();
+ } finally {
+ if (c != null) c.close();
+ }
+ }
+
+}
diff --git a/src/tests/src/com/android/browser/tests/utils/BP2TestCaseHelper.java b/src/tests/src/com/android/browser/tests/utils/BP2TestCaseHelper.java
new file mode 100644
index 00000000..9133a872
--- /dev/null
+++ b/src/tests/src/com/android/browser/tests/utils/BP2TestCaseHelper.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser.tests.utils;
+
+import com.android.browser.provider.BrowserProvider2;
+
+import java.io.File;
+import java.io.FilenameFilter;
+
+import android.content.ContentValues;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Browser;
+import android.util.Log;
+
+import com.android.browser.platformsupport.BrowserContract;
+import com.android.browser.platformsupport.BrowserContract.Bookmarks;
+import com.android.browser.platformsupport.BrowserContract.History;
+
+/**
+ * This is a replacement for ProviderTestCase2 that can handle notifyChange testing.
+ * It also has helper methods specifically for testing BrowserProvider2
+ */
+public abstract class BP2TestCaseHelper extends ProviderTestCase3<BrowserProvider2> {
+
+ // Tag for potential performance impacts
+ private static final String PERFTAG = "BP2-PerfCheck";
+
+ private TriggeredObserver mLegacyObserver;
+ private TriggeredObserver mRootObserver;
+ private TriggeredObserver mBookmarksObserver;
+ private TriggeredObserver mHistoryObserver;
+ private TriggeredObserver mWidgetObserver;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mLegacyObserver = new TriggeredObserver(Browser.BOOKMARKS_URI);
+ mRootObserver = new TriggeredObserver(BrowserContract.AUTHORITY_URI);
+ mBookmarksObserver = new TriggeredObserver(Bookmarks.CONTENT_URI);
+ mHistoryObserver = new TriggeredObserver(History.CONTENT_URI);
+ mWidgetObserver = new TriggeredObserver();
+ // We don't need to worry about setting this back to null since this
+ // is a private instance local to the MockContentResolver
+ getProvider().setWidgetObserver(mWidgetObserver);
+ }
+
+ public BP2TestCaseHelper() {
+ super(BrowserProvider2.class,
+ BrowserContract.AUTHORITY, BrowserProvider2.LEGACY_AUTHORITY);
+ }
+
+ public void perfIdeallyUntriggered(TriggeredObserver... obs) {
+ for (TriggeredObserver ob : obs) {
+ if (ob.checkTriggered()) {
+ // Not ideal, unnecessary notification
+ Log.i(PERFTAG, ob.mUri + " onChange called but content unaltered!");
+ }
+ }
+ }
+
+ public void assertObserversTriggered(boolean triggered,
+ TriggeredObserver... observers) {
+ for (TriggeredObserver obs : observers) {
+ assertEquals(obs.mUri + ", descendents:" + obs.mNotifyForDescendents,
+ triggered, obs.checkTriggered());
+ }
+ }
+
+ public class TriggeredObserver extends ContentObserver {
+ private boolean mTriggered;
+ Uri mUri;
+ boolean mNotifyForDescendents;
+
+ /**
+ * Creates an unmanaged TriggeredObserver
+ */
+ public TriggeredObserver() {
+ super(null);
+ }
+
+ /**
+ * Same as TriggeredObserver(uri, true);
+ */
+ public TriggeredObserver(Uri uri) {
+ this(uri, true);
+ }
+
+ /**
+ * Creates a managed TriggeredObserver that self-registers with the
+ * mock ContentResolver
+ */
+ public TriggeredObserver(Uri uri, boolean notifyForDescendents) {
+ super(null);
+ mUri = uri;
+ mNotifyForDescendents = notifyForDescendents;
+ registerContentObserver(uri, notifyForDescendents, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ mTriggered = true;
+ }
+
+ public boolean checkTriggered() {
+ boolean ret = mTriggered;
+ mTriggered = false;
+ return ret;
+ }
+ }
+
+ Uri mockInsert(Uri uri, ContentValues values) {
+ assertObserversTriggered(false, mLegacyObserver, mRootObserver);
+ Uri ret = getMockContentResolver().insert(uri, values);
+ assertObserversTriggered(true, mLegacyObserver, mRootObserver);
+ return ret;
+ }
+
+ int mockUpdate(Uri uri, ContentValues values, String where,
+ String[] selectionArgs) {
+ assertObserversTriggered(false, mLegacyObserver, mRootObserver);
+ int ret = getMockContentResolver().update(uri, values, where, selectionArgs);
+ if (ret > 0) {
+ assertObserversTriggered(true, mLegacyObserver, mRootObserver);
+ } else {
+ perfIdeallyUntriggered(mLegacyObserver);
+ perfIdeallyUntriggered(mRootObserver);
+ }
+ return ret;
+ }
+
+ public Uri insertBookmark(String url, String title) {
+ ContentValues values = new ContentValues();
+ values.put(BrowserContract.Bookmarks.TITLE, title);
+ values.put(BrowserContract.Bookmarks.URL, url);
+ values.put(BrowserContract.Bookmarks.IS_FOLDER, 0);
+ return insertBookmark(values);
+ }
+
+ public Uri insertBookmark(ContentValues values) {
+ assertObserversTriggered(false, mBookmarksObserver, mWidgetObserver);
+ Uri ret = mockInsert(Bookmarks.CONTENT_URI, values);
+ assertObserversTriggered(true, mBookmarksObserver, mWidgetObserver);
+ perfIdeallyUntriggered(mHistoryObserver);
+ return ret;
+ }
+
+ public boolean updateBookmark(Uri uri, String url, String title) {
+ ContentValues values = new ContentValues();
+ values.put(BrowserContract.Bookmarks.TITLE, title);
+ values.put(BrowserContract.Bookmarks.URL, url);
+ return updateBookmark(uri, values);
+ }
+
+ public boolean updateBookmark(Uri uri, ContentValues values) {
+ assertObserversTriggered(false, mBookmarksObserver, mWidgetObserver);
+ int modifyCount = mockUpdate(uri, values, null, null);
+ assertTrue("UpdatedBookmark modified too much! " + uri, modifyCount <= 1);
+ boolean updated = modifyCount == 1;
+ if (updated) {
+ assertObserversTriggered(updated, mBookmarksObserver, mWidgetObserver);
+ } else {
+ perfIdeallyUntriggered(mBookmarksObserver, mWidgetObserver);
+ }
+ perfIdeallyUntriggered(mHistoryObserver);
+ return updated;
+ }
+
+ public Uri insertHistory(String url, String title) {
+ ContentValues values = new ContentValues();
+ values.put(BrowserContract.History.TITLE, title);
+ values.put(BrowserContract.History.URL, url);
+ assertObserversTriggered(false, mHistoryObserver);
+ Uri ret = mockInsert(History.CONTENT_URI, values);
+ assertObserversTriggered(true, mHistoryObserver);
+ perfIdeallyUntriggered(mBookmarksObserver, mWidgetObserver);
+ return ret;
+ }
+
+ public boolean updateHistory(Uri uri, String url, String title) {
+ ContentValues values = new ContentValues();
+ values.put(BrowserContract.History.TITLE, title);
+ values.put(BrowserContract.History.URL, url);
+ return updateHistory(uri, values);
+ }
+
+ public boolean updateHistory(Uri uri, ContentValues values) {
+ assertObserversTriggered(false, mHistoryObserver);
+ int modifyCount = mockUpdate(uri, values, null, null);
+ assertTrue("UpdatedHistory modified too much! " + uri, modifyCount <= 1);
+ boolean updated = modifyCount == 1;
+ if (updated) {
+ assertObserversTriggered(updated, mHistoryObserver);
+ } else {
+ perfIdeallyUntriggered(mHistoryObserver);
+ }
+ perfIdeallyUntriggered(mBookmarksObserver, mWidgetObserver);
+ return updated;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ // Delete the test databases so that subsequent runs have a clean slate
+ File f = getMockContext().getDatabasePath("test");
+ File dir = f.getParentFile();
+ File testFiles[] = dir.listFiles(new FilenameFilter() {
+
+ @Override
+ public boolean accept(File dir, String filename) {
+ return filename.startsWith(ProviderTestCase3.FILENAME_PREFIX);
+ }
+ });
+ for (File testFile : testFiles) {
+ testFile.delete();
+ }
+ }
+}
diff --git a/src/tests/src/com/android/browser/tests/utils/MockContentResolver2.java b/src/tests/src/com/android/browser/tests/utils/MockContentResolver2.java
new file mode 100644
index 00000000..4fed65a0
--- /dev/null
+++ b/src/tests/src/com/android/browser/tests/utils/MockContentResolver2.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser.tests.utils;
+
+import com.google.android.collect.Maps;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.database.ContentObserver;
+import android.net.Uri;
+
+import java.util.Map;
+
+public class MockContentResolver2 extends ContentResolver {
+
+ Map<String, ContentProvider> mProviders;
+ private final MockObserverNode mRootNode = new MockObserverNode("");
+
+ /*
+ * Creates a local map of providers. This map is used instead of the global map when an
+ * API call tries to acquire a provider.
+ */
+ public MockContentResolver2() {
+ super(null);
+ mProviders = Maps.newHashMap();
+ }
+
+ /**
+ * Adds access to a provider based on its authority
+ *
+ * @param name The authority name associated with the provider.
+ * @param provider An instance of {@link android.content.ContentProvider} or one of its
+ * subclasses, or null.
+ */
+ public void addProvider(String name, ContentProvider provider) {
+
+ /*
+ * Maps the authority to the provider locally.
+ */
+ mProviders.put(name, provider);
+ }
+
+ /** @hide */
+ @Override
+ protected IContentProvider acquireProvider(Context context, String name) {
+ return acquireExistingProvider(context, name);
+ }
+
+ /** @hide */
+ @Override
+ protected IContentProvider acquireExistingProvider(Context context, String name) {
+
+ /*
+ * Gets the content provider from the local map
+ */
+ final ContentProvider provider = mProviders.get(name);
+
+ if (provider != null) {
+ return provider.getIContentProvider();
+ } else {
+ return null;
+ }
+ }
+
+ /** @hide */
+ @Override
+ public boolean releaseProvider(IContentProvider provider) {
+ return true;
+ }
+
+ /** @hide */
+ protected IContentProvider acquireUnstableProvider(Context c, String name) {
+ return acquireProvider(c, name);
+ }
+
+ /** @hide */
+ public boolean releaseUnstableProvider(IContentProvider icp) {
+ return releaseProvider(icp);
+ }
+
+ /** @hide */
+ public void unstableProviderDied(IContentProvider icp) {
+ }
+
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer,
+ boolean syncToNetwork) {
+ mRootNode.notifyMyObservers(uri, 0, observer, false);
+ }
+
+ public void safeRegisterContentObserver(Uri uri, boolean notifyForDescendents,
+ ContentObserver observer) {
+ mRootNode.addObserver(uri, observer, notifyForDescendents);
+ }
+
+ public void safeUnregisterContentObserver(ContentObserver observer) {
+ mRootNode.removeObserver(observer);
+ }
+
+}
diff --git a/src/tests/src/com/android/browser/tests/utils/MockObserverNode.java b/src/tests/src/com/android/browser/tests/utils/MockObserverNode.java
new file mode 100644
index 00000000..edcffd4f
--- /dev/null
+++ b/src/tests/src/com/android/browser/tests/utils/MockObserverNode.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser.tests.utils;
+
+import android.database.ContentObserver;
+import android.net.Uri;
+
+import java.util.ArrayList;
+
+public final class MockObserverNode {
+ private class MockObserverEntry {
+ public final ContentObserver observer;
+ public final boolean notifyForDescendents;
+
+ public MockObserverEntry(ContentObserver o, boolean n) {
+ observer = o;
+ notifyForDescendents = n;
+ }
+ }
+
+ public static final int INSERT_TYPE = 0;
+ public static final int UPDATE_TYPE = 1;
+ public static final int DELETE_TYPE = 2;
+
+ private String mName;
+ private ArrayList<MockObserverNode> mChildren = new ArrayList<MockObserverNode>();
+ private ArrayList<MockObserverEntry> mObservers = new ArrayList<MockObserverEntry>();
+
+ public MockObserverNode(String name) {
+ mName = name;
+ }
+
+ private String getUriSegment(Uri uri, int index) {
+ if (uri != null) {
+ if (index == 0) {
+ return uri.getAuthority();
+ } else {
+ return uri.getPathSegments().get(index - 1);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private int countUriSegments(Uri uri) {
+ if (uri == null) {
+ return 0;
+ }
+ return uri.getPathSegments().size() + 1;
+ }
+
+ public void addObserver(Uri uri, ContentObserver observer,
+ boolean notifyForDescendents) {
+ addObserver(uri, 0, observer, notifyForDescendents);
+ }
+
+ private void addObserver(Uri uri, int index, ContentObserver observer,
+ boolean notifyForDescendents) {
+ // If this is the leaf node add the observer
+ if (index == countUriSegments(uri)) {
+ mObservers.add(new MockObserverEntry(observer, notifyForDescendents));
+ return;
+ }
+
+ // Look to see if the proper child already exists
+ String segment = getUriSegment(uri, index);
+ if (segment == null) {
+ throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
+ }
+ int N = mChildren.size();
+ for (int i = 0; i < N; i++) {
+ MockObserverNode node = mChildren.get(i);
+ if (node.mName.equals(segment)) {
+ node.addObserver(uri, index + 1, observer, notifyForDescendents);
+ return;
+ }
+ }
+
+ // No child found, create one
+ MockObserverNode node = new MockObserverNode(segment);
+ mChildren.add(node);
+ node.addObserver(uri, index + 1, observer, notifyForDescendents);
+ }
+
+ public boolean removeObserver(ContentObserver observer) {
+ int size = mChildren.size();
+ for (int i = 0; i < size; i++) {
+ boolean empty = mChildren.get(i).removeObserver(observer);
+ if (empty) {
+ mChildren.remove(i);
+ i--;
+ size--;
+ }
+ }
+
+ size = mObservers.size();
+ for (int i = 0; i < size; i++) {
+ MockObserverEntry entry = mObservers.get(i);
+ if (entry.observer == observer) {
+ mObservers.remove(i);
+ break;
+ }
+ }
+
+ if (mChildren.size() == 0 && mObservers.size() == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ private void notifyMyObservers(boolean leaf, ContentObserver observer,
+ boolean selfNotify) {
+ int N = mObservers.size();
+ for (int i = 0; i < N; i++) {
+ MockObserverEntry entry = mObservers.get(i);
+
+ // Don't notify the observer if it sent the notification and isn't interesed
+ // in self notifications
+ if (entry.observer == observer && !selfNotify) {
+ continue;
+ }
+
+ // Make sure the observer is interested in the notification
+ if (leaf || (!leaf && entry.notifyForDescendents)) {
+ entry.observer.onChange(selfNotify);
+ }
+ }
+ }
+
+ public void notifyMyObservers(Uri uri, int index, ContentObserver observer,
+ boolean selfNotify) {
+ String segment = null;
+ int segmentCount = countUriSegments(uri);
+ if (index >= segmentCount) {
+ // This is the leaf node, notify all observers
+ notifyMyObservers(true, observer, selfNotify);
+ } else if (index < segmentCount){
+ segment = getUriSegment(uri, index);
+ // Notify any observers at this level who are interested in descendents
+ notifyMyObservers(false, observer, selfNotify);
+ }
+
+ int N = mChildren.size();
+ for (int i = 0; i < N; i++) {
+ MockObserverNode node = mChildren.get(i);
+ if (segment == null || node.mName.equals(segment)) {
+ // We found the child,
+ node.notifyMyObservers(uri, index + 1, observer, selfNotify);
+ if (segment != null) {
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/tests/src/com/android/browser/tests/utils/ProviderTestCase3.java b/src/tests/src/com/android/browser/tests/utils/ProviderTestCase3.java
new file mode 100644
index 00000000..75bc052e
--- /dev/null
+++ b/src/tests/src/com/android/browser/tests/utils/ProviderTestCase3.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser.tests.utils;
+
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.test.IsolatedContext;
+import android.test.RenamingDelegatingContext;
+import android.test.mock.MockContext;
+
+import java.io.File;
+
+/**
+ * Replacement for ProviderTestCase2 that keeps calls to ContentResolver.notifyChanged
+ * internal to observers registered with ProviderTestCase3.registerContentObserver
+ */
+public abstract class ProviderTestCase3<T extends ContentProvider> extends AndroidTestCase {
+
+ public static final String FILENAME_PREFIX = "test.";
+
+ Class<T> mProviderClass;
+ String[] mProviderAuthority;
+
+ private IsolatedContext mProviderContext;
+ private MockContentResolver2 mResolver;
+
+ private class MockContext2 extends MockContext {
+
+ @Override
+ public Resources getResources() {
+ return getContext().getResources();
+ }
+
+ @Override
+ public File getDir(String name, int mode) {
+ // name the directory so the directory will be separated from
+ // one created through the regular Context
+ return getContext().getDir("mockcontext2_" + name, mode);
+ }
+
+ @Override
+ public String getPackageName() {
+ return getContext().getPackageName();
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(String name, int mode) {
+ return getContext().getSharedPreferences("mockcontext2_" + name, mode);
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ return null;
+ }
+ }
+ /**
+ * Constructor.
+ *
+ * @param providerClass The class name of the provider under test
+ * @param providerAuthorities The provider's authority string
+ */
+ public ProviderTestCase3(Class<T> providerClass, String... providerAuthorities) {
+ mProviderClass = providerClass;
+ mProviderAuthority = providerAuthorities;
+ }
+
+ private T mProvider;
+
+ /**
+ * Returns the content provider created by this class in the {@link #setUp()} method.
+ * @return T An instance of the provider class given as a parameter to the test case class.
+ */
+ public T getProvider() {
+ return mProvider;
+ }
+
+ /**
+ * Sets up the environment for the test fixture.
+ * <p>
+ * Creates a new
+ * {@link com.android.browser.tests.utils.MockContentResolver2}, a new IsolatedContext
+ * that isolates the provider's file operations, and a new instance of
+ * the provider under test within the isolated environment.
+ * </p>
+ *
+ * @throws Exception
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mResolver = new MockContentResolver2();
+ RenamingDelegatingContext targetContextWrapper = new
+ RenamingDelegatingContext(
+ new MockContext2(), // The context that most methods are
+ //delegated to
+ getContext(), // The context that file methods are delegated to
+ FILENAME_PREFIX);
+ // The default IsolatedContext has a mock AccountManager that doesn't
+ // work for us, so override getSystemService to always return null
+ mProviderContext = new IsolatedContext(mResolver, targetContextWrapper) {
+
+ @Override
+ public Object getSystemService(String name) {
+ return null;
+ }
+ };
+
+ mProvider = mProviderClass.newInstance();
+ mProvider.attachInfo(mProviderContext, null);
+ assertNotNull(mProvider);
+ for (String auth : mProviderAuthority) {
+ mResolver.addProvider(auth, getProvider());
+ }
+ }
+
+ /**
+ * Tears down the environment for the test fixture.
+ * <p>
+ * Calls {@link android.content.ContentProvider#shutdown()} on the
+ * {@link android.content.ContentProvider} represented by mProvider.
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ mProvider.shutdown();
+ super.tearDown();
+ }
+
+ /**
+ * Gets the {@link MockContentResolver2} created by this class during initialization. You
+ * must use the methods of this resolver to access the provider under test.
+ *
+ * @return A {@link MockContentResolver2} instance.
+ */
+ public MockContentResolver2 getMockContentResolver() {
+ return mResolver;
+ }
+
+ /**
+ * Gets the {@link IsolatedContext} created by this class during initialization.
+ * @return The {@link IsolatedContext} instance
+ */
+ public IsolatedContext getMockContext() {
+ return mProviderContext;
+ }
+
+ public void registerContentObserver(Uri uri, boolean notifyForDescendents,
+ ContentObserver observer) {
+ mResolver.safeRegisterContentObserver(uri, notifyForDescendents, observer);
+ }
+
+ public void unregisterContentObserver(ContentObserver observer) {
+ mResolver.safeUnregisterContentObserver(observer);
+ }
+
+}