1/*
2 * Copyright (C) 2007 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 android.test;
18
19import android.content.ContentProvider;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.res.Resources;
23import android.test.mock.MockContext;
24import android.test.mock.MockContentResolver;
25import android.database.DatabaseUtils;
26
27import java.io.File;
28
29/**
30 * This test case class provides a framework for testing a single
31 * {@link ContentProvider} and for testing your app code with an
32 * isolated content provider. Instead of using the system map of
33 * providers that is based on the manifests of other applications, the test
34 * case creates its own internal map. It then uses this map to resolve providers
35 * given an authority. This allows you to inject test providers and to null out
36 * providers that you do not want to use.
37 * <p>
38 *      This test case also sets up the following mock objects:
39 * </p>
40 * <ul>
41 *      <li>
42 *          An {@link android.test.IsolatedContext} that stubs out Context methods that might
43 *          affect the rest of the running system, while allowing tests to do real file and
44 *          database work.
45 *      </li>
46 *      <li>
47 *          A {@link android.test.mock.MockContentResolver} that provides the functionality of a
48 *          regular content resolver, but uses {@link IsolatedContext}. It stubs out
49 *          {@link ContentResolver#notifyChange(Uri, ContentObserver, boolean)} to
50 *          prevent the test from affecting the running system.
51 *      </li>
52 *      <li>
53 *          An instance of the provider under test, running in an {@link IsolatedContext}.
54 *      </li>
55 * </ul>
56 * <p>
57 *      This framework is set up automatically by the base class' {@link #setUp()} method. If you
58 *      override this method, you must call the super method as the first statement in
59 *      your override.
60 * </p>
61 * <p>
62 *     In order for their tests to be run, concrete subclasses must provide their own
63 *     constructor with no arguments. This constructor must call
64 *     {@link #ProviderTestCase2(Class, String)} as  its first operation.
65 * </p>
66 * For more information on content provider testing, please see
67 * <a href="{@docRoot}tools/testing/contentprovider_testing.html">Content Provider Testing</a>.
68 */
69public abstract class ProviderTestCase2<T extends ContentProvider> extends AndroidTestCase {
70
71    Class<T> mProviderClass;
72    String mProviderAuthority;
73
74    private IsolatedContext mProviderContext;
75    private MockContentResolver mResolver;
76
77    private class MockContext2 extends MockContext {
78
79        @Override
80        public Resources getResources() {
81            return getContext().getResources();
82        }
83
84        @Override
85        public File getDir(String name, int mode) {
86            // name the directory so the directory will be separated from
87            // one created through the regular Context
88            return getContext().getDir("mockcontext2_" + name, mode);
89        }
90
91        @Override
92        public Context getApplicationContext() {
93            return this;
94        }
95    }
96    /**
97     * Constructor.
98     *
99     * @param providerClass The class name of the provider under test
100     * @param providerAuthority The provider's authority string
101     */
102    public ProviderTestCase2(Class<T> providerClass, String providerAuthority) {
103        mProviderClass = providerClass;
104        mProviderAuthority = providerAuthority;
105    }
106
107    private T mProvider;
108
109    /**
110     * Returns the content provider created by this class in the {@link #setUp()} method.
111     * @return T An instance of the provider class given as a parameter to the test case class.
112     */
113    public T getProvider() {
114        return mProvider;
115    }
116
117    /**
118     * Sets up the environment for the test fixture.
119     * <p>
120     * Creates a new
121     * {@link android.test.mock.MockContentResolver}, a new IsolatedContext
122     * that isolates the provider's file operations, and a new instance of
123     * the provider under test within the isolated environment.
124     * </p>
125     *
126     * @throws Exception
127     */
128    @Override
129    protected void setUp() throws Exception {
130        super.setUp();
131
132        mResolver = new MockContentResolver();
133        final String filenamePrefix = "test.";
134        RenamingDelegatingContext targetContextWrapper = new
135                RenamingDelegatingContext(
136                new MockContext2(), // The context that most methods are
137                                    //delegated to
138                getContext(), // The context that file methods are delegated to
139                filenamePrefix);
140        mProviderContext = new IsolatedContext(mResolver, targetContextWrapper);
141
142        mProvider = mProviderClass.newInstance();
143        mProvider.attachInfo(mProviderContext, null);
144        assertNotNull(mProvider);
145        mResolver.addProvider(mProviderAuthority, getProvider());
146    }
147
148    /**
149     * Tears down the environment for the test fixture.
150     * <p>
151     * Calls {@link android.content.ContentProvider#shutdown()} on the
152     * {@link android.content.ContentProvider} represented by mProvider.
153     */
154    @Override
155    protected void tearDown() throws Exception {
156        mProvider.shutdown();
157        super.tearDown();
158    }
159
160    /**
161     * Gets the {@link MockContentResolver} created by this class during initialization. You
162     * must use the methods of this resolver to access the provider under test.
163     *
164     * @return A {@link MockContentResolver} instance.
165     */
166    public MockContentResolver getMockContentResolver() {
167        return mResolver;
168    }
169
170    /**
171     * Gets the {@link IsolatedContext} created by this class during initialization.
172     * @return The {@link IsolatedContext} instance
173     */
174    public IsolatedContext getMockContext() {
175        return mProviderContext;
176    }
177
178    /**
179     * <p>
180     *      Creates a new content provider of the same type as that passed to the test case class,
181     *      with an authority name set to the authority parameter, and using an SQLite database as
182     *      the underlying data source. The SQL statement parameter is used to create the database.
183     *      This method also creates a new {@link MockContentResolver} and adds the provider to it.
184     * </p>
185     * <p>
186     *      Both the new provider and the new resolver are put into an {@link IsolatedContext}
187     *      that uses the targetContext parameter for file operations and a {@link MockContext}
188     *      for everything else. The IsolatedContext prepends the filenamePrefix parameter to
189     *      file, database, and directory names.
190     * </p>
191     * <p>
192     *      This is a convenience method for creating a "mock" provider that can contain test data.
193     * </p>
194     *
195     * @param targetContext The context to use as the basis of the IsolatedContext
196     * @param filenamePrefix A string that is prepended to file, database, and directory names
197     * @param providerClass The type of the provider being tested
198     * @param authority The authority string to associated with the test provider
199     * @param databaseName The name assigned to the database
200     * @param databaseVersion The version assigned to the database
201     * @param sql A string containing the SQL statements that are needed to create the desired
202     * database and its tables. The format is the same as that generated by the
203     * <a href="http://www.sqlite.org/sqlite.html">sqlite3</a> tool's <code>.dump</code> command.
204     * @return ContentResolver A new {@link MockContentResolver} linked to the provider
205     *
206     * @throws IllegalAccessException
207     * @throws InstantiationException
208     */
209    public static <T extends ContentProvider> ContentResolver newResolverWithContentProviderFromSql(
210            Context targetContext, String filenamePrefix, Class<T> providerClass, String authority,
211            String databaseName, int databaseVersion, String sql)
212            throws IllegalAccessException, InstantiationException {
213        MockContentResolver resolver = new MockContentResolver();
214        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
215                new MockContext(), // The context that most methods are delegated to
216                targetContext, // The context that file methods are delegated to
217                filenamePrefix);
218        Context context = new IsolatedContext(resolver, targetContextWrapper);
219        DatabaseUtils.createDbFromSqlStatements(context, databaseName, databaseVersion, sql);
220
221        T provider = providerClass.newInstance();
222        provider.attachInfo(context, null);
223        resolver.addProvider(authority, provider);
224
225        return resolver;
226    }
227}
228