You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ma...@apache.org on 2018/08/01 12:34:25 UTC

[07/14] james-project git commit: JAMES-2502 merge util and util-java8

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/CompletableFutureUtilTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/CompletableFutureUtilTest.java b/server/container/util/src/test/java/org/apache/james/util/CompletableFutureUtilTest.java
new file mode 100644
index 0000000..6f333cf
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/CompletableFutureUtilTest.java
@@ -0,0 +1,424 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+
+public class CompletableFutureUtilTest {
+    private ExecutorService executorService;
+
+    @Before
+    public void setUp() {
+        executorService = Executors.newFixedThreadPool(4);
+    }
+
+    @After
+    public void tearDown() {
+        executorService.shutdownNow();
+    }
+
+    @Test
+    public void combineShouldReturnCombinationOfBothSuppliedFutures() {
+        int value1 = 18;
+        int value2 = 12;
+
+        assertThat(CompletableFutureUtil.combine(
+            CompletableFuture.completedFuture(value1),
+            CompletableFuture.completedFuture(value2),
+            (a, b) -> 2 * a + b)
+            .join())
+            .isEqualTo(2 * value1 + value2);
+
+    }
+
+    @Test
+    public void allOfShouldUnboxEmptyStream() {
+        assertThat(
+            CompletableFutureUtil.allOf(Stream.empty())
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void chainAllShouldPreserveExecutionOrder() {
+        int itemCount = 10;
+        ImmutableList<Integer> ints = IntStream.range(0, itemCount)
+            .boxed()
+            .collect(Guavate.toImmutableList());
+
+        ConcurrentLinkedDeque<Integer> queue = new ConcurrentLinkedDeque<>();
+
+        CompletableFutureUtil.chainAll(ints.stream(),
+            i -> CompletableFuture.supplyAsync(() -> {
+                try {
+                    Thread.sleep(itemCount - i);
+                } catch (InterruptedException e) {
+                    throw Throwables.propagate(e);
+                }
+                queue.add(i);
+                return i;
+            }, executorService))
+            .join();
+
+        assertThat(queue)
+            .containsExactlyElementsOf(ints);
+    }
+
+    @Test
+    public void chainAllShouldNotThrowOnEmptyStream() {
+        Stream<Integer> result = CompletableFutureUtil.chainAll(Stream.<Integer>of(),
+            i -> CompletableFuture.supplyAsync(() -> i, executorService))
+            .join();
+
+        assertThat(result.collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void chainAllShouldPreserveOrder() {
+        int itemCount = 10;
+        ImmutableList<Integer> ints = IntStream.range(0, itemCount)
+            .boxed()
+            .collect(Guavate.toImmutableList());
+
+        Stream<Integer> result = CompletableFutureUtil.chainAll(ints.stream(),
+            i -> CompletableFuture.supplyAsync(() -> i, executorService))
+            .join();
+
+        assertThat(result.collect(Guavate.toImmutableList()))
+            .containsExactlyElementsOf(ints);
+    }
+
+    @Test
+    public void allOfShouldUnboxStream() {
+        long value1 = 18L;
+        long value2 = 19L;
+        long value3 = 20L;
+        assertThat(
+            CompletableFutureUtil.allOf(
+                Stream.of(
+                    CompletableFuture.completedFuture(value1),
+                    CompletableFuture.completedFuture(value2),
+                    CompletableFuture.completedFuture(value3)))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsOnly(value1, value2, value3);
+    }
+
+    @Test
+    public void allOfShouldPreserveOrder() {
+        long value1 = 18L;
+        long value2 = 19L;
+        long value3 = 20L;
+        long value4 = 21L;
+        long value5 = 22L;
+        long value6 = 23L;
+        long value7 = 24L;
+        long value8 = 25L;
+        long value9 = 26L;
+        long value10 = 27L;
+        assertThat(
+            CompletableFutureUtil.allOf(
+                Stream.of(
+                    CompletableFuture.completedFuture(value1),
+                    CompletableFuture.completedFuture(value2),
+                    CompletableFuture.completedFuture(value3),
+                    CompletableFuture.completedFuture(value4),
+                    CompletableFuture.completedFuture(value5),
+                    CompletableFuture.completedFuture(value6),
+                    CompletableFuture.completedFuture(value7),
+                    CompletableFuture.completedFuture(value8),
+                    CompletableFuture.completedFuture(value9),
+                    CompletableFuture.completedFuture(value10)))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10);
+    }
+
+    @Test
+    public void allOfArrayShouldPreserveOrder() {
+        long value1 = 18L;
+        long value2 = 19L;
+        long value3 = 20L;
+        long value4 = 21L;
+        long value5 = 22L;
+        long value6 = 23L;
+        long value7 = 24L;
+        long value8 = 25L;
+        long value9 = 26L;
+        long value10 = 27L;
+        assertThat(
+            CompletableFutureUtil.allOfArray(
+                    CompletableFuture.completedFuture(value1),
+                    CompletableFuture.completedFuture(value2),
+                    CompletableFuture.completedFuture(value3),
+                    CompletableFuture.completedFuture(value4),
+                    CompletableFuture.completedFuture(value5),
+                    CompletableFuture.completedFuture(value6),
+                    CompletableFuture.completedFuture(value7),
+                    CompletableFuture.completedFuture(value8),
+                    CompletableFuture.completedFuture(value9),
+                    CompletableFuture.completedFuture(value10))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(value1, value2, value3, value4, value5, value6, value7, value8, value9, value10);
+    }
+
+    @Test
+    public void allOfArrayShouldUnboxNoArgs() {
+        assertThat(
+            CompletableFutureUtil.allOfArray()
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void allOfArrayShouldUnboxArray() {
+        long value1 = 18L;
+        long value2 = 19L;
+        long value3 = 20L;
+        assertThat(
+            CompletableFutureUtil.allOfArray(
+                    CompletableFuture.completedFuture(value1),
+                    CompletableFuture.completedFuture(value2),
+                    CompletableFuture.completedFuture(value3))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsOnly(value1, value2, value3);
+    }
+
+    @Test
+    public void allOfShouldWorkOnVeryLargeStream() {
+        CompletableFutureUtil.allOf(
+            IntStream.range(0, 100000)
+                .boxed()
+                .map(CompletableFuture::completedFuture))
+            .join();
+    }
+
+    @Test
+    public void mapShouldMapOnStreamInsideACompletableFuturOfStream() {
+        CompletableFuture<Stream<Integer>> futurOfInteger = CompletableFuture.completedFuture(Stream.of(1, 2, 3));
+
+        assertThat(
+            CompletableFutureUtil.map(futurOfInteger, integer ->
+                integer * 2)
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(2, 4, 6);
+    }
+
+    @Test
+    public void mapShouldReturnEmptyStreamWhenGivenAnEmptyStream() {
+        CompletableFuture<Stream<Integer>> futurOfInteger = CompletableFuture.completedFuture(Stream.of());
+
+        assertThat(
+            CompletableFutureUtil.map(futurOfInteger, integer ->
+                integer * 2)
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void thenComposeOnAllShouldMapOnStreamInsideACompletableFuturOfStreamAndTransformTheResultingStreamOfCompletableFutureIntoACompletableOfStreamAndFlatIt() {
+        CompletableFuture<Stream<Integer>> futurOfInteger = CompletableFuture.completedFuture(Stream.of(1, 2, 3));
+
+        assertThat(
+            CompletableFutureUtil.thenComposeOnAll(futurOfInteger, integer ->
+                CompletableFuture.completedFuture(integer * 2))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(2, 4, 6);
+    }
+
+    @Test
+    public void thenComposeOnAllOnEmptyStreamShouldReturnAnEmptyStream() {
+        CompletableFuture<Stream<Integer>> futurOfInteger = CompletableFuture.completedFuture(Stream.of());
+
+        assertThat(
+            CompletableFutureUtil.thenComposeOnAll(futurOfInteger, integer ->
+                CompletableFuture.completedFuture(integer * 2))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void keepValueShouldCompleteWhenTheGivenCompletableFutureEnd() {
+        final AtomicInteger numOfFutureExecution = new AtomicInteger(0);
+
+        Supplier<CompletableFuture<Void>> future = () ->
+            CompletableFuture.runAsync(numOfFutureExecution::incrementAndGet);
+
+        assertThat(
+            CompletableFutureUtil.keepValue(future, 42)
+                .join())
+            .isEqualTo(42);
+
+        assertThat(
+            numOfFutureExecution.get())
+            .isEqualTo(1);
+    }
+
+    @Test
+    public void keepValueShouldReturnNullWithNullValue() {
+        Supplier<CompletableFuture<Void>> future = () ->
+            CompletableFuture.completedFuture(null);
+
+        assertThat(
+            CompletableFutureUtil.keepValue(future, null)
+                .join())
+            .isNull();
+    }
+
+    @Test
+    public void composeIfTrueShouldReturnTrueWhenTrue() {
+        assertThat(
+            CompletableFutureUtil.composeIfTrue(() -> CompletableFuture.completedFuture(null))
+                .apply(true)
+                .join())
+            .isTrue();
+    }
+
+    @Test
+    public void composeIfTrueShouldReturnFalseWhenFalse() {
+        assertThat(
+            CompletableFutureUtil.composeIfTrue(() -> CompletableFuture.completedFuture(null))
+                .apply(false)
+                .join())
+            .isFalse();
+    }
+
+    @Test
+    public void composeIfTrueShouldComposeWhenTrue() {
+        AtomicInteger atomicInteger = new AtomicInteger(0);
+        CompletableFutureUtil.composeIfTrue(() -> {
+            atomicInteger.incrementAndGet();
+            return CompletableFuture.completedFuture(null);
+        })
+            .apply(true)
+            .join();
+
+        assertThat(atomicInteger.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void composeIfTrueShouldNotComposeWhenFalse() {
+        AtomicInteger atomicInteger = new AtomicInteger(0);
+        CompletableFutureUtil.composeIfTrue(() -> {
+            atomicInteger.incrementAndGet();
+            return CompletableFuture.completedFuture(null);
+        })
+            .apply(false)
+            .join();
+
+        assertThat(atomicInteger.get()).isEqualTo(0);
+    }
+
+    @Test
+    public void reduceShouldReturnEmptyWhenNoValue() {
+        assertThat(
+            CompletableFutureUtil.reduce(
+                (i, j) -> i + j,
+                CompletableFutureUtil.<Long>allOfArray())
+                .join())
+            .isEmpty();
+    }
+
+    @Test
+    public void reduceShouldWork() {
+        assertThat(
+            CompletableFutureUtil.reduce(
+                (i, j) -> i + j,
+                CompletableFutureUtil.allOfArray(
+                    CompletableFuture.completedFuture(1L),
+                    CompletableFuture.completedFuture(2L),
+                    CompletableFuture.completedFuture(3L)
+                ))
+                .join())
+            .contains(6L);
+    }
+
+    @Test
+    public void reduceShouldReturnIdentityAccumulatorWhenNoValue() {
+        long identityAccumulator = 0L;
+        assertThat(
+            CompletableFutureUtil.reduce(
+                (i, j) -> i + j,
+                CompletableFutureUtil.<Long>allOfArray(),
+                identityAccumulator)
+                .join())
+            .isEqualTo(identityAccumulator);
+    }
+
+    @Test
+    public void reduceShouldWorkWithIdentityAccumulator() {
+        assertThat(
+            CompletableFutureUtil.reduce(
+                (i, j) -> i + j,
+                CompletableFutureUtil.allOfArray(
+                    CompletableFuture.completedFuture(1L),
+                    CompletableFuture.completedFuture(2L),
+                    CompletableFuture.completedFuture(3L)
+                ),
+                0L)
+                .join())
+            .isEqualTo(6L);
+    }
+
+    @Test
+    public void unwrapShouldUnwrapWhenValue() {
+        assertThat(
+            CompletableFutureUtil.unwrap(
+                    CompletableFuture.completedFuture(Optional.of(CompletableFuture.completedFuture(1L))))
+                .join())
+            .isEqualTo(Optional.of(1L));
+    }
+
+    @Test
+    public void unwrapShouldUnwrapWhenEmpty() {
+        assertThat(
+            CompletableFutureUtil.unwrap(
+                    CompletableFuture.completedFuture(Optional.empty()))
+                .join())
+            .isEmpty();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/FluentFutureStreamTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/FluentFutureStreamTest.java b/server/container/util/src/test/java/org/apache/james/util/FluentFutureStreamTest.java
new file mode 100644
index 0000000..0877414
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/FluentFutureStreamTest.java
@@ -0,0 +1,258 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+
+import com.github.steveash.guavate.Guavate;
+
+public class FluentFutureStreamTest {
+
+    @Test
+    public void ofFutureShouldConstructAFluentFutureStream() {
+        assertThat(
+            FluentFutureStream.ofFutures(
+                CompletableFuture.completedFuture(1),
+                CompletableFuture.completedFuture(2),
+                CompletableFuture.completedFuture(3))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void ofShouldConstructAFluentFutureStreamWhenProvidedAFutureOfStream() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void ofShouldConstructAFluentFutureStreamWhenProvidedAStreamOfFuture() {
+        assertThat(
+            FluentFutureStream.of(
+                Stream.of(
+                    CompletableFuture.completedFuture(1),
+                    CompletableFuture.completedFuture(2),
+                    CompletableFuture.completedFuture(3)))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void ofNestedStreamsShouldConstructAFluentFutureStreamWhenProvidedAStreamOfFutureOfStream() {
+        assertThat(
+            FluentFutureStream.ofNestedStreams(
+                Stream.of(
+                    CompletableFuture.completedFuture(Stream.of(1, 2)),
+                    CompletableFuture.completedFuture(Stream.of()),
+                    CompletableFuture.completedFuture(Stream.of(3))))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 3);
+    }
+
+
+    @Test
+    public void ofOptionalsShouldConstructAFluentFutureStreamWhenProvidedAStreamOfFutureOfOptionals() {
+        assertThat(
+            FluentFutureStream.ofOptionals(
+                Stream.of(
+                    CompletableFuture.completedFuture(Optional.of(1)),
+                    CompletableFuture.completedFuture(Optional.of(2)),
+                    CompletableFuture.completedFuture(Optional.empty()),
+                    CompletableFuture.completedFuture(Optional.of(3))))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void completableFutureShouldReturnAFutureOfTheUnderLayingStream() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .completableFuture()
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void mapShouldTransformUnderlyingValues() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .map(i -> i + 1)
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(2, 3, 4);
+    }
+
+    @Test
+    public void flatMapShouldTransformUnderlyingValuesAndFlatMapResult() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .flatMap(i -> Stream.of(i, i + 1))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 2, 3, 3, 4);
+    }
+
+    @Test
+    public void flatMapOptionalShouldTransformUnderlyingValuesAndUnboxResult() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .flatMapOptional(i -> Optional.of(i + 1)
+                    .filter(j -> j % 2 == 0))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(2, 4);
+    }
+
+    @Test
+    public void reduceShouldGatherAllValuesOfTheUnderlyingStream() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .reduce((a, b) -> a + b)
+                .join())
+            .contains(6);
+    }
+
+    @Test
+    public void reduceShouldGatherAllValuesOfTheUnderlyingStreamWithAnEmptyValue() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .reduce(0, (a, b) -> a + b)
+                .join())
+            .isEqualTo(6);
+    }
+
+    @Test
+    public void filterShouldBeAppliedOnTheUnderlyingStream() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .filter(i -> i % 2 == 1)
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 3);
+    }
+
+    @Test
+    public void thenComposeOnAllShouldTransformUnderlyingValuesAndComposeFutures() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .thenComposeOnAll(i -> CompletableFuture.completedFuture(i + 1))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(2, 3, 4);
+    }
+
+    @Test
+    public void thenFlatComposeShouldTransformUnderlyingValuesAndComposeFuturesWithStreamUnboxing() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .thenFlatCompose(i -> CompletableFuture.completedFuture(Stream.of(i, i + 1)))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 2, 3, 3, 4);
+    }
+
+    @Test
+    public void thenFlatComposeOnOptionalShouldTransformUnderlyingValuesAndComposeFuturesWithOptionalUnboxing() {
+        assertThat(
+            FluentFutureStream.of(
+                CompletableFuture.completedFuture(
+                    Stream.of(1, 2, 3)))
+                .thenFlatComposeOnOptional(i -> CompletableFuture.completedFuture(Optional.of(i + 1)
+                    .filter(j -> j % 2 == 0)))
+                .join()
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(2, 4);
+    }
+
+    @Test
+    public void thenPerformOnAllShouldGenerateASynchronousSideEffectForAllElementsOfTheUnderlyingStream() {
+        ConcurrentLinkedDeque<Integer> sideEffects = new ConcurrentLinkedDeque<>();
+
+        FluentFutureStream.of(
+            CompletableFuture.completedFuture(
+                Stream.of(1, 2, 3)))
+            .performOnAll(i -> {
+                sideEffects.addLast(i);
+                return CompletableFuture.completedFuture(null);
+            })
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(sideEffects).containsOnly(1, 2, 3);
+    }
+
+    @Test
+    public void collectShouldReturnTheCollectionOfData() {
+        assertThat(
+            FluentFutureStream.of(
+                Stream.of(
+                    CompletableFuture.completedFuture(1),
+                    CompletableFuture.completedFuture(2),
+                    CompletableFuture.completedFuture(3)))
+                .collect(Guavate.toImmutableList())
+                .join())
+            .containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void collectShouldReturnEmptyWhenStreamIsEmpty() {
+        assertThat(
+            FluentFutureStream.ofFutures()
+                .collect(Guavate.toImmutableList())
+                .join())
+            .isEmpty();
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/GuavaUtilsTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/GuavaUtilsTest.java b/server/container/util/src/test/java/org/apache/james/util/GuavaUtilsTest.java
new file mode 100644
index 0000000..2d5a224
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/GuavaUtilsTest.java
@@ -0,0 +1,81 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+
+public class GuavaUtilsTest {
+
+    @Test
+    public void toMultimapShouldAcceptEmptyMaps() {
+        assertThat(GuavaUtils.toMultimap(ImmutableMap
+            .<String, List<String>>builder()
+            .build())
+            .asMap())
+            .isEqualTo(ImmutableMap.of());
+    }
+
+    @Test
+    public void toMultimapShouldAcceptSingleValuesMaps() {
+        assertThat(GuavaUtils.toMultimap(ImmutableMap
+            .<String, List<String>>builder()
+            .put("k1", ImmutableList.of("v1"))
+            .put("k2", ImmutableList.of("v2"))
+            .build())
+            .asMap())
+            .isEqualTo(ImmutableListMultimap.of(
+                "k1", "v1",
+                "k2", "v2")
+            .asMap());
+    }
+
+    @Test
+    public void toMultimapShouldAcceptMultiplesValuesMaps() {
+        assertThat(GuavaUtils.toMultimap(ImmutableMap
+            .<String, List<String>>builder()
+            .put("k1", ImmutableList.of("v1"))
+            .put("k2", ImmutableList.of("v2", "v2.1"))
+            .build())
+            .asMap())
+            .isEqualTo(ImmutableListMultimap.of(
+                "k1", "v1",
+                "k2", "v2",
+                "k2", "v2.1")
+                .asMap());
+    }
+
+    @Test
+    public void shouldStripEntriesWithEmptyList() {
+        assertThat(GuavaUtils.toMultimap(ImmutableMap
+            .<String, List<String>>builder()
+            .put("k1", ImmutableList.of())
+            .build())
+            .asMap())
+            .isEqualTo(ImmutableListMultimap.of().asMap());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/HostTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/HostTest.java b/server/container/util/src/test/java/org/apache/james/util/HostTest.java
new file mode 100644
index 0000000..3ebeaee
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/HostTest.java
@@ -0,0 +1,219 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class HostTest {
+
+    private static final int DEFAULT_PORT = 154;
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void parseConfStringShouldParseConfWithIpAndPort() {
+        //Given
+        int expectedPort = 44;
+        String expectedIp = "142.145.254.111";
+        String ipAndPort = expectedIp + ":" + 44;
+
+        //When
+        Host actual = Host.parseConfString(ipAndPort);
+
+        //Then
+        assertThat(actual).isEqualTo(new Host(expectedIp, expectedPort));
+    }
+
+    @Test
+    public void parseConfStringShouldParseConfWithHostanmeAndPort() {
+        int expectedPort = 44;
+        String host = "host";
+
+        Host actual = Host.parseConfString(host + ":" + expectedPort);
+
+        assertThat(actual).isEqualTo(new Host(host, expectedPort));
+    }
+
+    @Test
+    public void parseConfStringShouldParseConfWithHostOnlyWhenDefaultPortIsProvided() {
+        //Given
+        String ipAndPort = "142.145.254.111";
+        String expectedIp = "142.145.254.111";
+
+        //When
+        Host actual = Host.parseConfString(ipAndPort, DEFAULT_PORT);
+
+        //Then
+        assertThat(actual).isEqualTo(new Host(expectedIp, DEFAULT_PORT));
+    }
+
+    @Test
+    public void parseConfStringShouldFailWhenConfigIsAnEmptyString() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        //Given
+        String ipAndPort = "";
+
+        //When
+        Host.parseConfString(ipAndPort);
+    }
+
+    @Test
+    public void parseConfStringShouldFailWhenOnlyHostnameAndNoDefaultPort() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        //Given
+        String hostname = "hostnameOnly";
+
+        //When
+        Host.parseConfString(hostname);
+    }
+
+    @Test
+    public void parseConfStringShouldFailWhenNegativePort() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        Host.parseConfString("host:-1");
+    }
+
+    @Test
+    public void parseConfStringShouldFailWhenZeroPort() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        Host.parseConfString("host:0");
+    }
+
+    @Test
+    public void parseConfStringShouldFailWhenTooHighPort() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        Host.parseConfString("host:65536");
+    }
+
+    @Test
+    public void parseConfStringShouldFailWhenConfigIsANullString() {
+        expectedException.expect(NullPointerException.class);
+
+        //Given
+        String ipAndPort = null;
+
+        //When
+        Host.parseConfString(ipAndPort);
+    }
+
+
+    @Test
+    public void parseConfStringShouldFailWhenConfigIsInvalid() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        //Given
+        String ipAndPort = "10.10.10.10:42:43";
+
+        //When
+        Host.parseConfString(ipAndPort);
+    }
+
+    @Test
+    public void parseHostsShouldParseEmptyString() {
+        assertThat(Host.parseHosts(""))
+            .isEmpty();
+    }
+
+    @Test
+    public void parseHostsShouldParseMonoHost() {
+        assertThat(Host.parseHosts("localhost:9200"))
+            .containsOnly(new Host("localhost", 9200));
+    }
+
+    @Test
+    public void parseHostsShouldParseMultiHosts() {
+        assertThat(Host.parseHosts("localhost:9200,server:9155"))
+            .containsOnly(
+                new Host("localhost", 9200),
+                new Host("server", 9155));
+    }
+
+    @Test
+    public void parseHostsShouldNotFailOnMultiComma() {
+        assertThat(Host.parseHosts("localhost:9200,,server:9155"))
+            .containsOnly(
+                new Host("localhost", 9200),
+                new Host("server", 9155));
+    }
+
+    @Test
+    public void parseHostsShouldFailOnInvalidHost() {
+        expectedException.expect(NumberFormatException.class);
+
+        Host.parseHosts("localhost:invalid,,server:9155");
+    }
+
+    @Test
+    public void parseHostsShouldSwallowDuplicates() {
+        assertThat(Host.parseHosts("localhost:9200,localhost:9200"))
+            .containsOnly(
+                new Host("localhost", 9200));
+    }
+
+    @Test
+    public void parseHostsShouldNotSwallowSameAddressDifferentPort() {
+        assertThat(Host.parseHosts("localhost:9200,localhost:9155"))
+            .containsOnly(
+                new Host("localhost", 9200),
+                new Host("localhost", 9155));
+    }
+
+    @Test
+    public void parseHostsShouldNotSwallowSamePortDifferentAddress() {
+        assertThat(Host.parseHosts("localhost:9200,abcd:9200"))
+            .containsOnly(
+                new Host("localhost", 9200),
+                new Host("abcd", 9200));
+    }
+
+    @Test
+    public void parseHostsShouldHandleDefaultPort() {
+        int defaultPort = 155;
+
+        assertThat(Host.parseHosts("localhost:9200,abcd", defaultPort))
+            .containsOnly(
+                new Host("localhost", 9200),
+                new Host("abcd", 155));
+    }
+
+    @Test
+    public void parseHostsShouldThrowOnAbsentPortWhenNoDefaultPort() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        Host.parseHosts("localhost:9200,abcd");
+    }
+
+    @Test
+    public void hostShouldRespectBeanContract() {
+        EqualsVerifier.forClass(Host.class).verify();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/MDCBuilderTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/MDCBuilderTest.java b/server/container/util/src/test/java/org/apache/james/util/MDCBuilderTest.java
new file mode 100644
index 0000000..bf09d1d
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/MDCBuilderTest.java
@@ -0,0 +1,139 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.google.common.collect.ImmutableList;
+
+public class MDCBuilderTest {
+
+    private static final String KEY_1 = "key1";
+    private static final String KEY_2 = "key2";
+    private static final String VALUE_1 = "value1";
+    private static final String VALUE_2 = "value2";
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void addContextShouldThrowOnNullKey() {
+        expectedException.expect(NullPointerException.class);
+
+        MDCBuilder.create()
+            .addContext(null, "any");
+    }
+
+    @Test
+    public void buildContextMapShouldReturnEmptyWhenNoContext() {
+        assertThat(MDCBuilder.create().buildContextMap())
+            .isEmpty();
+    }
+
+    @Test
+    public void buildContextMapShouldReturnContext() {
+        assertThat(
+            MDCBuilder.create()
+                .addContext(KEY_1, VALUE_1)
+                .addContext(KEY_2, VALUE_2)
+                .buildContextMap())
+            .containsOnlyKeys(KEY_1, KEY_2)
+            .containsEntry(KEY_1, VALUE_1)
+            .containsEntry(KEY_2, VALUE_2);
+    }
+
+    @Test
+    public void addContextShouldFilterOutNullValues() {
+        assertThat(
+            MDCBuilder.create()
+                .addContext(KEY_1, null)
+                .buildContextMap())
+            .isEmpty();
+    }
+
+    @Test
+    public void addContextShouldAllowRecursiveBuild() {
+        assertThat(
+            MDCBuilder.create()
+                .addContext(KEY_1, VALUE_1)
+                .addContext(MDCBuilder.create()
+                    .addContext(KEY_2, VALUE_2))
+                .buildContextMap())
+            .containsOnlyKeys(KEY_1, KEY_2)
+            .containsEntry(KEY_1, VALUE_1)
+            .containsEntry(KEY_2, VALUE_2);
+    }
+
+    @SuppressWarnings("resource")
+    @Test
+    public void closeablesConstructorShouldThrowOnNullList() {
+        expectedException.expect(NullPointerException.class);
+
+        new MDCBuilder.Closeables(null);
+    }
+
+    @Test
+    public void closeablesCloseShouldNotThrowWhenEmpty() throws IOException {
+        new MDCBuilder.Closeables(ImmutableList.of())
+            .close();
+    }
+
+    @Test
+    public void closeablesCloseShouldCallAllUnderlyingCloseables() throws IOException {
+        ImmutableList.Builder<String> builder = ImmutableList.builder();
+
+        Closeable closeable1 = () -> builder.add(VALUE_1);
+        Closeable closeable2 = () -> builder.add(VALUE_2);
+
+        new MDCBuilder.Closeables(
+            ImmutableList.of(closeable1, closeable2))
+            .close();
+
+        assertThat(builder.build())
+            .containsExactly(VALUE_1, VALUE_2);
+    }
+
+
+    @Test
+    public void closeablesCloseShouldCallAllUnderlyingCloseablesWhenError() throws IOException {
+        ImmutableList.Builder<String> builder = ImmutableList.builder();
+
+        Closeable closeable1 = () -> builder.add(VALUE_1);
+        Closeable closeable2 = () -> {
+            throw new IOException();
+        };
+        Closeable closeable3 = () -> builder.add(VALUE_2);
+
+        new MDCBuilder.Closeables(
+            ImmutableList.of(closeable1, closeable2, closeable3))
+            .close();
+
+        assertThat(builder.build())
+            .containsExactly(VALUE_1, VALUE_2);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/MemoizedSupplierTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/MemoizedSupplierTest.java b/server/container/util/src/test/java/org/apache/james/util/MemoizedSupplierTest.java
new file mode 100644
index 0000000..e774354
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/MemoizedSupplierTest.java
@@ -0,0 +1,84 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
+import org.junit.Test;
+
+public class MemoizedSupplierTest {
+
+    @Test
+    public void getShouldReturnSuppliedValue() {
+        Supplier<Integer> supplier = MemoizedSupplier.of(() -> 42);
+
+        assertThat(supplier.get()).isEqualTo(42);
+    }
+
+    @Test
+    public void getShouldBeIdempotent() {
+        Supplier<Integer> supplier = MemoizedSupplier.of(() -> 42);
+
+        supplier.get();
+        assertThat(supplier.get()).isEqualTo(42);
+    }
+
+    @Test
+    public void nullValueShouldBeSupported() {
+        Supplier<Integer> supplier = MemoizedSupplier.of(() -> null);
+
+        supplier.get();
+        assertThat(supplier.get()).isNull();
+    }
+
+    @Test
+    public void underlyingSupplierShouldBeCalledOnlyOnce() {
+        AtomicInteger atomicInteger = new AtomicInteger(0);
+
+        Supplier<Integer> supplier = MemoizedSupplier.of(() -> {
+            atomicInteger.incrementAndGet();
+            return 42;
+        });
+
+        supplier.get();
+        supplier.get();
+
+        assertThat(atomicInteger.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void underlyingSupplierShouldBeCalledOnlyOnceWhenReturningNullValue() {
+        AtomicInteger atomicInteger = new AtomicInteger(0);
+
+        Supplier<Integer> supplier = MemoizedSupplier.of(() -> {
+            atomicInteger.incrementAndGet();
+            return null;
+        });
+
+        supplier.get();
+        supplier.get();
+
+        assertThat(atomicInteger.get()).isEqualTo(1);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java b/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
new file mode 100644
index 0000000..17fe06e
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/OptionalUtilsTest.java
@@ -0,0 +1,243 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.github.steveash.guavate.Guavate;
+
+public class OptionalUtilsTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void ifEmptyShouldPreserveValueOfEmptyOptionals() {
+        Optional<Object> expected = OptionalUtils.executeIfEmpty(Optional.empty(), () -> { });
+
+        assertThat(expected).isEmpty();
+    }
+
+    @Test
+    public void ifEmptyShouldPreserveValueOfPresentOptionals() {
+        String value = "value";
+        Optional<String> expected = OptionalUtils.executeIfEmpty(Optional.of(value), () -> { });
+
+        assertThat(expected).contains(value);
+    }
+
+    @Test
+    public void ifEmptyShouldPerformOperationIfEmpty() {
+        AtomicInteger operationCounter = new AtomicInteger(0);
+
+        OptionalUtils.executeIfEmpty(Optional.empty(), operationCounter::incrementAndGet);
+
+        assertThat(operationCounter.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void ifEmptyShouldNotPerformOperationIfPresent() {
+        AtomicInteger operationCounter = new AtomicInteger(0);
+
+        OptionalUtils.executeIfEmpty(Optional.of("value"), operationCounter::incrementAndGet);
+
+        assertThat(operationCounter.get()).isEqualTo(0);
+    }
+
+    @Test
+    public void toStreamShouldConvertEmptyOptionalToEmptyStream() {
+        assertThat(
+            OptionalUtils.toStream(Optional.empty())
+                .collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void toStreamShouldConvertFullOptionalToStream() {
+        long value = 18L;
+        assertThat(
+            OptionalUtils.toStream(Optional.of(value))
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(value);
+    }
+
+    @Test
+    public void orShouldReturnEmptyWhenNoParameter() {
+        assertThat(OptionalUtils.or())
+            .isEmpty();
+    }
+
+    @Test
+    public void orShouldReturnEmptyWhenEmpty() {
+        assertThat(
+            OptionalUtils.or(
+                Optional.empty()))
+            .isEmpty();
+    }
+
+    @Test
+    public void orShouldReturnValueWhenValue() {
+        assertThat(
+            OptionalUtils.or(
+                Optional.of(1)))
+            .contains(1);
+    }
+
+    @Test
+    public void orShouldReturnEmptyWhenBothEmpty() {
+        assertThat(
+            OptionalUtils.or(
+                Optional.empty(),
+                Optional.empty()))
+            .isEmpty();
+    }
+
+    @Test
+    public void orShouldReturnFirstValueWhenOnlyFirstValue() {
+        assertThat(
+            OptionalUtils.or(
+                Optional.of(18),
+                Optional.empty()))
+            .contains(18);
+    }
+
+    @Test
+    public void orShouldReturnSecondValueWhenOnlySecondValue() {
+        assertThat(
+            OptionalUtils.or(
+                Optional.empty(),
+                Optional.of(18)))
+            .contains(18);
+    }
+
+    @Test
+    public void orShouldReturnFirstValueWhenBothValues() {
+        assertThat(
+            OptionalUtils.or(
+                Optional.of(1),
+                Optional.of(2)))
+            .contains(1);
+    }
+
+    @Test
+    public void orShouldReturnThirdValueWhenOnlyThirdValue() {
+        assertThat(
+            OptionalUtils.or(
+                Optional.empty(),
+                Optional.empty(),
+                Optional.of(1)))
+            .contains(1);
+    }
+
+    @Test
+    public void orSuppliersShouldReturnEmptyWhenNoParameter() {
+        assertThat(OptionalUtils.or())
+            .isEmpty();
+    }
+
+    @Test
+    public void orSuppliersShouldReturnEmptyWhenEmpty() {
+        assertThat(
+            OptionalUtils.orSuppliers(
+                Optional::empty))
+            .isEmpty();
+    }
+
+    @Test
+    public void orSuppliersShouldReturnValueWhenValue() {
+        assertThat(
+            OptionalUtils.orSuppliers(
+                () -> Optional.of(1)))
+            .contains(1);
+    }
+
+    @Test
+    public void orSuppliersShouldReturnEmptyWhenBothEmpty() {
+        assertThat(
+            OptionalUtils.orSuppliers(
+                () -> Optional.empty(),
+                () -> Optional.empty()))
+            .isEmpty();
+    }
+
+    @Test
+    public void orSuppliersShouldReturnFirstValueWhenOnlyFirstValue() {
+        assertThat(
+            OptionalUtils.orSuppliers(
+                () -> Optional.of(18),
+                Optional::empty))
+            .contains(18);
+    }
+
+    @Test
+    public void orSuppliersShouldReturnSecondValueWhenOnlySecondValue() {
+        assertThat(
+            OptionalUtils.orSuppliers(
+                Optional::empty,
+                () -> Optional.of(18)))
+            .contains(18);
+    }
+
+    @Test
+    public void orSuppliersShouldReturnFirstValueWhenBothValues() {
+        assertThat(
+            OptionalUtils.orSuppliers(
+                () -> Optional.of(1),
+                () -> Optional.of(2)))
+            .contains(1);
+    }
+
+    @Test
+    public void orSuppliersShouldReturnThirdValueWhenOnlyThirdValue() {
+        assertThat(
+            OptionalUtils.orSuppliers(
+                Optional::empty,
+                Optional::empty,
+                () -> Optional.of(1)))
+            .contains(1);
+    }
+
+    @Test
+    public void containsDifferentShouldReturnTrueWhenNullStoreValue() throws Exception {
+        assertThat(OptionalUtils.containsDifferent(Optional.of("any"), null)).isTrue();
+    }
+
+    @Test
+    public void containsDifferentShouldReturnFalseWhenEmpty() throws Exception {
+        assertThat(OptionalUtils.containsDifferent(Optional.empty(), "any")).isFalse();
+    }
+
+    @Test
+    public void containsDifferentShouldReturnFalseWhenSameValue() throws Exception {
+        assertThat(OptionalUtils.containsDifferent(Optional.of("any"), "any")).isFalse();
+    }
+
+    @Test
+    public void containsDifferentShouldReturnTrueWhenDifferentValue() throws Exception {
+        assertThat(OptionalUtils.containsDifferent(Optional.of("any"), "other")).isTrue();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/PortTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/PortTest.java b/server/container/util/src/test/java/org/apache/james/util/PortTest.java
new file mode 100644
index 0000000..9d04204
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/PortTest.java
@@ -0,0 +1,92 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.Test;
+
+public class PortTest {
+    @Test
+    public void assertValidShouldThrowOnNegativePort() {
+        assertThatThrownBy(() -> Port.assertValid(-1))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void assertValidShouldThrowOnZeroPort() {
+        assertThatThrownBy(() -> Port.assertValid(0))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void assertValidShouldAcceptOne() {
+        Port.assertValid(1);
+    }
+
+    @Test
+    public void assertValidShouldAcceptMaxValue() {
+        Port.assertValid(Port.MAX_PORT_VALUE);
+    }
+
+    @Test
+    public void assertValidShouldThrowOnTooBigValue() {
+        assertThatThrownBy(() -> Port.assertValid(Port.MAX_PORT_VALUE + 1))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void isValidShouldReturnFalseWhenNegative() {
+        assertThat(Port.isValid(-1))
+            .isFalse();
+    }
+
+    @Test
+    public void isValidShouldReturnFalseWhenZero() {
+        assertThat(Port.isValid(0))
+            .isFalse();
+    }
+
+    @Test
+    public void isValidShouldReturnTrueWhenOne() {
+        assertThat(Port.isValid(1))
+            .isTrue();
+    }
+
+    @Test
+    public void isValidShouldReturnTrueWhenMaxValue() {
+        assertThat(Port.isValid(Port.MAX_PORT_VALUE))
+            .isTrue();
+    }
+
+    @Test
+    public void isValidShouldReturnFalseWhenAboveMaxValue() {
+        assertThat(Port.isValid(Port.MAX_PORT_VALUE + 1))
+            .isFalse();
+    }
+
+    @Test
+    public void generateValidUnprivilegedPortShouldReturnAValidPort() {
+        assertThat(Port.generateValidUnprivilegedPort())
+            .isBetween(Port.PRIVILEGED_PORT_BOUND, Port.MAX_PORT_VALUE);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/StreamUtilsTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/StreamUtilsTest.java b/server/container/util/src/test/java/org/apache/james/util/StreamUtilsTest.java
new file mode 100644
index 0000000..691c671
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/StreamUtilsTest.java
@@ -0,0 +1,115 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.stream.Stream;
+
+import org.junit.Test;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ImmutableList;
+
+public class StreamUtilsTest {
+
+    @Test
+    public void flattenShouldReturnEmptyWhenEmptyStreams() {
+        assertThat(
+            StreamUtils.<Integer>flatten(ImmutableList.of())
+                .collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void flattenShouldPreserveSingleStreams() {
+        assertThat(
+            StreamUtils.flatten(ImmutableList.of(
+                Stream.of(1, 2, 3)))
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void flattenShouldMergeSeveralStreamsTogether() {
+        assertThat(
+            StreamUtils.flatten(ImmutableList.of(
+                Stream.of(1, 2, 3),
+                Stream.of(4, 5)))
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 3, 4, 5);
+    }
+
+    @Test
+    public void flattenShouldAcceptEmptyStreams() {
+        assertThat(
+            StreamUtils.flatten(ImmutableList.of(
+                Stream.of()))
+                .collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void flattenShouldMergeEmptyStreamsWithOtherData() {
+        assertThat(
+            StreamUtils.flatten(ImmutableList.of(
+                Stream.of(1, 2),
+                Stream.of(),
+                Stream.of(3)))
+                .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void flattenShouldAcceptEmptyVarArg() {
+        assertThat(
+            StreamUtils.flatten()
+                .collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void flattenShouldThrowOnNullVarArg() {
+        Stream<String>[] streams = null;
+        assertThatThrownBy(() -> StreamUtils.flatten(streams).collect(Guavate.toImmutableList()))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void flattenShouldFlattenNonEmptyVarArg() {
+        assertThat(StreamUtils.flatten(Stream.of(1), Stream.of(2)).collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2);
+    }
+
+    @Test
+    public void ofNullableShouldReturnEmptyStreamWhenNull() {
+        assertThat(StreamUtils.ofNullable(null)
+            .collect(Guavate.toImmutableList()))
+            .isEmpty();
+    }
+
+    @Test
+    public void ofNullableShouldReturnAStreamWithElementsOfTheArray() {
+        assertThat(StreamUtils.ofNullable(ImmutableList.of(1, 2).toArray())
+            .collect(Guavate.toImmutableList()))
+            .containsExactly(1, 2);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/ValuePatchTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/ValuePatchTest.java b/server/container/util/src/test/java/org/apache/james/util/ValuePatchTest.java
new file mode 100644
index 0000000..ef84393
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/ValuePatchTest.java
@@ -0,0 +1,220 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 modifyTo 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 org.apache.james.util;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
+import org.junit.Test;
+
+public class ValuePatchTest {
+
+    public static final int REPLACEMENT_VALUE = 24;
+    public static final Optional<Integer> REPLACEMENT = Optional.of(REPLACEMENT_VALUE);
+    public static final int VALUE = 12;
+    public static final Optional<Integer> OPTIONAL_OF_VALUE = Optional.of(VALUE);
+
+    @Test
+    public void keepShouldProduceKeptValues() {
+        assertThat(ValuePatch.<Integer>keep().isKept()).isTrue();
+    }
+
+    @Test
+    public void keepShouldThrowOnGet() {
+        assertThatThrownBy(() -> ValuePatch.<Integer>keep().get()).isInstanceOf(NoSuchElementException.class);
+    }
+
+    @Test
+    public void keepShouldNotBeModified() {
+        assertThat(ValuePatch.<Integer>keep().isModified()).isFalse();
+    }
+
+    @Test
+    public void keepShouldNotBeRemoved() {
+        assertThat(ValuePatch.<Integer>keep().isRemoved()).isFalse();
+    }
+
+    @Test
+    public void removeShouldNotBeKept() {
+        assertThat(ValuePatch.<Integer>remove().isKept()).isFalse();
+    }
+
+    @Test
+    public void removeShouldBeRemoved() {
+        assertThat(ValuePatch.<Integer>remove().isRemoved()).isTrue();
+    }
+
+    @Test
+    public void removedShouldNotBeModified() {
+        assertThat(ValuePatch.<Integer>remove().isModified()).isFalse();
+    }
+
+    @Test
+    public void removeShouldThrowOnGet() {
+        assertThatThrownBy(() -> ValuePatch.<Integer>remove().get()).isInstanceOf(NoSuchElementException.class);
+    }
+
+    @Test
+    public void ofNullableShouldBeEquivalentToRemoveWhenNullParameter() {
+        assertThat(ValuePatch.<Integer>ofNullable(null)).isEqualTo(ValuePatch.<Integer>remove());
+    }
+
+    @Test
+    public void ofNullableShouldBeEquivalentToModifyWhenNonNullParameter() {
+        assertThat(ValuePatch.ofNullable(VALUE)).isEqualTo(ValuePatch.modifyTo(VALUE));
+    }
+
+    @Test
+    public void modifyToShouldNotBeKept() {
+        assertThat(ValuePatch.modifyTo(VALUE).isKept()).isFalse();
+    }
+
+    @Test
+    public void modifyToShouldNotBeRemoved() {
+        assertThat(ValuePatch.modifyTo(VALUE).isRemoved()).isFalse();
+    }
+
+    @Test
+    public void modifyToShouldBeModified() {
+        assertThat(ValuePatch.modifyTo(VALUE).isModified()).isTrue();
+    }
+
+    @Test
+    public void modifyToShouldThrowOnNullValue() {
+        assertThatThrownBy(() -> ValuePatch.modifyTo(null)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void modifyToShouldBeRetrievedByGet() {
+        assertThat(ValuePatch.modifyTo(VALUE).get()).isEqualTo(VALUE);
+    }
+
+    @Test
+    public void ofOptionalShouldThrowOnNullValue() {
+        assertThatThrownBy(() -> ValuePatch.ofOptional(null)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void ofOptionalShouldBeEquivalentToModifyToWhenPresent() {
+        assertThat(ValuePatch.ofOptional(OPTIONAL_OF_VALUE)).isEqualTo(ValuePatch.modifyTo(VALUE));
+    }
+
+    @Test
+    public void ofOptionalShouldBeEquivalentToRemoveWhenEmpty() {
+        assertThat(ValuePatch.ofOptional(Optional.empty())).isEqualTo(ValuePatch.remove());
+    }
+
+    @Test
+    public void notKeptOrElseShouldReturnElseWhenKept() {
+        assertThat(ValuePatch.<Integer>keep().notKeptOrElse(REPLACEMENT)).isEqualTo(REPLACEMENT);
+    }
+
+    @Test
+    public void notKeptOrElseShouldReturnEmptyWhenRemoved() {
+        assertThat(ValuePatch.<Integer>remove().notKeptOrElse(REPLACEMENT)).isEqualTo(Optional.empty());
+    }
+
+    @Test
+    public void notKeptOrElseShouldReturnOptionalWhenModified() {
+        assertThat(ValuePatch.modifyTo(VALUE).notKeptOrElse(REPLACEMENT)).isEqualTo(OPTIONAL_OF_VALUE);
+    }
+
+    @Test
+    public void toOptionalShouldReturnElseWhenKept() {
+        assertThat(ValuePatch.<Integer>keep().toOptional()).isEqualTo(Optional.empty());
+    }
+
+    @Test
+    public void toOptionalShouldReturnEmptyWhenRemoved() {
+        assertThat(ValuePatch.<Integer>remove().toOptional()).isEqualTo(Optional.empty());
+    }
+
+    @Test
+    public void toOptionalShouldReturnOptionalWhenModified() {
+        assertThat(ValuePatch.modifyTo(VALUE).toOptional()).isEqualTo(OPTIONAL_OF_VALUE);
+    }
+
+    @Test
+    public void getOrElseShouldReturnReplacementWhenKept() {
+        assertThat(ValuePatch.<Integer>keep().getOrElse(REPLACEMENT_VALUE)).isEqualTo(REPLACEMENT_VALUE);
+    }
+
+    @Test
+    public void getOrElseShouldReturnReplacementWhenRemoved() {
+        assertThat(ValuePatch.<Integer>remove().getOrElse(REPLACEMENT_VALUE)).isEqualTo(REPLACEMENT_VALUE);
+    }
+
+    @Test
+    public void getOrElseShouldReturnValueWhenPresent() {
+        assertThat(ValuePatch.modifyTo(VALUE).getOrElse(REPLACEMENT_VALUE)).isEqualTo(VALUE);
+    }
+
+    @Test
+    public void getOrElseShouldReturnNullWhenKeptAndNullSpecified() {
+        assertThat(ValuePatch.<Integer>keep().getOrElse(null)).isNull();
+    }
+
+    @Test
+    public void getOrElseShouldReturnNullWhenRemovedAndNullSpecified() {
+        assertThat(ValuePatch.<Integer>remove().getOrElse(null)).isNull();
+    }
+
+    @Test
+    public void getOrElseShouldReturnValueWhenPresentAndNullSpecified() {
+        assertThat(ValuePatch.modifyTo(VALUE).getOrElse(null)).isEqualTo(VALUE);
+    }
+
+    @Test
+    public void mapNotKeptToValueShouldPreserveKept() {
+        assertThat(
+            ValuePatch.<Integer>keep()
+                .mapNotKeptToOptional(optional -> optional.map(i -> i + 1).orElse(REPLACEMENT_VALUE)))
+            .isEmpty();
+    }
+
+    @Test
+    public void mapNotKeptToValueShouldTransformOf() {
+        assertThat(
+            ValuePatch.modifyTo(VALUE)
+                .mapNotKeptToOptional(optional -> optional.map(i -> i + 1).orElse(REPLACEMENT_VALUE)))
+            .contains(VALUE + 1);
+    }
+
+    @Test
+    public void mapNotKeptToValueShouldTransformRemoved() {
+        assertThat(
+            ValuePatch.<Integer>remove()
+                .mapNotKeptToOptional(optional -> optional.map(i -> i + 1).orElse(REPLACEMENT_VALUE)))
+            .contains(REPLACEMENT_VALUE);
+    }
+
+    @Test
+    public void mapNotKeptToValueShouldThrowWhenNull() {
+        assertThatThrownBy(
+            () -> ValuePatch.modifyTo(12)
+                .mapNotKeptToOptional(any -> null)
+                .isPresent())
+            .isInstanceOf(NullPointerException.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/630dcab1/server/container/util/src/test/java/org/apache/james/util/date/ImapDateTimeFormatterTest.java
----------------------------------------------------------------------
diff --git a/server/container/util/src/test/java/org/apache/james/util/date/ImapDateTimeFormatterTest.java b/server/container/util/src/test/java/org/apache/james/util/date/ImapDateTimeFormatterTest.java
new file mode 100644
index 0000000..ec970fb
--- /dev/null
+++ b/server/container/util/src/test/java/org/apache/james/util/date/ImapDateTimeFormatterTest.java
@@ -0,0 +1,252 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.util.date;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.DayOfWeek;
+import java.time.Month;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeParseException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class ImapDateTimeFormatterTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void dayOfWeekShouldBeParsed() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("Wed, 28 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getDayOfWeek()).isEqualTo(DayOfWeek.WEDNESDAY);
+    }
+
+    @Test
+    public void parseShouldNotThrowWhenDayOfWeekIsAbsent() {
+        ZonedDateTime.parse("28 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenDayOfWeekIsWrong() {
+        expectedException.expect(DateTimeParseException.class);
+        // must be wednesday
+        ZonedDateTime.parse("Mon, 28 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenDayOfWeekIsUnknow() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("Abc, 28 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void dayOfWeekShouldBeParsedWhenOneDigit() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("3 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getDayOfMonth()).isEqualTo(3);
+    }
+
+    @Test
+    public void dayOfWeekShouldBeParsedWhenTwoDigits() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("13 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getDayOfMonth()).isEqualTo(13);
+    }
+
+    @Test
+    public void parseShouldThrowWhenDayOfMonthIsAbsent() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenDayOfMonthIsNegative() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("-2 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenDayOfMonthIsUnknow() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("64 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void monthOfYearShouldBeParsed() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("Wed, 28 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getMonth()).isEqualTo(Month.JUNE);
+    }
+
+    @Test
+    public void parseShouldThrowWhenMonthOfYearIsAbsent() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("Wed, 28 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenMonthOfYearIsUnknow() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("Wed, 28 Abc 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void yearShouldBeParsedWhenFourDigits() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("Wed, 28 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getYear()).isEqualTo(2017);
+    }
+
+    @Test
+    public void yearShouldBeParsedWhenTwoDigitsGreaterThanInitialYear() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("28 Jun 77 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getYear()).isEqualTo(1977);
+    }
+
+    @Test
+    public void yearShouldBeParsedWhenTwoDigitsLesserThanInitialYear() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("28 Jun 64 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getYear()).isEqualTo(2064);
+    }
+
+    @Test
+    public void parseShouldThrowWhenYearIsAbsent() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("Wed, 28 Jun 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenYearIsLesserThanTwoDigits() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("Wed, 28 Jun 1 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenYearIsGreaterThanFourDigits() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("Wed, 28 Jun 12345 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void hourOfDayShouldBeParsed() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("3 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getHour()).isEqualTo(4);
+    }
+
+    @Test
+    public void parseShouldNotThrowWhenHourOfDayIsLesserThanTwoDigits() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("3 Jun 2017 4:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getHour()).isEqualTo(4);
+    }
+
+    @Test
+    public void parseShouldThrowWhenHourOfDayIsAbsent() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 :35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenHourOfDayIsGreaterThanTwoDigits() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 123:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenHourOfDayIsUnknow() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 48:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void minuteOfHourShouldBeParsed() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("3 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getMinute()).isEqualTo(35);
+    }
+
+    @Test
+    public void parseShouldNotThrowWhenMinuteOfHourIsLesserThanTwoDigits() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("3 Jun 2017 04:5:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getMinute()).isEqualTo(5);
+    }
+
+    @Test
+    public void parseShouldThrowWhenMinuteOfHourIsAbsent() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 04::11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenMinuteOfHourIsGreaterThanTwoDigits() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 04:123:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenMinuteOfHourDayIsUnknow() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 04:72:11 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void secondOfMinuteShouldBeParsed() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("3 Jun 2017 04:35:11 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getSecond()).isEqualTo(11);
+    }
+
+    @Test
+    public void parseShouldNotThrowWhenSecondOfMinuteIsLesserThanTwoDigits() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("3 Jun 2017 04:35:1 -0700", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getSecond()).isEqualTo(1);
+    }
+
+    @Test
+    public void parseShouldNotThrowWhenSecondOfMinuteIsAbsent() {
+        ZonedDateTime.parse("28 Jun 2017 04:35 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenSecondOfMinuteIsGreaterThanTwoDigits() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 04:35:123 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenSecondOfMinuteDayIsUnknow() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 04:35:78 -0700", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void offsetShouldBeParsed() {
+        ZonedDateTime dateTime = ZonedDateTime.parse("3 Jun 2017 04:35:11 -0712", ImapDateTimeFormatter.rfc5322());
+        assertThat(dateTime.getOffset()).isEqualTo(ZoneOffset.ofHoursMinutes(-7, -12));
+    }
+
+    @Test
+    public void parseShouldThrowWhenOffsetIsAbsent() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 04:35:11", ImapDateTimeFormatter.rfc5322());
+    }
+
+    @Test
+    public void parseShouldThrowWhenOffsetIsUnknow() {
+        expectedException.expect(DateTimeParseException.class);
+        ZonedDateTime.parse("3 Jun 2017 04:35:11 +7894", ImapDateTimeFormatter.rfc5322());
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org