/*
* 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;
}
}
}