1 /*
2  * Copyright (C) 2010 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 
17 package com.android.contacts.interactions;
18 
19 import android.app.LoaderManager;
20 import android.content.AsyncTaskLoader;
21 import android.content.Loader;
22 import android.os.Bundle;
23 import android.util.Log;
24 
25 import com.google.common.annotations.VisibleForTesting;
26 
27 import junit.framework.Assert;
28 
29 import java.io.FileDescriptor;
30 import java.io.PrintWriter;
31 import java.util.ArrayList;
32 import java.util.HashSet;
33 import java.util.List;
34 
35 /**
36  * This implementation of TestLoaderManagerBase uses hidden APIs and must therefore
37  * be kept outside of the main Contacts apk.
38  */
39 public class TestLoaderManager extends TestLoaderManagerBase {
40     private static final String TAG = "TestLoaderManager";
41 
42     private final HashSet<Integer> mFinishedLoaders;
43 
44     private LoaderManager mDelegate;
45 
46     @VisibleForTesting
TestLoaderManager()47     public TestLoaderManager() {
48         mFinishedLoaders = new HashSet<Integer>();
49     }
50 
51     /**
52      * Sets the object to which we delegate the actual work.
53      * <p>
54      * It can not be set to null. Once set, it cannot be changed (but it allows setting it to the
55      * same value again).
56      */
setDelegate(LoaderManager delegate)57     public void setDelegate(LoaderManager delegate) {
58         if (delegate == null || (mDelegate != null && mDelegate != delegate)) {
59             throw new IllegalArgumentException("TestLoaderManager cannot be shared");
60         }
61 
62         mDelegate = delegate;
63     }
64 
getDelegate()65     public LoaderManager getDelegate() {
66         return mDelegate;
67     }
68 
reset()69     public void reset() {
70         mFinishedLoaders.clear();
71     }
72 
73     /**
74      * Waits for the specified loaders to complete loading.
75      * <p>
76      * If one of the loaders has already completed since the last call to {@link #reset()}, it will
77      * not wait for it to complete again.
78      */
79     @VisibleForTesting
waitForLoaders(int... loaderIds)80     public synchronized void waitForLoaders(int... loaderIds) {
81         List<Loader<?>> loaders = new ArrayList<Loader<?>>(loaderIds.length);
82         for (int loaderId : loaderIds) {
83             if (mFinishedLoaders.contains(loaderId)) {
84                 // This loader has already completed since the last reset, do not wait for it.
85                 continue;
86             }
87 
88             final AsyncTaskLoader<?> loader =
89                     (AsyncTaskLoader<?>) mDelegate.getLoader(loaderId);
90             if (loader == null) {
91                 Assert.fail("Loader does not exist: " + loaderId);
92                 return;
93             }
94 
95             loaders.add(loader);
96         }
97 
98         waitForLoaders(loaders.toArray(new Loader<?>[0]));
99     }
100 
101     /**
102      * Waits for the specified loaders to complete loading.
103      */
waitForLoaders(Loader<?>.... loaders)104     public static void waitForLoaders(Loader<?>... loaders) {
105         // We want to wait for each loader using a separate thread, so that we can
106         // simulate race conditions.
107         Thread[] waitThreads = new Thread[loaders.length];
108         for (int i = 0; i < loaders.length; i++) {
109             final AsyncTaskLoader<?> loader = (AsyncTaskLoader<?>) loaders[i];
110             waitThreads[i] = new Thread("LoaderWaitingThread" + i) {
111                 @Override
112                 public void run() {
113                     try {
114                         AsyncTaskLoader.class.getMethod("waitForLoader").invoke(loader);
115                     } catch (Throwable e) {
116                         Log.e(TAG, "Exception while waiting for loader: " + loader.getId(), e);
117                         Assert.fail("Exception while waiting for loader: " + loader.getId());
118                     }
119                 }
120             };
121             waitThreads[i].start();
122         }
123 
124         // Now we wait for all these threads to finish
125         for (Thread thread : waitThreads) {
126             try {
127                 thread.join();
128             } catch (InterruptedException e) {
129                 // Ignore
130             }
131         }
132     }
133 
134     @Override
initLoader(final int id, Bundle args, final LoaderCallbacks<D> callback)135     public <D> Loader<D> initLoader(final int id, Bundle args, final LoaderCallbacks<D> callback) {
136         return mDelegate.initLoader(id, args, new LoaderManager.LoaderCallbacks<D>() {
137             @Override
138             public Loader<D> onCreateLoader(int id, Bundle args) {
139                 return callback.onCreateLoader(id, args);
140             }
141 
142             @Override
143             public void onLoadFinished(Loader<D> loader, D data) {
144                 callback.onLoadFinished(loader, data);
145                 synchronized (this) {
146                     mFinishedLoaders.add(id);
147                 }
148             }
149 
150             @Override
151             public void onLoaderReset(Loader<D> loader) {
152                 callback.onLoaderReset(loader);
153             }
154         });
155     }
156 
157     @Override
158     public <D> Loader<D> restartLoader(int id, Bundle args, LoaderCallbacks<D> callback) {
159         return mDelegate.restartLoader(id, args, callback);
160     }
161 
162     @Override
163     public void destroyLoader(int id) {
164         mDelegate.destroyLoader(id);
165     }
166 
167     @Override
168     public <D> Loader<D> getLoader(int id) {
169         return mDelegate.getLoader(id);
170     }
171 
172     @Override
173     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
174         mDelegate.dump(prefix, fd, writer, args);
175     }
176 }
177