blob: 4ecec0f24c9be56c2c86b9dfeb7e36a80e0dffb3 [file] [log] [blame]
Hao Liu59255812022-06-22 14:01:29 -04001/*
2 * Copyright 2022 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.google.android.libraries.mobiledatadownload;
17
18import static com.google.common.truth.Truth.assertThat;
19import static com.google.common.util.concurrent.Futures.immediateCancelledFuture;
20import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
21import static com.google.common.util.concurrent.Futures.immediateVoidFuture;
22import static org.junit.Assert.assertThrows;
23
24import com.google.android.libraries.mobiledatadownload.DownloadException.DownloadResultCode;
25import com.google.common.base.Optional;
26import com.google.common.collect.ImmutableList;
27import com.google.common.util.concurrent.FutureCallback;
28import com.google.common.util.concurrent.ListenableFuture;
29import java.io.IOException;
30import java.util.ArrayList;
31import java.util.List;
32import java.util.concurrent.CancellationException;
33import java.util.concurrent.ExecutionException;
34import java.util.concurrent.atomic.AtomicInteger;
35import org.junit.Test;
36import org.junit.runner.RunWith;
37import org.robolectric.RobolectricTestRunner;
38
39/** Tests for {@link AggregateException}. */
40@RunWith(RobolectricTestRunner.class)
41public class AggregateExceptionTest {
42 @Test
43 public void unwrapException_recursivelyUnwrapExecutionException() {
44 DownloadException downloadException =
45 DownloadException.builder().setDownloadResultCode(DownloadResultCode.UNKNOWN_ERROR).build();
46 ExecutionException executionException1 = new ExecutionException(downloadException);
47 ExecutionException executionException2 = new ExecutionException(executionException1);
48 ExecutionException executionException3 = new ExecutionException(executionException2);
49
50 Throwable throwable = AggregateException.unwrapException(executionException3);
51 assertThat(throwable).isEqualTo(downloadException);
52 assertThat(throwable).hasMessageThat().contains("UNKNOWN_ERROR");
53 assertThat(throwable).hasCauseThat().isNull();
54 }
55
56 @Test
57 public void unwrapException_keepOtherExceptions() {
58 NullPointerException nullPointerException = new NullPointerException("NPE");
59 DownloadException downloadException =
60 DownloadException.builder()
61 .setDownloadResultCode(DownloadResultCode.UNKNOWN_ERROR)
62 .setCause(nullPointerException)
63 .build();
64
65 Throwable throwable = AggregateException.unwrapException(downloadException);
66 assertThat(throwable).isEqualTo(downloadException);
67 assertThat(throwable).hasMessageThat().contains("UNKNOWN_ERROR");
68 assertThat(throwable).hasCauseThat().isEqualTo(nullPointerException);
69 }
70
71 @Test
72 public void throwableToString_maxCauseDepthCutoff() {
73 Throwable throwable =
74 new IOException(
75 "One",
76 new IOException(
77 "Two",
78 new IOException(
79 "Three",
80 new IOException("Four", new IOException("Five", new IOException("Six"))))));
81 assertThat(AggregateException.throwableToString(throwable))
82 .isEqualTo(
83 "java.io.IOException: One"
84 + "\nCaused by: java.io.IOException: Two"
85 + "\nCaused by: java.io.IOException: Three"
86 + "\nCaused by: java.io.IOException: Four"
87 + "\nCaused by: java.io.IOException: Five"
88 + "\n(...)");
89 }
90
91 @Test
92 public void throwIfFailed_multipleExceptions() {
93 List<ListenableFuture<Void>> futures = new ArrayList<>();
94
95 // First 10 futures failed.
96 for (int i = 0; i < 10; i++) {
97 futures.add(
98 immediateFailedFuture(
99 DownloadException.builder()
100 .setDownloadResultCode(DownloadResultCode.UNKNOWN_ERROR)
101 .setMessage(String.format("ERROR_#%d", i + 1))
102 .build()));
103 }
104 // Next 10 futures are canceled.
105 for (int i = 0; i < 10; i++) {
106 futures.add(immediateCancelledFuture());
107 }
108 // The remaining 10 futures succeeded.
109 for (int i = 0; i < 10; i++) {
110 futures.add(immediateVoidFuture());
111 }
112
113 AggregateException exception =
114 assertThrows(
115 AggregateException.class, () -> AggregateException.throwIfFailed(futures, "Failed"));
116 ImmutableList<Throwable> failures = exception.getFailures();
117 assertThat(failures).hasSize(20);
118
119 for (int i = 0; i < 10; i++) {
120 assertThat(failures.get(i)).isNotNull();
121 assertThat(failures.get(i)).isInstanceOf(DownloadException.class);
122 assertThat(failures.get(i)).hasMessageThat().contains("ERROR");
123 }
124 for (int i = 10; i < 20; i++) {
125 assertThat(failures.get(i)).isNotNull();
126 assertThat(failures.get(i)).isInstanceOf(CancellationException.class);
127 }
128 }
129
130 @Test
131 public void throwIfFailed_withCallback_invokesCallback() throws Exception {
132 List<ListenableFuture<Void>> futures = new ArrayList<>();
133
134 // First 10 futures failed.
135 for (int i = 0; i < 10; i++) {
136 futures.add(
137 immediateFailedFuture(
138 DownloadException.builder()
139 .setDownloadResultCode(DownloadResultCode.UNKNOWN_ERROR)
140 .setMessage(String.format("ERROR_#%d", i + 1))
141 .build()));
142 }
143 // Next 10 futures are canceled.
144 for (int i = 0; i < 10; i++) {
145 futures.add(immediateCancelledFuture());
146 }
147 // The remaining 10 futures succeeded.
148 for (int i = 0; i < 10; i++) {
149 futures.add(immediateVoidFuture());
150 }
151
152 AtomicInteger successCount = new AtomicInteger(0);
153 AtomicInteger failureCount = new AtomicInteger(0);
154 AggregateException exception =
155 assertThrows(
156 AggregateException.class,
157 () ->
158 AggregateException.throwIfFailed(
159 futures,
160 Optional.of(
161 new FutureCallback<Void>() {
162 @Override
163 public void onSuccess(Void unused) {
164 successCount.getAndIncrement();
165 }
166
167 @Override
168 public void onFailure(Throwable t) {
169 failureCount.getAndIncrement();
170 }
171 }),
172 "Failed"));
173
174 // Make sure that onSuccess is called 10 times, and onFailure is called 20 times.
175 assertThat(exception.getFailures()).hasSize(20);
176 assertThat(successCount.get()).isEqualTo(10);
177 assertThat(failureCount.get()).isEqualTo(20);
178 }
179}