/*****************************************************************************
* ------------------------------------------------------------------------- *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
*****************************************************************************/
package com.google.mu.util;
import static com.google.common.truth.Truth.assertThat;
import static com.google.mu.util.FutureAssertions.assertCauseOf;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.mockito.Mockito;
import com.google.common.testing.NullPointerTester;
import com.google.mu.util.Utils;
public class UtilsTest {
@Test public void testMapList_empty() {
assertThat(Utils.mapList(asList(), Object::toString)).isEmpty();
}
@Test public void testMapList_nonEmpty() {
assertThat(Utils.mapList(asList(1, 2), Object::toString)).containsExactly("1", "2").inOrder();
}
@Test public void testNulls() {
for (Method method : Utils.class.getDeclaredMethods()) {
if (method.isSynthetic()) continue;
if (method.getName().equals("cast")) continue;
new NullPointerTester().testMethod(null, method);
}
}
@Test public void testTyped_notOfType() {
StringCondition condition = Mockito.mock(StringCondition.class);
assertThat(Utils.typed(String.class, condition::test).test(1)).isFalse();
verify(condition, never()).test(any(String.class));
}
@Test public void testTyped_ofType_false() {
StringCondition condition = Mockito.mock(StringCondition.class);
when(condition.test("hi")).thenReturn(false);
assertThat(Utils.typed(String.class, condition::test).test("hi")).isFalse();
verify(condition).test("hi");
}
@Test public void testTyped_ofType_true() {
StringCondition condition = Mockito.mock(StringCondition.class);
when(condition.test("hi")).thenReturn(true);
assertThat(Utils.typed(String.class, condition::test).test("hi")).isTrue();
verify(condition).test("hi");
}
@Test public void testCast_notAnInstance() {
assertThat(Utils.cast(1, String.class)).isEqualTo(Optional.empty());
}
@Test public void testCast_isAnInstance() {
assertThat(Utils.cast("hi", String.class)).isEqualTo(Optional.of("hi"));
}
@Test public void testCast_null() {
assertThat(Utils.cast(null, String.class)).isEqualTo(Optional.empty());
assertThrows(NullPointerException.class, () -> Utils.cast("hi", null));
}
@Test public void testTyped_doesNotPassCondition() {
assertThat(Utils.typed(String.class, x -> true).test(1)).isFalse();
}
@Test public void testIfCancelled_pending() {
AtomicReference<CancellationException> cancelled = new AtomicReference<>();
CompletableFuture<String> future = new CompletableFuture<>();
Utils.ifCancelled(future, cancelled::set);
assertThat(cancelled.get()).isNull();
}
@Test public void testIfCancelled_completed() {
AtomicReference<CancellationException> cancelled = new AtomicReference<>();
CompletableFuture<String> future = new CompletableFuture<>();
future.complete("good");
Utils.ifCancelled(future, cancelled::set);
assertThat(cancelled.get()).isNull();
}
@Test public void testIfCancelled_exception() {
AtomicReference<CancellationException> cancelled = new AtomicReference<>();
CompletableFuture<String> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
Utils.ifCancelled(future, cancelled::set);
assertThat(cancelled.get()).isNull();
}
@Test public void testIfCancelled_cancellationException() {
AtomicReference<CancellationException> cancelled = new AtomicReference<>();
CompletableFuture<String> future = new CompletableFuture<>();
CancellationException exception = new CancellationException();
future.completeExceptionally(exception);
Utils.ifCancelled(future, cancelled::set);
assertThat(cancelled.get()).isSameAs(exception);
}
@Test public void testIfCancelled_cancelledWithInterruption() {
AtomicReference<CancellationException> cancelled = new AtomicReference<>();
CompletableFuture<String> future = new CompletableFuture<>();
future.cancel(true);
Utils.ifCancelled(future, cancelled::set);
assertThat(cancelled.get()).isInstanceOf(CancellationException.class);
}
@Test public void testIfCancelled_cancelledWithoutInterruption() {
AtomicReference<CancellationException> cancelled = new AtomicReference<>();
CompletableFuture<String> future = new CompletableFuture<>();
future.cancel(false);
Utils.ifCancelled(future, cancelled::set);
assertThat(cancelled.get()).isInstanceOf(CancellationException.class);
}
@Test public void testIfCancelled_callbackExceptionIgnored() {
CompletableFuture<String> future = new CompletableFuture<>();
future.cancel(false);
Utils.ifCancelled(future, e -> {throw new NullPointerException();});
assertThat(future.isCancelled()).isTrue();
}
@Test public void testPropagateCancellation_bothPending() {
CompletableFuture<String> outer = new CompletableFuture<>();
CompletableFuture<String> inner = new CompletableFuture<>();
assertThat(Utils.propagateCancellation(outer, inner)).isSameAs(outer);
assertThat(outer.isCancelled()).isFalse();
assertThat(inner.isCancelled()).isFalse();
assertThat(outer.isDone()).isFalse();
assertThat(inner.isDone()).isFalse();
}
@Test public void testPropagateCancellation_cancellationWithInterruptionPropagated() {
CompletableFuture<String> outer = new CompletableFuture<>();
CompletableFuture<String> inner = new CompletableFuture<>();
assertThat(Utils.propagateCancellation(outer, inner)).isSameAs(outer);
outer.cancel(true);
assertThat(outer.isCancelled()).isTrue();
assertThat(inner.isCancelled()).isTrue();
assertThat(outer.isDone()).isTrue();
assertThat(inner.isDone()).isTrue();
assertThrows(CancellationException.class, inner::get);
}
@Test public void testPropagateCancellation_cancellationWithoutInterruptionPropagated() {
CompletableFuture<String> outer = new CompletableFuture<>();
CompletableFuture<String> inner = new CompletableFuture<>();
assertThat(Utils.propagateCancellation(outer, inner)).isSameAs(outer);
outer.cancel(false);
assertThat(outer.isCancelled()).isTrue();
assertThat(inner.isCancelled()).isTrue();
assertThat(outer.isDone()).isTrue();
assertThat(inner.isDone()).isTrue();
assertThrows(CancellationException.class, inner::get);
}
@Test public void testPropagateCancellation_completedResultNotPropagated() {
CompletableFuture<String> outer = new CompletableFuture<>();
CompletableFuture<String> inner = new CompletableFuture<>();
assertThat(Utils.propagateCancellation(outer, inner)).isSameAs(outer);
outer.complete("ok");
assertThat(outer.isCancelled()).isFalse();
assertThat(inner.isCancelled()).isFalse();
assertThat(outer.isDone()).isTrue();
assertThat(inner.isDone()).isFalse();
}
@Test public void testPropagateCancellation_exceptionalResultNotPropagated() {
CompletableFuture<String> outer = new CompletableFuture<>();
CompletableFuture<String> inner = new CompletableFuture<>();
assertThat(Utils.propagateCancellation(outer, inner)).isSameAs(outer);
outer.completeExceptionally(new IllegalArgumentException());
assertThat(outer.isCancelled()).isFalse();
assertThat(inner.isCancelled()).isFalse();
assertThat(outer.isCompletedExceptionally()).isTrue();
assertThat(inner.isCompletedExceptionally()).isFalse();
assertThat(outer.isDone()).isTrue();
assertThat(inner.isDone()).isFalse();
}
@Test public void testPropagateCancellation_innerAlreadyCancelled() {
CompletableFuture<String> outer = new CompletableFuture<>();
CompletableFuture<String> inner = new CompletableFuture<>();
assertThat(Utils.propagateCancellation(outer, inner)).isSameAs(outer);
inner.cancel(false);
outer.cancel(false);
assertThat(outer.isCancelled()).isTrue();
assertThat(inner.isCancelled()).isTrue();
assertThat(outer.isCompletedExceptionally()).isTrue();
assertThat(inner.isCompletedExceptionally()).isTrue();
assertThat(outer.isDone()).isTrue();
assertThat(inner.isDone()).isTrue();
}
@Test public void testPropagateCancellation_innerAlreadyCompleted() throws Exception {
CompletableFuture<String> outer = new CompletableFuture<>();
CompletableFuture<String> inner = new CompletableFuture<>();
assertThat(Utils.propagateCancellation(outer, inner)).isSameAs(outer);
inner.complete("inner");
outer.cancel(false);
assertThat(outer.isCancelled()).isTrue();
assertThat(inner.isCancelled()).isFalse();
assertThat(outer.isCompletedExceptionally()).isTrue();
assertThat(inner.isCompletedExceptionally()).isFalse();
assertThat(outer.isDone()).isTrue();
assertThat(inner.isDone()).isTrue();
assertThat(inner.get()).isEqualTo("inner");
}
@Test public void testPropagateCancellation_innerAlreadyFailed() {
CompletableFuture<String> outer = new CompletableFuture<>();
CompletableFuture<String> inner = new CompletableFuture<>();
assertThat(Utils.propagateCancellation(outer, inner)).isSameAs(outer);
IOException exception = new IOException();
inner.completeExceptionally(exception);
outer.cancel(false);
assertThat(outer.isCancelled()).isTrue();
assertThat(inner.isCancelled()).isFalse();
assertThat(outer.isCompletedExceptionally()).isTrue();
assertThat(inner.isCompletedExceptionally()).isTrue();
assertThat(outer.isDone()).isTrue();
assertThat(inner.isDone()).isTrue();
assertCauseOf(ExecutionException.class, inner).isSameAs(exception);
}
@Test public void testPropagateCancellation_innerDoesNotSupportToCompletableFuture() {
CompletableFuture<String> outer = new CompletableFuture<>();
CompletableFuture<String> inner = Mockito.spy(new CompletableFuture<>());
assertThat(Utils.propagateCancellation(outer, inner)).isSameAs(outer);
Mockito.doThrow(new UnsupportedOperationException()).when(inner).toCompletableFuture();
outer.cancel(false);
assertThat(outer.isCancelled()).isTrue();
assertThat(inner.isCancelled()).isFalse();
assertThat(outer.isCompletedExceptionally()).isTrue();
assertThat(inner.isCompletedExceptionally()).isFalse();
assertThat(outer.isDone()).isTrue();
assertThat(inner.isDone()).isFalse();
assertThrows(CancellationException.class, outer::get);
}
private interface StringCondition {
boolean test(String s);
}
}