1 /* 2 * Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 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 56 public void run(Path scratchPath) { 57 hasRun = true; 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 { 104 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 }