1/* 2 * Copyright (C) 2015 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.support.v7.util; 18 19import static org.hamcrest.MatcherAssert.*; 20import static org.hamcrest.CoreMatchers.*; 21 22import org.junit.Before; 23import org.junit.Test; 24import org.junit.runner.RunWith; 25 26import android.os.Looper; 27import android.support.test.runner.AndroidJUnit4; 28 29import java.util.HashMap; 30import java.util.Map; 31import java.util.concurrent.Semaphore; 32import java.util.concurrent.TimeUnit; 33 34@RunWith(AndroidJUnit4.class) 35public class ThreadUtilTest { 36 Map<String, LockedObject> results = new HashMap<>(); 37 38 ThreadUtil.MainThreadCallback<Integer> mMainThreadProxy; 39 ThreadUtil.BackgroundCallback<Integer> mBackgroundProxy; 40 41 @Before 42 public void setup() { 43 ThreadUtil<Integer> threadUtil = new MessageThreadUtil<>(); 44 45 mMainThreadProxy = threadUtil.getMainThreadProxy( 46 new ThreadUtil.MainThreadCallback<Integer>() { 47 @Override 48 public void updateItemCount(int generation, int itemCount) { 49 assertMainThread(); 50 setResultData("updateItemCount", generation, itemCount); 51 } 52 53 @Override 54 public void addTile(int generation, TileList.Tile<Integer> data) { 55 assertMainThread(); 56 setResultData("addTile", generation, data); 57 } 58 59 @Override 60 public void removeTile(int generation, int position) { 61 assertMainThread(); 62 setResultData("removeTile", generation, position); 63 } 64 }); 65 66 mBackgroundProxy = threadUtil.getBackgroundProxy( 67 new ThreadUtil.BackgroundCallback<Integer>() { 68 @Override 69 public void refresh(int generation) { 70 assertBackgroundThread(); 71 setResultData("refresh", generation); 72 } 73 74 @Override 75 public void updateRange(int rangeStart, int rangeEnd, int extRangeStart, 76 int extRangeEnd, int scrollHint) { 77 assertBackgroundThread(); 78 setResultData("updateRange", rangeStart, rangeEnd, 79 extRangeStart, extRangeEnd, scrollHint); 80 } 81 82 @Override 83 public void loadTile(int position, int scrollHint) { 84 assertBackgroundThread(); 85 setResultData("loadTile", position, scrollHint); 86 } 87 88 @Override 89 public void recycleTile(TileList.Tile<Integer> data) { 90 assertBackgroundThread(); 91 setResultData("recycleTile", data); 92 } 93 }); 94 } 95 96 @Test 97 public void testUpdateItemCount() throws InterruptedException { 98 initWait("updateItemCount"); 99 mMainThreadProxy.updateItemCount(7, 9); 100 Object[] data = waitFor("updateItemCount"); 101 assertThat(data, is(new Object[]{7, 9})); 102 } 103 104 @Test 105 public void testAddTile() throws InterruptedException { 106 initWait("addTile"); 107 TileList.Tile<Integer> tile = new TileList.Tile<Integer>(Integer.class, 10); 108 mMainThreadProxy.addTile(3, tile); 109 Object[] data = waitFor("addTile"); 110 assertThat(data, is(new Object[]{3, tile})); 111 } 112 113 @Test 114 public void testRemoveTile() throws InterruptedException { 115 initWait("removeTile"); 116 mMainThreadProxy.removeTile(1, 2); 117 Object[] data = waitFor("removeTile"); 118 assertThat(data, is(new Object[]{1, 2})); 119 } 120 121 @Test 122 public void testRefresh() throws InterruptedException { 123 initWait("refresh"); 124 mBackgroundProxy.refresh(2); 125 Object[] data = waitFor("refresh"); 126 assertThat(data, is(new Object[]{2})); 127 } 128 129 @Test 130 public void testRangeUpdate() throws InterruptedException { 131 initWait("updateRange"); 132 mBackgroundProxy.updateRange(10, 20, 5, 25, 1); 133 Object[] data = waitFor("updateRange"); 134 assertThat(data, is(new Object[] {10, 20, 5, 25, 1})); 135 } 136 137 @Test 138 public void testLoadTile() throws InterruptedException { 139 initWait("loadTile"); 140 mBackgroundProxy.loadTile(2, 1); 141 Object[] data = waitFor("loadTile"); 142 assertThat(data, is(new Object[]{2, 1})); 143 } 144 145 @Test 146 public void testRecycleTile() throws InterruptedException { 147 initWait("recycleTile"); 148 TileList.Tile<Integer> tile = new TileList.Tile<Integer>(Integer.class, 10); 149 mBackgroundProxy.recycleTile(tile); 150 Object[] data = waitFor("recycleTile"); 151 assertThat(data, is(new Object[]{tile})); 152 } 153 154 private void assertMainThread() { 155 assertThat(Looper.myLooper(), notNullValue()); 156 assertThat(Looper.myLooper(), sameInstance(Looper.getMainLooper())); 157 } 158 159 private void assertBackgroundThread() { 160 assertThat(Looper.myLooper(), not(Looper.getMainLooper())); 161 } 162 163 private void initWait(String key) throws InterruptedException { 164 results.put(key, new LockedObject()); 165 } 166 167 private Object[] waitFor(String key) throws InterruptedException { 168 return results.get(key).waitFor(); 169 } 170 171 private void setResultData(String key, Object... args) { 172 if (results.containsKey(key)) { 173 results.get(key).set(args); 174 } 175 } 176 177 private class LockedObject { 178 private Semaphore mLock = new Semaphore(1); 179 private volatile Object[] mArgs; 180 181 public LockedObject() { 182 mLock.drainPermits(); 183 } 184 185 public void set(Object... args) { 186 mArgs = args; 187 mLock.release(1); 188 } 189 190 public Object[] waitFor() throws InterruptedException { 191 mLock.tryAcquire(1, 2, TimeUnit.SECONDS); 192 return mArgs; 193 } 194 } 195} 196