|
50 | 50 | import java.util.List; |
51 | 51 | import java.util.Locale; |
52 | 52 | import java.util.Map; |
| 53 | +import java.util.Optional; |
53 | 54 | import java.util.concurrent.Callable; |
54 | 55 | import java.util.concurrent.ConcurrentHashMap; |
55 | 56 | import java.util.concurrent.CyclicBarrier; |
@@ -361,8 +362,23 @@ public void socketTimeout_throwsIOExceptionInsteadOfSocketTimeoutException() thr |
361 | 362 | ISO_8859_1)) { |
362 | 363 | fail("Should have thrown"); |
363 | 364 | } catch (IOException expected) { |
364 | | - assertThat(expected).hasCauseThat().isInstanceOf(SocketTimeoutException.class); |
365 | | - assertThat(expected).hasCauseThat().hasMessageThat().ignoringCase().contains("timed out"); |
| 365 | + if (expected.getCause() != null) { |
| 366 | + // SocketTimeoutException gets wrapped in an IOException and rethrown. |
| 367 | + assertThat(expected).hasCauseThat().isInstanceOf(SocketTimeoutException.class); |
| 368 | + assertThat(expected).hasCauseThat().hasMessageThat().ignoringCase().contains("timed out"); |
| 369 | + } else { |
| 370 | + // For windows, it is possible that the thrown exception is a ConnectException (no cause) |
| 371 | + // from {@code HttpURLConnection.connect()} rather than a SocketTimeoutException from |
| 372 | + // {@code HttpURLConnection.getResponseCode()}. In the former case, we expect the |
| 373 | + // SocketTimeoutException to have already occurred but gets suppressed within the final |
| 374 | + // ConnectException (upon max retry). |
| 375 | + ImmutableList<Throwable> suppressed = ImmutableList.copyOf(expected.getSuppressed()); |
| 376 | + Optional<Throwable> ste = |
| 377 | + suppressed.stream().filter(t -> t instanceof SocketTimeoutException).findFirst(); |
| 378 | + assertThat(ste).isPresent(); |
| 379 | + assertThat(ste.get()).isInstanceOf(SocketTimeoutException.class); |
| 380 | + assertThat(ste.get()).hasMessageThat().ignoringCase().contains("timed out"); |
| 381 | + } |
366 | 382 | } |
367 | 383 | } |
368 | 384 | } |
|
0 commit comments