/*
* 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 com.android.provider.BrowserContract;
import com.android.browser.provider.BrowserContract.Bookmarks;
import com.android.browser.provider.BrowserContract.History;
import android.util.Log;
/**
* 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();
}
}
}