blob: 472374de1bf079d288ef27a09059411f166c99a0 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.v7.util;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.CoreMatchers.*;
import android.os.Looper;
import android.support.annotation.UiThread;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class ThreadUtilTest extends BaseThreadedTest {
Map<String, LockedObject> results = new HashMap<>();
ThreadUtil.MainThreadCallback<Integer> mMainThreadProxy;
ThreadUtil.BackgroundCallback<Integer> mBackgroundProxy;
@Override
@UiThread
public void setUpUi() {
ThreadUtil<Integer> threadUtil = new MessageThreadUtil<>();
mMainThreadProxy = threadUtil.getMainThreadProxy(
new ThreadUtil.MainThreadCallback<Integer>() {
@Override
public void updateItemCount(int generation, int itemCount) {
assertMainThread();
setResultData("updateItemCount", generation, itemCount);
}
@Override
public void addTile(int generation, TileList.Tile<Integer> data) {
assertMainThread();
setResultData("addTile", generation, data);
}
@Override
public void removeTile(int generation, int position) {
assertMainThread();
setResultData("removeTile", generation, position);
}
});
mBackgroundProxy = threadUtil.getBackgroundProxy(
new ThreadUtil.BackgroundCallback<Integer>() {
@Override
public void refresh(int generation) {
assertBackgroundThread();
setResultData("refresh", generation);
}
@Override
public void updateRange(int rangeStart, int rangeEnd, int extRangeStart,
int extRangeEnd, int scrollHint) {
assertBackgroundThread();
setResultData("updateRange", rangeStart, rangeEnd,
extRangeStart, extRangeEnd, scrollHint);
}
@Override
public void loadTile(int position, int scrollHint) {
assertBackgroundThread();
setResultData("loadTile", position, scrollHint);
}
@Override
public void recycleTile(TileList.Tile<Integer> data) {
assertBackgroundThread();
setResultData("recycleTile", data);
}
});
}
public void testUpdateItemCount() throws InterruptedException {
initWait("updateItemCount");
// In this test and below the calls to mMainThreadProxy are not really made from the UI
// thread. That's OK since the message queue inside mMainThreadProxy is synchronized.
mMainThreadProxy.updateItemCount(7, 9);
Object[] data = waitFor("updateItemCount");
assertThat(data, is(new Object[]{7, 9}));
}
public void testAddTile() throws InterruptedException {
initWait("addTile");
TileList.Tile<Integer> tile = new TileList.Tile<Integer>(Integer.class, 10);
mMainThreadProxy.addTile(3, tile);
Object[] data = waitFor("addTile");
assertThat(data, is(new Object[]{3, tile}));
}
public void testRemoveTile() throws InterruptedException {
initWait("removeTile");
mMainThreadProxy.removeTile(1, 2);
Object[] data = waitFor("removeTile");
assertThat(data, is(new Object[]{1, 2}));
}
public void testRefresh() throws InterruptedException {
initWait("refresh");
// In this test and below the calls to mBackgroundProxy are not really made from the worker
// thread. That's OK since the message queue inside mBackgroundProxy is synchronized.
mBackgroundProxy.refresh(2);
Object[] data = waitFor("refresh");
assertThat(data, is(new Object[]{2}));
}
public void testRangeUpdate() throws InterruptedException {
initWait("updateRange");
mBackgroundProxy.updateRange(10, 20, 5, 25, 1);
Object[] data = waitFor("updateRange");
assertThat(data, is(new Object[] {10, 20, 5, 25, 1}));
}
public void testLoadTile() throws InterruptedException {
initWait("loadTile");
mBackgroundProxy.loadTile(2, 1);
Object[] data = waitFor("loadTile");
assertThat(data, is(new Object[]{2, 1}));
}
public void testRecycleTile() throws InterruptedException {
initWait("recycleTile");
TileList.Tile<Integer> tile = new TileList.Tile<Integer>(Integer.class, 10);
mBackgroundProxy.recycleTile(tile);
Object[] data = waitFor("recycleTile");
assertThat(data, is(new Object[]{tile}));
}
private void assertMainThread() {
assertThat(Looper.myLooper(), notNullValue());
assertThat(Looper.myLooper(), sameInstance(Looper.getMainLooper()));
}
private void assertBackgroundThread() {
assertThat(Looper.myLooper(), not(Looper.getMainLooper()));
}
private void initWait(String key) throws InterruptedException {
results.put(key, new LockedObject());
}
private Object[] waitFor(String key) throws InterruptedException {
return results.get(key).waitFor();
}
private void setResultData(String key, Object... args) {
if (results.containsKey(key)) {
results.get(key).set(args);
}
}
private class LockedObject {
private Semaphore mLock = new Semaphore(1);
private volatile Object[] mArgs;
public LockedObject() {
mLock.drainPermits();
}
public void set(Object... args) {
mArgs = args;
mLock.release(1);
}
public Object[] waitFor() throws InterruptedException {
mLock.tryAcquire(1, 2, TimeUnit.SECONDS);
return mArgs;
}
}
}