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