/* * 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.beam.sdk.testing; import com.google.common.base.MoreObjects; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.List; import javax.annotation.Nullable; import org.apache.beam.sdk.coders.Coder; import org.apache.beam.sdk.coders.CoderException; import org.apache.beam.sdk.coders.ListCoder; import org.apache.beam.sdk.util.CoderUtils; import org.apache.beam.sdk.util.UserCodeException; import org.apache.beam.sdk.values.KV; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.Matchers; /** * Static class for building and using {@link SerializableMatcher} instances. * * <p>Most matchers are wrappers for hamcrest's {@link Matchers}. Please be familiar with the * documentation there. Values retained by a {@link SerializableMatcher} are required to be * serializable, either via Java serialization or via a provided {@link Coder}. * * <p>The following matchers are novel to Apache Beam: * <ul> * <li>{@link #kvWithKey} for matching just the key of a {@link KV}. * <li>{@link #kvWithValue} for matching just the value of a {@link KV}. * <li>{@link #kv} for matching the key and value of a {@link KV}. * </ul> * * <p>For example, to match a group from * {@link org.apache.beam.sdk.transforms.GroupByKey}, which has type * {@code KV<K, Iterable<V>>} for some {@code K} and {@code V} and where the order of the iterable * is undefined, use a matcher like * {@code kv(equalTo("some key"), containsInAnyOrder(1, 2, 3))}. */ class SerializableMatchers implements Serializable { // Serializable only because of capture by anonymous inner classes private SerializableMatchers() { } // not instantiable /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#allOf(Iterable)}. */ public static <T> SerializableMatcher<T> allOf(Iterable<SerializableMatcher<? super T>> serializableMatchers) { @SuppressWarnings({"rawtypes", "unchecked"}) // safe covariant cast final Iterable<Matcher<? super T>> matchers = (Iterable) serializableMatchers; return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.allOf(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#allOf(Matcher[])}. */ @SafeVarargs public static <T> SerializableMatcher<T> allOf(final SerializableMatcher<T>... matchers) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.allOf(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#anyOf(Iterable)}. */ public static <T> SerializableMatcher<T> anyOf(Iterable<SerializableMatcher<? super T>> serializableMatchers) { @SuppressWarnings({"rawtypes", "unchecked"}) // safe covariant cast final Iterable<Matcher<? super T>> matchers = (Iterable) serializableMatchers; return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.anyOf(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#anyOf(Matcher[])}. */ @SafeVarargs public static <T> SerializableMatcher<T> anyOf(final SerializableMatcher<T>... matchers) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.anyOf(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#anything()}. */ public static SerializableMatcher<Object> anything() { return fromSupplier(new SerializableSupplier<Matcher<Object>>() { @Override public Matcher<Object> get() { return Matchers.anything(); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayContaining(Object[])}. */ @SafeVarargs public static <T extends Serializable> SerializableMatcher<T[]> arrayContaining(final T... items) { return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.arrayContaining(items); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayContaining(Object[])}. * * <p>The items of type {@code T} will be serialized using the provided {@link Coder}. They are * explicitly <i>not</i> required or expected to be serializable via Java serialization. */ @SafeVarargs public static <T> SerializableMatcher<T[]> arrayContaining(Coder<T> coder, T... items) { final SerializableSupplier<T[]> itemsSupplier = new SerializableArrayViaCoder<>(coder, items); return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.arrayContaining(itemsSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayContaining(Matcher[])}. */ @SafeVarargs public static <T> SerializableMatcher<T[]> arrayContaining(final SerializableMatcher<? super T>... matchers) { return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.<T>arrayContaining(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayContaining(List)}. */ public static <T> SerializableMatcher<T[]> arrayContaining(List<SerializableMatcher<? super T>> serializableMatchers) { @SuppressWarnings({"rawtypes", "unchecked"}) // safe covariant cast final List<Matcher<? super T>> matchers = (List) serializableMatchers; return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.arrayContaining(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayContainingInAnyOrder(Object[])}. */ @SafeVarargs public static <T extends Serializable> SerializableMatcher<T[]> arrayContainingInAnyOrder(final T... items) { return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.arrayContainingInAnyOrder(items); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayContainingInAnyOrder(Object[])}. * * <p>The items of type {@code T} will be serialized using the provided {@link Coder}. They are * explicitly <i>not</i> required or expected to be serializable via Java serialization. */ @SafeVarargs public static <T> SerializableMatcher<T[]> arrayContainingInAnyOrder(Coder<T> coder, T... items) { final SerializableSupplier<T[]> itemsSupplier = new SerializableArrayViaCoder<>(coder, items); return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.arrayContaining(itemsSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayContainingInAnyOrder(Matcher[])}. */ @SafeVarargs public static <T> SerializableMatcher<T[]> arrayContainingInAnyOrder( final SerializableMatcher<? super T>... matchers) { return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.<T>arrayContainingInAnyOrder(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayContainingInAnyOrder(Collection)}. */ public static <T> SerializableMatcher<T[]> arrayContainingInAnyOrder( Collection<SerializableMatcher<? super T>> serializableMatchers) { @SuppressWarnings({"rawtypes", "unchecked"}) // safe covariant cast final Collection<Matcher<? super T>> matchers = (Collection) serializableMatchers; return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.arrayContainingInAnyOrder(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayWithSize(int)}. */ public static <T> SerializableMatcher<T[]> arrayWithSize(final int size) { return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.arrayWithSize(size); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#arrayWithSize(Matcher)}. */ public static <T> SerializableMatcher<T[]> arrayWithSize( final SerializableMatcher<? super Integer> sizeMatcher) { return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.arrayWithSize(sizeMatcher); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#closeTo(double,double)}. */ public static SerializableMatcher<Double> closeTo(final double target, final double error) { return fromSupplier(new SerializableSupplier<Matcher<Double>>() { @Override public Matcher<Double> get() { return Matchers.closeTo(target, error); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#contains(Object[])}. */ @SafeVarargs public static <T extends Serializable> SerializableMatcher<Iterable<? extends T>> contains( final T... items) { return fromSupplier(new SerializableSupplier<Matcher<Iterable<? extends T>>>() { @Override public Matcher<Iterable<? extends T>> get() { return Matchers.contains(items); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#contains(Object[])}. * * <p>The items of type {@code T} will be serialized using the provided {@link Coder}. They are * explicitly <i>not</i> required or expected to be serializable via Java serialization. */ @SafeVarargs public static <T> SerializableMatcher<Iterable<? extends T>> contains(Coder<T> coder, T... items) { final SerializableSupplier<T[]> itemsSupplier = new SerializableArrayViaCoder<>(coder, items); return fromSupplier(new SerializableSupplier<Matcher<Iterable<? extends T>>>() { @Override public Matcher<Iterable<? extends T>> get() { return Matchers.containsInAnyOrder(itemsSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#contains(Matcher[])}. */ @SafeVarargs public static <T> SerializableMatcher<Iterable<? extends T>> contains( final SerializableMatcher<? super T>... matchers) { return fromSupplier(new SerializableSupplier<Matcher<Iterable<? extends T>>>() { @Override public Matcher<Iterable<? extends T>> get() { return Matchers.<T>contains(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#contains(List)}. */ public static <T extends Serializable> SerializableMatcher<Iterable<? extends T>> contains( List<SerializableMatcher<? super T>> serializableMatchers) { @SuppressWarnings({"rawtypes", "unchecked"}) // safe covariant cast final List<Matcher<? super T>> matchers = (List) serializableMatchers; return fromSupplier(new SerializableSupplier<Matcher<Iterable<? extends T>>>() { @Override public Matcher<Iterable<? extends T>> get() { return Matchers.contains(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#containsInAnyOrder(Object[])}. */ @SafeVarargs public static <T extends Serializable> SerializableMatcher<Iterable<? extends T>> containsInAnyOrder(final T... items) { return fromSupplier(new SerializableSupplier<Matcher<Iterable<? extends T>>>() { @Override public Matcher<Iterable<? extends T>> get() { return Matchers.containsInAnyOrder(items); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#containsInAnyOrder(Object[])}. * * <p>The items of type {@code T} will be serialized using the provided {@link Coder}. * It is explicitly <i>not</i> required or expected to be serializable via Java serialization. */ @SafeVarargs public static <T> SerializableMatcher<Iterable<? extends T>> containsInAnyOrder(Coder<T> coder, T... items) { final SerializableSupplier<T[]> itemsSupplier = new SerializableArrayViaCoder<>(coder, items); return fromSupplier(new SerializableSupplier<Matcher<Iterable<? extends T>>>() { @Override public Matcher<Iterable<? extends T>> get() { return Matchers.containsInAnyOrder(itemsSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#containsInAnyOrder(Matcher[])}. */ @SafeVarargs public static <T> SerializableMatcher<Iterable<? extends T>> containsInAnyOrder( final SerializableMatcher<? super T>... matchers) { return fromSupplier(new SerializableSupplier<Matcher<Iterable<? extends T>>>() { @Override public Matcher<Iterable<? extends T>> get() { return Matchers.<T>containsInAnyOrder(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#containsInAnyOrder(Collection)}. */ public static <T> SerializableMatcher<Iterable<? extends T>> containsInAnyOrder( Collection<SerializableMatcher<? super T>> serializableMatchers) { @SuppressWarnings({"rawtypes", "unchecked"}) // safe covariant cast final Collection<Matcher<? super T>> matchers = (Collection) serializableMatchers; return fromSupplier(new SerializableSupplier<Matcher<Iterable<? extends T>>>() { @Override public Matcher<Iterable<? extends T>> get() { return Matchers.containsInAnyOrder(matchers); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#containsString}. */ public static SerializableMatcher<String> containsString(final String substring) { return fromSupplier(new SerializableSupplier<Matcher<String>>() { @Override public Matcher<String> get() { return Matchers.containsString(substring); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#empty()}. */ public static <T> SerializableMatcher<Collection<? extends T>> empty() { return fromSupplier(new SerializableSupplier<Matcher<Collection<? extends T>>>() { @Override public Matcher<Collection<? extends T>> get() { return Matchers.empty(); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#emptyArray()}. */ public static <T> SerializableMatcher<T[]> emptyArray() { return fromSupplier(new SerializableSupplier<Matcher<T[]>>() { @Override public Matcher<T[]> get() { return Matchers.emptyArray(); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#emptyIterable()}. */ public static <T> SerializableMatcher<Iterable<? extends T>> emptyIterable() { return fromSupplier(new SerializableSupplier<Matcher<Iterable<? extends T>>>() { @Override public Matcher<Iterable<? extends T>> get() { return Matchers.emptyIterable(); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#endsWith}. */ public static SerializableMatcher<String> endsWith(final String substring) { return fromSupplier(new SerializableSupplier<Matcher<String>>() { @Override public Matcher<String> get() { return Matchers.endsWith(substring); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#equalTo(Object)}. */ public static <T extends Serializable> SerializableMatcher<T> equalTo(final T expected) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.equalTo(expected); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#equalTo(Object)}. * * <p>The expected value of type {@code T} will be serialized using the provided {@link Coder}. * It is explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <T> SerializableMatcher<T> equalTo(Coder<T> coder, T expected) { final SerializableSupplier<T> expectedSupplier = new SerializableViaCoder<>(coder, expected); return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.equalTo(expectedSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#greaterThan(Comparable)}. */ public static <T extends Comparable<T> & Serializable> SerializableMatcher<T> greaterThan(final T target) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.greaterThan(target); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#greaterThan(Comparable)}. * * <p>The target value of type {@code T} will be serialized using the provided {@link Coder}. * It is explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <T extends Comparable<T> & Serializable> SerializableMatcher<T> greaterThan(final Coder<T> coder, T target) { final SerializableSupplier<T> targetSupplier = new SerializableViaCoder<>(coder, target); return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.greaterThan(targetSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#greaterThanOrEqualTo(Comparable)}. */ public static <T extends Comparable<T>> SerializableMatcher<T> greaterThanOrEqualTo( final T target) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.greaterThanOrEqualTo(target); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#greaterThanOrEqualTo(Comparable)}. * * <p>The target value of type {@code T} will be serialized using the provided {@link Coder}. * It is explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <T extends Comparable<T> & Serializable> SerializableMatcher<T> greaterThanOrEqualTo(final Coder<T> coder, T target) { final SerializableSupplier<T> targetSupplier = new SerializableViaCoder<>(coder, target); return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.greaterThanOrEqualTo(targetSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#hasItem(Object)}. */ public static <T extends Serializable> SerializableMatcher<Iterable<? super T>> hasItem( final T target) { return fromSupplier(new SerializableSupplier<Matcher<Iterable<? super T>>>() { @Override public Matcher<Iterable<? super T>> get() { return Matchers.hasItem(target); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#hasItem(Object)}. * * <p>The item of type {@code T} will be serialized using the provided {@link Coder}. * It is explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <T> SerializableMatcher<Iterable<? super T>> hasItem(Coder<T> coder, T target) { final SerializableSupplier<T> targetSupplier = new SerializableViaCoder<>(coder, target); return fromSupplier(new SerializableSupplier<Matcher<Iterable<? super T>>>() { @Override public Matcher<Iterable<? super T>> get() { return Matchers.hasItem(targetSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#hasItem(Matcher)}. */ public static <T> SerializableMatcher<Iterable<? super T>> hasItem( final SerializableMatcher<? super T> matcher) { return fromSupplier(new SerializableSupplier<Matcher<Iterable<? super T>>>() { @Override public Matcher<Iterable<? super T>> get() { return Matchers.hasItem(matcher); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#hasSize(int)}. */ public static <T> SerializableMatcher<Collection<? extends T>> hasSize(final int size) { return fromSupplier(new SerializableSupplier<Matcher<Collection<? extends T>>>() { @Override public Matcher<Collection<? extends T>> get() { return Matchers.hasSize(size); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#hasSize(Matcher)}. */ public static <T> SerializableMatcher<Collection<? extends T>> hasSize( final SerializableMatcher<? super Integer> sizeMatcher) { return fromSupplier(new SerializableSupplier<Matcher<Collection<? extends T>>>() { @Override public Matcher<Collection<? extends T>> get() { return Matchers.hasSize(sizeMatcher); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#iterableWithSize(int)}. */ public static <T> SerializableMatcher<Iterable<T>> iterableWithSize(final int size) { return fromSupplier(new SerializableSupplier<Matcher<Iterable<T>>>() { @Override public Matcher<Iterable<T>> get() { return Matchers.iterableWithSize(size); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#iterableWithSize(Matcher)}. */ public static <T> SerializableMatcher<Iterable<T>> iterableWithSize( final SerializableMatcher<? super Integer> sizeMatcher) { return fromSupplier(new SerializableSupplier<Matcher<Iterable<T>>>() { @Override public Matcher<Iterable<T>> get() { return Matchers.iterableWithSize(sizeMatcher); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#isIn(Collection)}. */ public static <T extends Serializable> SerializableMatcher<T> isIn(final Collection<T> collection) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.isIn(collection); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#isIn(Collection)}. * * <p>The items of type {@code T} will be serialized using the provided {@link Coder}. * They are explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <T> SerializableMatcher<T> isIn(Coder<T> coder, Collection<T> collection) { @SuppressWarnings("unchecked") T[] items = (T[]) collection.toArray(); final SerializableSupplier<T[]> itemsSupplier = new SerializableArrayViaCoder<>(coder, items); return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.isIn(itemsSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#isIn(Object[])}. */ public static <T extends Serializable> SerializableMatcher<T> isIn(final T[] items) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.isIn(items); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#isIn(Object[])}. * * <p>The items of type {@code T} will be serialized using the provided {@link Coder}. * They are explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <T> SerializableMatcher<T> isIn(Coder<T> coder, T[] items) { final SerializableSupplier<T[]> itemsSupplier = new SerializableArrayViaCoder<>(coder, items); return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.isIn(itemsSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#isOneOf}. */ @SafeVarargs public static <T extends Serializable> SerializableMatcher<T> isOneOf(final T... elems) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.isOneOf(elems); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#isOneOf}. * * <p>The items of type {@code T} will be serialized using the provided {@link Coder}. * They are explicitly <i>not</i> required or expected to be serializable via Java serialization. */ @SafeVarargs public static <T> SerializableMatcher<T> isOneOf(Coder<T> coder, T... items) { final SerializableSupplier<T[]> itemsSupplier = new SerializableArrayViaCoder<>(coder, items); return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.isOneOf(itemsSupplier.get()); } }); } /** * A {@link SerializableMatcher} that matches any {@link KV} with the specified key. */ public static <K extends Serializable, V> SerializableMatcher<KV<? extends K, ? extends V>> kvWithKey(K key) { return new KvKeyMatcher<K, V>(equalTo(key)); } /** * A {@link SerializableMatcher} that matches any {@link KV} with the specified key. * * <p>The key of type {@code K} will be serialized using the provided {@link Coder}. * It is explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <K, V> SerializableMatcher<KV<? extends K, ? extends V>> kvWithKey(Coder<K> coder, K key) { return new KvKeyMatcher<K, V>(equalTo(coder, key)); } /** * A {@link SerializableMatcher} that matches any {@link KV} with matching key. */ public static <K, V> SerializableMatcher<KV<? extends K, ? extends V>> kvWithKey( final SerializableMatcher<? super K> keyMatcher) { return new KvKeyMatcher<K, V>(keyMatcher); } /** * A {@link SerializableMatcher} that matches any {@link KV} with the specified value. */ public static <K, V extends Serializable> SerializableMatcher<KV<? extends K, ? extends V>> kvWithValue(V value) { return new KvValueMatcher<K, V>(equalTo(value)); } /** * A {@link SerializableMatcher} that matches any {@link KV} with the specified value. * * <p>The value of type {@code V} will be serialized using the provided {@link Coder}. * It is explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <K, V> SerializableMatcher<KV<? extends K, ? extends V>> kvWithValue(Coder<V> coder, V value) { return new KvValueMatcher<K, V>(equalTo(coder, value)); } /** * A {@link SerializableMatcher} that matches any {@link KV} with matching value. */ public static <K, V> SerializableMatcher<KV<? extends K, ? extends V>> kvWithValue( final SerializableMatcher<? super V> valueMatcher) { return new KvValueMatcher<>(valueMatcher); } /** * A {@link SerializableMatcher} that matches any {@link KV} with matching key and value. */ public static <K, V> SerializableMatcher<KV<? extends K, ? extends V>> kv( final SerializableMatcher<? super K> keyMatcher, final SerializableMatcher<? super V> valueMatcher) { return SerializableMatchers.<KV<? extends K, ? extends V>>allOf( SerializableMatchers.<K, V>kvWithKey(keyMatcher), SerializableMatchers.<K, V>kvWithValue(valueMatcher)); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#lessThan(Comparable)}. */ public static <T extends Comparable<T> & Serializable> SerializableMatcher<T> lessThan( final T target) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.lessThan(target); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#lessThan(Comparable)}. * * <p>The target value of type {@code T} will be serialized using the provided {@link Coder}. * It is explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <T extends Comparable<T>> SerializableMatcher<T> lessThan(Coder<T> coder, T target) { final SerializableSupplier<T> targetSupplier = new SerializableViaCoder<>(coder, target); return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.lessThan(targetSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#lessThanOrEqualTo(Comparable)}. */ public static <T extends Comparable<T> & Serializable> SerializableMatcher<T> lessThanOrEqualTo( final T target) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.lessThanOrEqualTo(target); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#lessThanOrEqualTo(Comparable)}. * * <p>The target value of type {@code T} will be serialized using the provided {@link Coder}. * It is explicitly <i>not</i> required or expected to be serializable via Java serialization. */ public static <T extends Comparable<T>> SerializableMatcher<T> lessThanOrEqualTo( Coder<T> coder, T target) { final SerializableSupplier<T> targetSupplier = new SerializableViaCoder<>(coder, target); return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.lessThanOrEqualTo(targetSupplier.get()); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#not}. */ public static <T> SerializableMatcher<T> not(final SerializableMatcher<T> matcher) { return fromSupplier(new SerializableSupplier<Matcher<T>>() { @Override public Matcher<T> get() { return Matchers.not(matcher); } }); } /** * A {@link SerializableMatcher} with identical criteria to * {@link Matchers#nullValue}. */ public static SerializableMatcher<Object> nullValue() { return fromSupplier(new SerializableSupplier<Matcher<Object>>() { @Override public Matcher<Object> get() { return Matchers.nullValue(); } }); } /** * A {@link SerializableMatcher} with identical criteria to {@link Matchers#startsWith}. */ public static SerializableMatcher<String> startsWith(final String substring) { return fromSupplier(new SerializableSupplier<Matcher<String>>() { @Override public Matcher<String> get() { return Matchers.startsWith(substring); } }); } private static class KvKeyMatcher<K, V> extends BaseMatcher<KV<? extends K, ? extends V>> implements SerializableMatcher<KV<? extends K, ? extends V>> { private final SerializableMatcher<? super K> keyMatcher; public KvKeyMatcher(SerializableMatcher<? super K> keyMatcher) { this.keyMatcher = keyMatcher; } @Override public boolean matches(Object item) { @SuppressWarnings("unchecked") KV<K, ?> kvItem = (KV<K, ?>) item; return keyMatcher.matches(kvItem.getKey()); } @Override public void describeMismatch(Object item, Description mismatchDescription) { @SuppressWarnings("unchecked") KV<K, ?> kvItem = (KV<K, ?>) item; if (!keyMatcher.matches(kvItem.getKey())) { mismatchDescription.appendText("key did not match: "); keyMatcher.describeMismatch(kvItem.getKey(), mismatchDescription); } } @Override public void describeTo(Description description) { description.appendText("KV with key matching "); keyMatcher.describeTo(description); } @Override public String toString() { return MoreObjects.toStringHelper(this) .addValue(keyMatcher) .toString(); } } private static class KvValueMatcher<K, V> extends BaseMatcher<KV<? extends K, ? extends V>> implements SerializableMatcher<KV<? extends K, ? extends V>> { private final SerializableMatcher<? super V> valueMatcher; public KvValueMatcher(SerializableMatcher<? super V> valueMatcher) { this.valueMatcher = valueMatcher; } @Override public boolean matches(Object item) { @SuppressWarnings("unchecked") KV<?, V> kvItem = (KV<?, V>) item; return valueMatcher.matches(kvItem.getValue()); } @Override public void describeMismatch(Object item, Description mismatchDescription) { @SuppressWarnings("unchecked") KV<?, V> kvItem = (KV<?, V>) item; if (!valueMatcher.matches(kvItem.getValue())) { mismatchDescription.appendText("value did not match: "); valueMatcher.describeMismatch(kvItem.getValue(), mismatchDescription); } } @Override public void describeTo(Description description) { description.appendText("KV with value matching "); valueMatcher.describeTo(description); } @Override public String toString() { return MoreObjects.toStringHelper(this) .addValue(valueMatcher) .toString(); } } /** * Constructs a {@link SerializableMatcher} from a non-serializable {@link Matcher} via * indirection through {@link SerializableSupplier}. * * <p>To wrap a {@link Matcher} which is not serializable, provide a {@link SerializableSupplier} * with a {@link SerializableSupplier#get()} method that returns a fresh instance of the * {@link Matcher} desired. The resulting {@link SerializableMatcher} will behave according to * the {@link Matcher} returned by {@link SerializableSupplier#get() get()} when it is invoked * during matching (which may occur on another machine). * * <pre>{@code * return fromSupplier(new SerializableSupplier<Matcher<T>>() { * * @Override * public Matcher<T> get() { * return new MyMatcherForT(); * } * }); * }</pre> */ public static <T> SerializableMatcher<T> fromSupplier( SerializableSupplier<Matcher<T>> supplier) { return new SerializableMatcherFromSupplier<>(supplier); } /** * Supplies values of type {@code T}, and is serializable. Thus, even if {@code T} is not * serializable, the supplier can be serialized and provide a {@code T} wherever it is * deserialized. * * @param <T> the type of value supplied. */ public interface SerializableSupplier<T> extends Serializable { T get(); } /** * Since the delegate {@link Matcher} is not generally serializable, instead this takes a nullary * SerializableFunction to return such a matcher. */ private static class SerializableMatcherFromSupplier<T> extends BaseMatcher<T> implements SerializableMatcher<T> { private SerializableSupplier<Matcher<T>> supplier; public SerializableMatcherFromSupplier(SerializableSupplier<Matcher<T>> supplier) { this.supplier = supplier; } @Override public void describeTo(Description description) { supplier.get().describeTo(description); } @Override public boolean matches(Object item) { return supplier.get().matches(item); } @Override public void describeMismatch(Object item, Description mismatchDescription) { supplier.get().describeMismatch(item, mismatchDescription); } } /** * Wraps any value that can be encoded via a {@link Coder} to make it {@link Serializable}. * This is not likely to be a good encoding, so should be used only for tests, where data * volume is small and minor costs are not critical. */ private static class SerializableViaCoder<T> implements SerializableSupplier<T> { /** Cached value that is not serialized. */ @Nullable private transient T value; /** The bytes of {@link #value} when encoded via {@link #coder}. */ private byte[] encodedValue; private Coder<T> coder; public SerializableViaCoder(Coder<T> coder, T value) { this.coder = coder; this.value = value; try { this.encodedValue = CoderUtils.encodeToByteArray(coder, value); } catch (CoderException exc) { throw new RuntimeException("Error serializing via Coder", exc); } } @Override public T get() { if (value == null) { try { value = CoderUtils.decodeFromByteArray(coder, encodedValue); } catch (CoderException exc) { throw new RuntimeException("Error deserializing via Coder", exc); } } return value; } } /** * Wraps any array with values that can be encoded via a {@link Coder} to make it * {@link Serializable}. This is not likely to be a good encoding, so should be used only for * tests, where data volume is small and minor costs are not critical. */ private static class SerializableArrayViaCoder<T> implements SerializableSupplier<T[]> { /** Cached value that is not serialized. */ @Nullable private transient T[] value; /** The bytes of {@link #value} when encoded via {@link #coder}. */ private byte[] encodedValue; private Coder<List<T>> coder; public SerializableArrayViaCoder(Coder<T> elementCoder, T[] value) { this.coder = ListCoder.of(elementCoder); this.value = value; try { this.encodedValue = CoderUtils.encodeToByteArray(coder, Arrays.asList(value)); } catch (CoderException exc) { throw UserCodeException.wrap(exc); } } @Override public T[] get() { if (value == null) { try { @SuppressWarnings("unchecked") T[] decoded = (T[]) CoderUtils.decodeFromByteArray(coder, encodedValue).toArray(); value = decoded; } catch (CoderException exc) { throw new RuntimeException("Error deserializing via Coder", exc); } } return value; } } }