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 package com.aragost.javahg.test;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.util.List;
31 import java.util.Random;
32
33 import org.junit.Assert;
34 import org.junit.Test;
35
36 import com.aragost.javahg.BaseRepository;
37 import com.aragost.javahg.Changeset;
38 import com.aragost.javahg.Repository;
39 import com.aragost.javahg.RepositoryConfiguration;
40 import com.aragost.javahg.commands.AddCommand;
41 import com.aragost.javahg.commands.CommitCommand;
42 import com.aragost.javahg.commands.ExecutionException;
43 import com.aragost.javahg.commands.LogCommand;
44 import com.aragost.javahg.commands.RemoveCommand;
45 import com.google.common.collect.Lists;
46 import com.google.common.io.Files;
47
48
49
50
51
52 public class StressTest extends AbstractTestCase {
53
54 private static final int CYCLES_PER_THREAD = 25;
55 private static final int NUMBER_OF_THREADS = 20;
56 private static volatile Random RANDOM = new Random(0);
57
58 @Test
59 public void stressTest() throws InterruptedException {
60 BaseRepository repo = getTestRepository();
61 Stats stats = new Stats();
62 List<StressThread> threads = Lists.newArrayList();
63 for (int i = 0; i < NUMBER_OF_THREADS; i++) {
64 StressThread t = new StressThread(CYCLES_PER_THREAD, i, stats, repo);
65 t.start();
66 threads.add(t);
67 }
68
69 for (StressThread cycleThread : threads) {
70 cycleThread.join();
71 }
72 Assert.assertEquals(2 * CYCLES_PER_THREAD * NUMBER_OF_THREADS, stats.changes + stats.noChanges);
73 System.err.println("Cache stats: " + repo.getCacheStats());
74 }
75
76 @Test
77 public void stressTestConcurrency() throws InterruptedException, IOException {
78 RepositoryConfiguration conf = makeRepoConf();
79 conf.setConcurrency(3);
80 BaseRepository repo = Repository.create(conf, Files.createTempDir());
81
82 Stats stats = new Stats();
83 List<StressThread> threads = Lists.newArrayList();
84
85
86 writeFile(repo, "a", "123");
87 AddCommand.on(repo).execute();
88 CommitCommand.on(repo).message("a").user("setup").execute();
89 writeFile(repo, "b", "123");
90 AddCommand.on(repo).execute();
91 CommitCommand.on(repo).message("b").user("setup").execute();
92 writeFile(repo, "c", "123");
93 AddCommand.on(repo).execute();
94 CommitCommand.on(repo).message("c").user("setup").execute();
95
96 for (int i = 0; i < NUMBER_OF_THREADS; i++) {
97 StressThread t = new StressThread2(CYCLES_PER_THREAD * 10, i, stats, repo);
98 t.start();
99 threads.add(t);
100 }
101
102 for (StressThread cycleThread : threads) {
103 cycleThread.join();
104 }
105 Assert.assertEquals(20 * CYCLES_PER_THREAD * NUMBER_OF_THREADS, stats.changes + stats.noChanges);
106 System.err.println("Cache stats: " + repo.getCacheStats());
107 }
108
109 private String randomString(int length) {
110 StringBuilder builder = new StringBuilder(length);
111 for (int i = 0; i < length; i++) {
112 builder.append((char) ('a' + RANDOM.nextInt(26)));
113 }
114 return builder.toString();
115 }
116
117 class StressThread extends Thread {
118
119 private int count;
120 protected final int threadId;
121 protected final Stats stats;
122 protected final BaseRepository repo;
123
124 public StressThread(int count, int threadId, Stats stats, BaseRepository repo) {
125 this.count = count;
126 this.threadId = threadId;
127 this.stats = stats;
128 this.repo = repo;
129 }
130
131 @Override
132 public void run() {
133 for (int i = 0; i < this.count; i++) {
134 try {
135 performCycle();
136 } catch (IOException e) {
137 throw new RuntimeException(e);
138 }
139 }
140 }
141
142 protected void performCycle() throws IOException {
143 String newFile = randomString(1);
144 String fileContent = randomString(20);
145 writeFile(repo, newFile, fileContent);
146 AddCommand.on(repo).execute();
147 CommitCommand commitCmd = CommitCommand.on(repo).user("thread: " + this.threadId).message(
148 "msg: " + this.threadId);
149 Changeset x = commitCmd.execute();
150 if (x != null) {
151 Changeset y = repo.changeset(x.getNode());
152 Assert.assertSame(x, y);
153 stats.incChanges();
154 } else {
155
156 stats.incNoChanges();
157 }
158
159 File file = new File(repo.getDirectory(), randomString(1));
160 try {
161 RemoveCommand.on(repo).execute(file.getPath());
162 } catch (ExecutionException e) {
163
164
165
166
167
168 }
169
170 commitCmd.execute();
171 if (commitCmd.getReturnCode() == 1) {
172
173 stats.incNoChanges();
174 } else {
175 stats.incChanges();
176 }
177
178 }
179 }
180
181 class StressThread2 extends StressThread {
182
183 public StressThread2(int count, int threadId, Stats stats,
184 BaseRepository repo) {
185 super(count, threadId, stats, repo);
186 }
187
188 @Override
189 protected void performCycle() throws IOException {
190 if (threadId == 0) {
191 super.performCycle();
192 } else {
193 LogCommand.on(repo).limit(3);
194 stats.incChanges();
195 stats.incChanges();
196 }
197 }
198 }
199
200 private static class Stats {
201 private int noChanges = 0;
202 private int changes = 0;
203
204 synchronized void incNoChanges() {
205 this.noChanges++;
206 }
207
208 synchronized void incChanges() {
209 this.changes++;
210 }
211 }
212 }