5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package org.openjdk.skara.bot;
24
25 import org.junit.jupiter.api.*;
26 import org.openjdk.skara.host.HostedRepository;
27 import org.openjdk.skara.json.*;
28
29 import java.nio.file.*;
30 import java.time.Duration;
31 import java.util.*;
32 import java.util.concurrent.TimeoutException;
33 import java.util.function.Supplier;
34 import java.util.logging.*;
35
36 class TestWorkItem implements WorkItem {
37 private final ConcurrencyCheck concurrencyCheck;
38 private final String description;
39 boolean hasRun = false;
40
41 interface ConcurrencyCheck {
42 boolean concurrentWith(WorkItem other);
43 }
44
45 TestWorkItem(ConcurrencyCheck concurrencyCheck) {
46 this.concurrencyCheck = concurrencyCheck;
47 this.description = null;
48 }
49
50 TestWorkItem(ConcurrencyCheck concurrencyCheck, String description) {
51 this.concurrencyCheck = concurrencyCheck;
52 this.description = description;
53 }
54
55 @Override
58 System.out.println("Item " + this.toString() + " now running");
59 }
60
61 @Override
62 public boolean concurrentWith(WorkItem other) {
63 return concurrencyCheck.concurrentWith(other);
64 }
65
66 @Override
67 public String toString() {
68 return description != null ? description : super.toString();
69 }
70 }
71
72 class TestWorkItemChild extends TestWorkItem {
73 TestWorkItemChild(ConcurrencyCheck concurrencyCheck, String description) {
74 super(concurrencyCheck, description);
75 }
76 }
77
78 class TestBot implements Bot {
79
80 private final List<WorkItem> items;
81 private final Supplier<List<WorkItem>> itemSupplier;
82
83 TestBot(TestWorkItem... items) {
84 this.items = Arrays.asList(items);
85 itemSupplier = null;
86 }
87
88 TestBot(Supplier<List<WorkItem>> itemSupplier) {
89 items = null;
90 this.itemSupplier = itemSupplier;
91 }
92
93 @Override
94 public List<WorkItem> getPeriodicItems() {
95 if (items != null) {
96 return items;
97 } else {
98 return itemSupplier.get();
99 }
100 }
101 }
102
103 class BotRunnerTests {
105 @BeforeAll
106 static void setUp() {
107 Logger log = Logger.getGlobal();
108 log.setLevel(Level.FINER);
109 log = Logger.getLogger("org.openjdk.bots.cli");
110 log.setLevel(Level.FINER);
111 ConsoleHandler handler = new ConsoleHandler();
112 handler.setLevel(Level.FINER);
113 log.addHandler(handler);
114 }
115
116 private BotRunnerConfiguration config() {
117 var config = JSON.object();
118 try {
119 return BotRunnerConfiguration.parse(config);
120 } catch (ConfigurationError configurationError) {
121 throw new RuntimeException(configurationError);
122 }
123 }
124
125 @Test
126 void simpleConcurrent() throws TimeoutException {
127 var item1 = new TestWorkItem(i -> true, "Item 1");
128 var item2 = new TestWorkItem(i -> true, "Item 2");
129 var bot = new TestBot(item1, item2);
130 var runner = new BotRunner(config(), List.of(bot));
131
132 runner.runOnce(Duration.ofSeconds(10));
133
134 Assertions.assertTrue(item1.hasRun);
135 Assertions.assertTrue(item2.hasRun);
136 }
137
138 @Test
139 void simpleSerial() throws TimeoutException {
140 var item1 = new TestWorkItem(i -> false, "Item 1");
141 var item2 = new TestWorkItem(i -> false, "Item 2");
142 var bot = new TestBot(item1, item2);
143 var runner = new BotRunner(config(), List.of(bot));
144
145 runner.runOnce(Duration.ofSeconds(10));
146
147 Assertions.assertTrue(item1.hasRun);
148 Assertions.assertTrue(item2.hasRun);
149 }
150
151 @Test
152 void moreItemsThanScratchPaths() throws TimeoutException {
153 List<TestWorkItem> items = new LinkedList<>();
154 for (int i = 0; i < 20; ++i) {
155 items.add(new TestWorkItem(x -> true, "Item " + i));
156 }
157 var bot = new TestBot(items.toArray(new TestWorkItem[0]));
158 var runner = new BotRunner(config(), List.of(bot));
159
160 runner.runOnce(Duration.ofSeconds(10));
161
162 for (var item : items) {
163 Assertions.assertTrue(item.hasRun);
164 }
165 }
166
167 static class ThrowingItemProvider {
168 private final List<WorkItem> items;
169 private int throwCount;
170
171 ThrowingItemProvider(List<WorkItem> items, int throwCount) {
172 this.items = items;
173 this.throwCount = throwCount;
174 }
175
176 List<WorkItem> get() {
177 if (throwCount-- > 0) {
178 throw new RuntimeException("Sorry, can't provide items just yet");
179 } else {
180 return items;
181 }
182 }
183 }
184
185 @Test
186 void periodItemsThrow() throws TimeoutException {
187 var item1 = new TestWorkItem(i -> false, "Item 1");
188 var item2 = new TestWorkItem(i -> false, "Item 2");
189 var provider = new ThrowingItemProvider(List.of(item1, item2), 1);
190
191 var bot = new TestBot(provider::get);
192
193 new BotRunner(config(), List.of(bot)).runOnce(Duration.ofSeconds(10));
194 Assertions.assertFalse(item1.hasRun);
195 Assertions.assertFalse(item2.hasRun);
196
197 new BotRunner(config(), List.of(bot)).runOnce(Duration.ofSeconds(10));
198 Assertions.assertTrue(item1.hasRun);
199 Assertions.assertTrue(item2.hasRun);
200 }
201
202 @Test
203 void discardAdditionalBlockedItems() throws TimeoutException {
204 var item1 = new TestWorkItem(i -> false, "Item 1");
205 var item2 = new TestWorkItem(i -> false, "Item 2");
206 var item3 = new TestWorkItem(i -> false, "Item 3");
207 var item4 = new TestWorkItem(i -> false, "Item 4");
208 var bot = new TestBot(item1, item2, item3, item4);
209 var runner = new BotRunner(config(), List.of(bot));
210
211 runner.runOnce(Duration.ofSeconds(10));
212
213 Assertions.assertTrue(item1.hasRun);
214 Assertions.assertFalse(item2.hasRun);
215 Assertions.assertFalse(item3.hasRun);
216 Assertions.assertTrue(item4.hasRun);
217 }
218
219 @Test
220 void dontDiscardDifferentBlockedItems() throws TimeoutException {
221 var item1 = new TestWorkItem(i -> false, "Item 1");
222 var item2 = new TestWorkItem(i -> false, "Item 2");
223 var item3 = new TestWorkItem(i -> false, "Item 3");
224 var item4 = new TestWorkItem(i -> false, "Item 4");
225 var item5 = new TestWorkItemChild(i -> false, "Item 5");
226 var item6 = new TestWorkItemChild(i -> false, "Item 6");
227 var item7 = new TestWorkItemChild(i -> false, "Item 7");
228 var bot = new TestBot(item1, item2, item3, item4, item5, item6, item7);
229 var runner = new BotRunner(config(), List.of(bot));
230
231 runner.runOnce(Duration.ofSeconds(10));
232
233 Assertions.assertTrue(item1.hasRun);
234 Assertions.assertFalse(item2.hasRun);
235 Assertions.assertFalse(item3.hasRun);
236 Assertions.assertTrue(item4.hasRun);
237 Assertions.assertFalse(item5.hasRun);
238 Assertions.assertFalse(item6.hasRun);
239 Assertions.assertTrue(item7.hasRun);
240 }
241 }
|
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package org.openjdk.skara.bot;
24
25 import org.openjdk.skara.json.JSON;
26
27 import org.junit.jupiter.api.*;
28
29 import java.nio.file.Path;
30 import java.time.Duration;
31 import java.util.*;
32 import java.util.concurrent.*;
33 import java.util.function.Supplier;
34 import java.util.logging.*;
35
36 import static org.junit.jupiter.api.Assertions.*;
37
38 class TestWorkItem implements WorkItem {
39 private final ConcurrencyCheck concurrencyCheck;
40 private final String description;
41 boolean hasRun = false;
42
43 interface ConcurrencyCheck {
44 boolean concurrentWith(WorkItem other);
45 }
46
47 TestWorkItem(ConcurrencyCheck concurrencyCheck) {
48 this.concurrencyCheck = concurrencyCheck;
49 this.description = null;
50 }
51
52 TestWorkItem(ConcurrencyCheck concurrencyCheck, String description) {
53 this.concurrencyCheck = concurrencyCheck;
54 this.description = description;
55 }
56
57 @Override
60 System.out.println("Item " + this.toString() + " now running");
61 }
62
63 @Override
64 public boolean concurrentWith(WorkItem other) {
65 return concurrencyCheck.concurrentWith(other);
66 }
67
68 @Override
69 public String toString() {
70 return description != null ? description : super.toString();
71 }
72 }
73
74 class TestWorkItemChild extends TestWorkItem {
75 TestWorkItemChild(ConcurrencyCheck concurrencyCheck, String description) {
76 super(concurrencyCheck, description);
77 }
78 }
79
80 class TestBlockedWorkItem implements WorkItem {
81 private final CountDownLatch countDownLatch;
82
83 TestBlockedWorkItem(CountDownLatch countDownLatch) {
84 this.countDownLatch = countDownLatch;
85 }
86
87 @Override
88 public boolean concurrentWith(WorkItem other) {
89 return false;
90 }
91
92 @Override
93 public void run(Path scratchPath) {
94 System.out.println("Starting to wait...");;
95 try {
96 countDownLatch.await();
97 } catch (InterruptedException e) {
98 throw new RuntimeException(e);
99 }
100 System.out.println("Done waiting");
101 }
102 }
103
104 class TestBot implements Bot {
105
106 private final List<WorkItem> items;
107 private final Supplier<List<WorkItem>> itemSupplier;
108
109 TestBot(WorkItem... items) {
110 this.items = Arrays.asList(items);
111 itemSupplier = null;
112 }
113
114 TestBot(Supplier<List<WorkItem>> itemSupplier) {
115 items = null;
116 this.itemSupplier = itemSupplier;
117 }
118
119 @Override
120 public List<WorkItem> getPeriodicItems() {
121 if (items != null) {
122 return items;
123 } else {
124 return itemSupplier.get();
125 }
126 }
127 }
128
129 class BotRunnerTests {
131 @BeforeAll
132 static void setUp() {
133 Logger log = Logger.getGlobal();
134 log.setLevel(Level.FINER);
135 log = Logger.getLogger("org.openjdk.bots.cli");
136 log.setLevel(Level.FINER);
137 ConsoleHandler handler = new ConsoleHandler();
138 handler.setLevel(Level.FINER);
139 log.addHandler(handler);
140 }
141
142 private BotRunnerConfiguration config() {
143 var config = JSON.object();
144 try {
145 return BotRunnerConfiguration.parse(config);
146 } catch (ConfigurationError configurationError) {
147 throw new RuntimeException(configurationError);
148 }
149 }
150
151 private BotRunnerConfiguration config(String json) {
152 var config = JSON.parse(json).asObject();
153 try {
154 return BotRunnerConfiguration.parse(config);
155 } catch (ConfigurationError configurationError) {
156 throw new RuntimeException(configurationError);
157 }
158 }
159 @Test
160 void simpleConcurrent() throws TimeoutException {
161 var item1 = new TestWorkItem(i -> true, "Item 1");
162 var item2 = new TestWorkItem(i -> true, "Item 2");
163 var bot = new TestBot(item1, item2);
164 var runner = new BotRunner(config(), List.of(bot));
165
166 runner.runOnce(Duration.ofSeconds(10));
167
168 assertTrue(item1.hasRun);
169 assertTrue(item2.hasRun);
170 }
171
172 @Test
173 void simpleSerial() throws TimeoutException {
174 var item1 = new TestWorkItem(i -> false, "Item 1");
175 var item2 = new TestWorkItem(i -> false, "Item 2");
176 var bot = new TestBot(item1, item2);
177 var runner = new BotRunner(config(), List.of(bot));
178
179 runner.runOnce(Duration.ofSeconds(10));
180
181 assertTrue(item1.hasRun);
182 assertTrue(item2.hasRun);
183 }
184
185 @Test
186 void moreItemsThanScratchPaths() throws TimeoutException {
187 List<TestWorkItem> items = new LinkedList<>();
188 for (int i = 0; i < 20; ++i) {
189 items.add(new TestWorkItem(x -> true, "Item " + i));
190 }
191 var bot = new TestBot(items.toArray(new TestWorkItem[0]));
192 var runner = new BotRunner(config(), List.of(bot));
193
194 runner.runOnce(Duration.ofSeconds(10));
195
196 for (var item : items) {
197 assertTrue(item.hasRun);
198 }
199 }
200
201 static class ThrowingItemProvider {
202 private final List<WorkItem> items;
203 private int throwCount;
204
205 ThrowingItemProvider(List<WorkItem> items, int throwCount) {
206 this.items = items;
207 this.throwCount = throwCount;
208 }
209
210 List<WorkItem> get() {
211 if (throwCount-- > 0) {
212 throw new RuntimeException("Sorry, can't provide items just yet");
213 } else {
214 return items;
215 }
216 }
217 }
218
219 @Test
220 void periodItemsThrow() throws TimeoutException {
221 var item1 = new TestWorkItem(i -> false, "Item 1");
222 var item2 = new TestWorkItem(i -> false, "Item 2");
223 var provider = new ThrowingItemProvider(List.of(item1, item2), 1);
224
225 var bot = new TestBot(provider::get);
226
227 new BotRunner(config(), List.of(bot)).runOnce(Duration.ofSeconds(10));
228 Assertions.assertFalse(item1.hasRun);
229 Assertions.assertFalse(item2.hasRun);
230
231 new BotRunner(config(), List.of(bot)).runOnce(Duration.ofSeconds(10));
232 assertTrue(item1.hasRun);
233 assertTrue(item2.hasRun);
234 }
235
236 @Test
237 void discardAdditionalBlockedItems() throws TimeoutException {
238 var item1 = new TestWorkItem(i -> false, "Item 1");
239 var item2 = new TestWorkItem(i -> false, "Item 2");
240 var item3 = new TestWorkItem(i -> false, "Item 3");
241 var item4 = new TestWorkItem(i -> false, "Item 4");
242 var bot = new TestBot(item1, item2, item3, item4);
243 var runner = new BotRunner(config(), List.of(bot));
244
245 runner.runOnce(Duration.ofSeconds(10));
246
247 assertTrue(item1.hasRun);
248 Assertions.assertFalse(item2.hasRun);
249 Assertions.assertFalse(item3.hasRun);
250 assertTrue(item4.hasRun);
251 }
252
253 @Test
254 void dontDiscardDifferentBlockedItems() throws TimeoutException {
255 var item1 = new TestWorkItem(i -> false, "Item 1");
256 var item2 = new TestWorkItem(i -> false, "Item 2");
257 var item3 = new TestWorkItem(i -> false, "Item 3");
258 var item4 = new TestWorkItem(i -> false, "Item 4");
259 var item5 = new TestWorkItemChild(i -> false, "Item 5");
260 var item6 = new TestWorkItemChild(i -> false, "Item 6");
261 var item7 = new TestWorkItemChild(i -> false, "Item 7");
262 var bot = new TestBot(item1, item2, item3, item4, item5, item6, item7);
263 var runner = new BotRunner(config(), List.of(bot));
264
265 runner.runOnce(Duration.ofSeconds(10));
266
267 assertTrue(item1.hasRun);
268 Assertions.assertFalse(item2.hasRun);
269 Assertions.assertFalse(item3.hasRun);
270 assertTrue(item4.hasRun);
271 Assertions.assertFalse(item5.hasRun);
272 Assertions.assertFalse(item6.hasRun);
273 assertTrue(item7.hasRun);
274 }
275
276 @Test
277 void watchdogTrigger() throws TimeoutException {
278 var countdownLatch = new CountDownLatch(1);
279 var item = new TestBlockedWorkItem(countdownLatch);
280 var bot = new TestBot(item);
281 var runner = new BotRunner(config("{ \"runner\": { \"watchdog\": \"PT0.01S\" } }"), List.of(bot));
282
283 var errors = new ArrayList<String>();
284 var log = Logger.getLogger("org.openjdk.skara.bot");
285 log.addHandler(new Handler() {
286 @Override
287 public void publish(LogRecord record) {
288 if (record.getLevel().equals(Level.SEVERE)) {
289 errors.add(record.getMessage());
290 }
291 }
292
293 @Override
294 public void flush() {
295 }
296
297 @Override
298 public void close() throws SecurityException {
299 }
300 });
301
302 assertThrows(TimeoutException.class, () -> runner.runOnce(Duration.ofMillis(100)));
303 assertTrue(errors.size() > 0);
304 assertTrue(errors.size() <= 10);
305 countdownLatch.countDown();
306 }
307 }
|