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