• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 package com.google.android.libraries.mobiledatadownload;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static com.google.common.util.concurrent.Futures.immediateCancelledFuture;
20 import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
21 import static com.google.common.util.concurrent.Futures.immediateVoidFuture;
22 import static org.junit.Assert.assertThrows;
23 
24 import com.google.android.libraries.mobiledatadownload.DownloadException.DownloadResultCode;
25 import com.google.common.base.Optional;
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.util.concurrent.FutureCallback;
28 import com.google.common.util.concurrent.ListenableFuture;
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.concurrent.CancellationException;
33 import java.util.concurrent.ExecutionException;
34 import java.util.concurrent.atomic.AtomicInteger;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 import org.robolectric.RobolectricTestRunner;
38 
39 /** Tests for {@link AggregateException}. */
40 @RunWith(RobolectricTestRunner.class)
41 public class AggregateExceptionTest {
42   @Test
unwrapException_recursivelyUnwrapExecutionException()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
unwrapException_keepOtherExceptions()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
throwableToString_maxCauseDepthCutoff()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
throwIfFailed_multipleExceptions()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
throwIfFailed_withCallback_invokesCallback()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 }
180