1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.browser.tests.utils;
18
19import com.android.browser.provider.BrowserProvider2;
20
21import java.io.File;
22import java.io.FilenameFilter;
23
24import android.content.ContentValues;
25import android.database.ContentObserver;
26import android.net.Uri;
27import android.provider.Browser;
28import android.provider.BrowserContract;
29import android.provider.BrowserContract.Bookmarks;
30import android.provider.BrowserContract.History;
31import android.util.Log;
32
33/**
34 *  This is a replacement for ProviderTestCase2 that can handle notifyChange testing.
35 *  It also has helper methods specifically for testing BrowserProvider2
36 */
37public abstract class BP2TestCaseHelper extends ProviderTestCase3<BrowserProvider2> {
38
39    // Tag for potential performance impacts
40    private static final String PERFTAG = "BP2-PerfCheck";
41
42    private TriggeredObserver mLegacyObserver;
43    private TriggeredObserver mRootObserver;
44    private TriggeredObserver mBookmarksObserver;
45    private TriggeredObserver mHistoryObserver;
46    private TriggeredObserver mWidgetObserver;
47
48    @Override
49    protected void setUp() throws Exception {
50        super.setUp();
51        mLegacyObserver = new TriggeredObserver(Browser.BOOKMARKS_URI);
52        mRootObserver = new TriggeredObserver(BrowserContract.AUTHORITY_URI);
53        mBookmarksObserver = new TriggeredObserver(Bookmarks.CONTENT_URI);
54        mHistoryObserver = new TriggeredObserver(History.CONTENT_URI);
55        mWidgetObserver = new TriggeredObserver();
56        // We don't need to worry about setting this back to null since this
57        // is a private instance local to the MockContentResolver
58        getProvider().setWidgetObserver(mWidgetObserver);
59    }
60
61    public BP2TestCaseHelper() {
62        super(BrowserProvider2.class,
63                BrowserContract.AUTHORITY, BrowserProvider2.LEGACY_AUTHORITY);
64    }
65
66    public void perfIdeallyUntriggered(TriggeredObserver... obs) {
67        for (TriggeredObserver ob : obs) {
68            if (ob.checkTriggered()) {
69                // Not ideal, unnecessary notification
70                Log.i(PERFTAG, ob.mUri + " onChange called but content unaltered!");
71            }
72        }
73    }
74
75    public void assertObserversTriggered(boolean triggered,
76            TriggeredObserver... observers) {
77        for (TriggeredObserver obs : observers) {
78            assertEquals(obs.mUri + ", descendents:" + obs.mNotifyForDescendents,
79                    triggered, obs.checkTriggered());
80        }
81    }
82
83    public class TriggeredObserver extends ContentObserver {
84        private boolean mTriggered;
85        Uri mUri;
86        boolean mNotifyForDescendents;
87
88        /**
89         * Creates an unmanaged TriggeredObserver
90         */
91        public TriggeredObserver() {
92            super(null);
93        }
94
95        /**
96         * Same as TriggeredObserver(uri, true);
97         */
98        public TriggeredObserver(Uri uri) {
99            this(uri, true);
100        }
101
102        /**
103         * Creates a managed TriggeredObserver that self-registers with the
104         * mock ContentResolver
105         */
106        public TriggeredObserver(Uri uri, boolean notifyForDescendents) {
107            super(null);
108            mUri = uri;
109            mNotifyForDescendents = notifyForDescendents;
110            registerContentObserver(uri, notifyForDescendents, this);
111        }
112
113        @Override
114        public void onChange(boolean selfChange) {
115            super.onChange(selfChange);
116            mTriggered = true;
117        }
118
119        public boolean checkTriggered() {
120            boolean ret = mTriggered;
121            mTriggered = false;
122            return ret;
123        }
124    }
125
126    Uri mockInsert(Uri uri, ContentValues values) {
127        assertObserversTriggered(false, mLegacyObserver, mRootObserver);
128        Uri ret = getMockContentResolver().insert(uri, values);
129        assertObserversTriggered(true, mLegacyObserver, mRootObserver);
130        return ret;
131    }
132
133    int mockUpdate(Uri uri, ContentValues values, String where,
134            String[] selectionArgs) {
135        assertObserversTriggered(false, mLegacyObserver, mRootObserver);
136        int ret = getMockContentResolver().update(uri, values, where, selectionArgs);
137        if (ret > 0) {
138            assertObserversTriggered(true, mLegacyObserver, mRootObserver);
139        } else {
140            perfIdeallyUntriggered(mLegacyObserver);
141            perfIdeallyUntriggered(mRootObserver);
142        }
143        return ret;
144    }
145
146    public Uri insertBookmark(String url, String title) {
147        ContentValues values = new ContentValues();
148        values.put(BrowserContract.Bookmarks.TITLE, title);
149        values.put(BrowserContract.Bookmarks.URL, url);
150        values.put(BrowserContract.Bookmarks.IS_FOLDER, 0);
151        return insertBookmark(values);
152    }
153
154    public Uri insertBookmark(ContentValues values) {
155        assertObserversTriggered(false, mBookmarksObserver, mWidgetObserver);
156        Uri ret = mockInsert(Bookmarks.CONTENT_URI, values);
157        assertObserversTriggered(true, mBookmarksObserver, mWidgetObserver);
158        perfIdeallyUntriggered(mHistoryObserver);
159        return ret;
160    }
161
162    public boolean updateBookmark(Uri uri, String url, String title) {
163        ContentValues values = new ContentValues();
164        values.put(BrowserContract.Bookmarks.TITLE, title);
165        values.put(BrowserContract.Bookmarks.URL, url);
166        return updateBookmark(uri, values);
167    }
168
169    public boolean updateBookmark(Uri uri, ContentValues values) {
170        assertObserversTriggered(false, mBookmarksObserver, mWidgetObserver);
171        int modifyCount = mockUpdate(uri, values, null, null);
172        assertTrue("UpdatedBookmark modified too much! " + uri, modifyCount <= 1);
173        boolean updated = modifyCount == 1;
174        if (updated) {
175            assertObserversTriggered(updated, mBookmarksObserver, mWidgetObserver);
176        } else {
177            perfIdeallyUntriggered(mBookmarksObserver, mWidgetObserver);
178        }
179        perfIdeallyUntriggered(mHistoryObserver);
180        return updated;
181    }
182
183    public Uri insertHistory(String url, String title) {
184        ContentValues values = new ContentValues();
185        values.put(BrowserContract.History.TITLE, title);
186        values.put(BrowserContract.History.URL, url);
187        assertObserversTriggered(false, mHistoryObserver);
188        Uri ret = mockInsert(History.CONTENT_URI, values);
189        assertObserversTriggered(true, mHistoryObserver);
190        perfIdeallyUntriggered(mBookmarksObserver, mWidgetObserver);
191        return ret;
192    }
193
194    public boolean updateHistory(Uri uri, String url, String title) {
195        ContentValues values = new ContentValues();
196        values.put(BrowserContract.History.TITLE, title);
197        values.put(BrowserContract.History.URL, url);
198        return updateHistory(uri, values);
199    }
200
201    public boolean updateHistory(Uri uri, ContentValues values) {
202        assertObserversTriggered(false, mHistoryObserver);
203        int modifyCount = mockUpdate(uri, values, null, null);
204        assertTrue("UpdatedHistory modified too much! " + uri, modifyCount <= 1);
205        boolean updated = modifyCount == 1;
206        if (updated) {
207            assertObserversTriggered(updated, mHistoryObserver);
208        } else {
209            perfIdeallyUntriggered(mHistoryObserver);
210        }
211        perfIdeallyUntriggered(mBookmarksObserver, mWidgetObserver);
212        return updated;
213    }
214
215    @Override
216    protected void tearDown() throws Exception {
217        super.tearDown();
218        // Delete the test databases so that subsequent runs have a clean slate
219        File f = getMockContext().getDatabasePath("test");
220        File dir = f.getParentFile();
221        File testFiles[] = dir.listFiles(new FilenameFilter() {
222
223            @Override
224            public boolean accept(File dir, String filename) {
225                return filename.startsWith(ProviderTestCase3.FILENAME_PREFIX);
226            }
227        });
228        for (File testFile : testFiles) {
229            testFile.delete();
230        }
231    }
232}
233