forked from google/thread-weaver
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUserCacheTest.java
More file actions
168 lines (142 loc) · 5.75 KB
/
UserCacheTest.java
File metadata and controls
168 lines (142 loc) · 5.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*
* Copyright 2009 Weaver authors
*
* This code is part of the Weaver tutorial and may be freely used.
*/
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.makeThreadSafe;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import com.google.testing.threadtester.Script;
import com.google.testing.threadtester.ScriptedTask;
import com.google.testing.threadtester.Scripter;
import com.google.testing.threadtester.ThreadedTest;
import com.google.testing.threadtester.ThreadedTestRunner;
import junit.framework.TestCase;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Tests {@link UserCache} using a {@link Script}.
*
* @author alasdair.mackintosh@gmail.com (Alasdair Mackintosh)
*/
public class UserCacheTest extends TestCase {
private static final String USER = "user";
private static final String AVATAR1 = "A1";
private static final String AVATAR2 = "A2";
private UserDb db;
private RenderingContext context;
private UserCache cache;
// Create a cache, and verify that it initialises itself from the database.
private void createCache() {
db = createMock(UserDb.class);
makeThreadSafe(db, true);
context = createMock(RenderingContext.class);
makeThreadSafe(context, true);
expect(db.getAvatar(USER)).andReturn(AVATAR1);
replay(db, context);
cache = new UserCache(db, USER, context);
verify(db, context);
}
private void resetMocks() {
// Note that resetting a mock will clear the thread-safe setting, so we need to
// mark the mock as threadsafe every time we reset it. Annoying but true...
reset(db);
makeThreadSafe(db, true);
reset(context);
makeThreadSafe(context, true);
}
/** Simple test for non-threaded operation */
public void testUserCacheBasicOperation() {
createCache();
// Verify a normal draw operation
resetMocks();
context.draw(AVATAR1);
replay(db, context);
cache.drawUserAvatar();
verify(db, context);
// Update the user, and verify that the cache is updated.
resetMocks();
db.update(USER);
expect(db.getAvatar(USER)).andReturn(AVATAR2);
context.draw(AVATAR2);
replay(db, context);
cache.updateUser();
cache.drawUserAvatar();
verify(db, context);
}
/** Runner for the threaded tests */
public void testThreadedTests() {
ThreadedTestRunner runner = new ThreadedTestRunner();
runner.setDebug(false); // Set this to true for debugging the scripting
runner.runTests(getClass(), UserCache.class);
}
/**
* Multithreaded test demonstrating the use of scripts.
*/
@ThreadedTest
public void runUserCacheMultiThreaded() throws Exception {
createCache();
// Update the user, which will mark the cache as invalid. This
// means that when we call cache.drawUserAvatar() below, it will
// refresh the cache.
resetMocks();
db.update(USER);
expect(db.getAvatar(USER)).andReturn(AVATAR2);
context.draw(AVATAR2);
replay(db, context);
cache.updateUser();
// Create two scripts. The main script will call
// cache.drawUserAvatar(). It will yeild control to the second
// script at two different pooints, and the second script will
// then verify the state of the locks.
final Script<UserCache> main = new Script<UserCache>(cache);
final Script<UserCache> second = new Script<UserCache>(main);
// Create control and target objects to allow us to specify a release point.
final UserCache control = main.object();
final UserDb dbTarget = main.createTarget(UserDb.class);
final RenderingContext contextTarget = main.createTarget(RenderingContext.class);
final ReadWriteLock lockTarget = main.createTarget(ReadWriteLock.class);
// Some of the methods that we are invoking are void, which means that we cannot use the
// main.in(xx).beforeCallng(yy) syntax. Instead we need to invoke a method on the control
// object, and then use inLastMethod() and beforeCalling(). This is analagous to
// EasyMock.expectLastcall()
//
// In this case we want to release control twice. The first time will be just before we
// call getAvatar(), and the second will be just before we callcontext.draw().
control.updateCache();
main.inLastMethod().beforeCalling(dbTarget.getAvatar("")).releaseTo(second);
control.drawUserAvatar();
main.inLastMethod();
contextTarget.draw("");
main.beforeCallingLastMethod().releaseTo(second);
main.addTask(new ScriptedTask<UserCache>() {
@Override
public void execute() {
cache.drawUserAvatar();
}
});
second.addTask(new ScriptedTask<UserCache>() {
@Override
public void execute() {
// The second task verifies the locks at the point where it is released, and then
// returns control to the first script.
ReentrantReadWriteLock lock = cache.rwl;
// This is the first point where the main script releases control to the second
// script. We print some diagnostics, and release control back to the main.
System.out.printf("First release - write = %s, num readers = %d\n",
lock.isWriteLocked(), lock.getReadLockCount());
releaseTo(main);
// This is the second point where the main script releases control to the second
// script. Again, print diagnostics, and release control back to the main.
System.out.printf("Second release - write = %s, num readers = %d\n",
lock.isWriteLocked(), lock.getReadLockCount());
releaseTo(main);
}
});
// Run the two scripts defined above.
new Scripter<UserCache>(main, second).execute();
}
}