|
18 | 18 |
|
19 | 19 | import com.google.cloud.ServiceOptions;
|
20 | 20 | import com.google.common.io.CharStreams;
|
| 21 | +import com.google.common.util.concurrent.SettableFuture; |
| 22 | +import com.google.common.util.concurrent.UncheckedExecutionException; |
21 | 23 |
|
22 | 24 | import java.io.BufferedInputStream;
|
23 | 25 | import java.io.BufferedOutputStream;
|
|
43 | 45 | import java.util.List;
|
44 | 46 | import java.util.Locale;
|
45 | 47 | import java.util.Map;
|
| 48 | +import java.util.concurrent.ExecutionException; |
| 49 | +import java.util.concurrent.TimeUnit; |
| 50 | +import java.util.concurrent.TimeoutException; |
46 | 51 | import java.util.logging.Level;
|
47 | 52 | import java.util.logging.Logger;
|
48 | 53 | import java.util.zip.ZipEntry;
|
49 | 54 | import java.util.zip.ZipInputStream;
|
| 55 | +import org.joda.time.Duration; |
50 | 56 |
|
51 | 57 | /**
|
52 | 58 | * Utility class to start and stop a local service which is used by unit testing.
|
@@ -104,16 +110,50 @@ protected final void startProcess(String blockUntilOutput)
|
104 | 110 | }
|
105 | 111 |
|
106 | 112 | /**
|
107 |
| - * Stops the local service's subprocess and any possible thread listening for its output. |
| 113 | + * Waits for the local service's subprocess to terminate, |
| 114 | + * and stop any possible thread listening for its output. |
108 | 115 | */
|
109 |
| - protected final void stopProcess() throws IOException, InterruptedException { |
| 116 | + protected final int waitForProcess(Duration timeout) throws IOException, InterruptedException, TimeoutException { |
110 | 117 | if (blockingProcessReader != null) {
|
111 | 118 | blockingProcessReader.terminate();
|
112 | 119 | blockingProcessReader = null;
|
113 | 120 | }
|
114 | 121 | if (activeRunner != null) {
|
115 |
| - activeRunner.stop(); |
| 122 | + int exitCode = activeRunner.waitFor(timeout); |
116 | 123 | activeRunner = null;
|
| 124 | + return exitCode; |
| 125 | + } |
| 126 | + return 0; |
| 127 | + } |
| 128 | + |
| 129 | + private static int waitForProcess(final Process process, Duration timeout) throws InterruptedException, TimeoutException { |
| 130 | + if (process == null) { |
| 131 | + return 0; |
| 132 | + } |
| 133 | + |
| 134 | + final SettableFuture<Integer> exitValue = SettableFuture.create(); |
| 135 | + |
| 136 | + Thread waiter = new Thread(new Runnable() { |
| 137 | + @Override |
| 138 | + public void run() { |
| 139 | + try { |
| 140 | + exitValue.set(process.waitFor()); |
| 141 | + } catch (InterruptedException e) { |
| 142 | + exitValue.setException(e); |
| 143 | + } |
| 144 | + } |
| 145 | + }); |
| 146 | + waiter.start(); |
| 147 | + |
| 148 | + try { |
| 149 | + return exitValue.get(timeout.getMillis(), TimeUnit.MILLISECONDS); |
| 150 | + } catch (ExecutionException e) { |
| 151 | + if (e.getCause() instanceof InterruptedException) { |
| 152 | + throw (InterruptedException) e.getCause(); |
| 153 | + } |
| 154 | + throw new UncheckedExecutionException(e); |
| 155 | + } finally { |
| 156 | + waiter.interrupt(); |
117 | 157 | }
|
118 | 158 | }
|
119 | 159 |
|
@@ -144,7 +184,7 @@ public String getProjectId() {
|
144 | 184 | /**
|
145 | 185 | * Stops the local emulator.
|
146 | 186 | */
|
147 |
| - public abstract void stop() throws IOException, InterruptedException; |
| 187 | + public abstract void stop(Duration timeout) throws IOException, InterruptedException, TimeoutException; |
148 | 188 |
|
149 | 189 | /**
|
150 | 190 | * Resets the internal state of the emulator.
|
@@ -195,9 +235,10 @@ protected interface EmulatorRunner {
|
195 | 235 | void start() throws IOException;
|
196 | 236 |
|
197 | 237 | /**
|
198 |
| - * Stops the emulator associated to this runner. |
| 238 | + * Wait for the emulator associated to this runner to terminate, |
| 239 | + * returning the exit status. |
199 | 240 | */
|
200 |
| - void stop() throws InterruptedException; |
| 241 | + int waitFor(Duration timeout) throws InterruptedException, TimeoutException; |
201 | 242 |
|
202 | 243 | /**
|
203 | 244 | * Returns the process associated to the emulator, if any.
|
@@ -239,11 +280,8 @@ public void start() throws IOException {
|
239 | 280 | }
|
240 | 281 |
|
241 | 282 | @Override
|
242 |
| - public void stop() throws InterruptedException { |
243 |
| - if (process != null) { |
244 |
| - process.destroy(); |
245 |
| - process.waitFor(); |
246 |
| - } |
| 283 | + public int waitFor(Duration timeout) throws InterruptedException, TimeoutException { |
| 284 | + return waitForProcess(process, timeout); |
247 | 285 | }
|
248 | 286 |
|
249 | 287 | @Override
|
@@ -337,11 +375,8 @@ public void start() throws IOException {
|
337 | 375 | }
|
338 | 376 |
|
339 | 377 | @Override
|
340 |
| - public void stop() throws InterruptedException { |
341 |
| - if (process != null) { |
342 |
| - process.destroy(); |
343 |
| - process.waitFor(); |
344 |
| - } |
| 378 | + public int waitFor(Duration timeout) throws InterruptedException, TimeoutException { |
| 379 | + return waitForProcess(process, timeout); |
345 | 380 | }
|
346 | 381 |
|
347 | 382 | @Override
|
|
0 commit comments