blob: c790821542f7f2d9f2892667b28b0b8aadfd08bf [file] [log] [blame]
/*
* Copyright (c) 2016 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.truth.extensions.proto;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.asList;
import static com.google.common.truth.extensions.proto.FieldScopeUtil.asList;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IterableSubject;
import com.google.common.truth.Ordered;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Message;
import java.util.Arrays;
import java.util.Comparator;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
/**
* Truth subject for the iterables of protocol buffers.
*
* <p>{@code ProtoTruth.assertThat(actual).containsExactly(expected)} performs the same assertion as
* {@code Truth.assertThat(actual).containsExactly(expected)}. By default, the assertions are strict
* with respect to repeated field order, missing fields, etc. This behavior can be changed with the
* configuration methods on this subject, e.g. {@code
* ProtoTruth.assertThat(actual).ignoringRepeatedFieldOrder().containsExactlyEntriesIn(expected)}.
*
* <p>Floating-point fields are compared using exact equality, which is <a
* href="https://truth.dev/floating_point">probably not what you want</a> if the values are the
* results of some arithmetic. Support for approximate equality may be added in a later version.
*
* <p>Equality tests, and other methods, may yield slightly different behavior for versions 2 and 3
* of Protocol Buffers. If testing protos of multiple versions, make sure you understand the
* behaviors of default and unknown fields so you don't under or over test.
*
* @param <M> the type of the messages in the {@code Iterable}
*/
public class IterableOfProtosSubject<M extends Message> extends IterableSubject {
/*
* Storing a FailureMetadata instance in a Subject subclass is generally a bad practice. For an
* explanation of why it works out OK here, see LiteProtoSubject.
*/
private final FailureMetadata metadata;
private final Iterable<M> actual;
private final FluentEqualityConfig config;
protected IterableOfProtosSubject(
FailureMetadata failureMetadata, @NullableDecl Iterable<M> messages) {
this(failureMetadata, FluentEqualityConfig.defaultInstance(), messages);
}
IterableOfProtosSubject(
FailureMetadata failureMetadata,
FluentEqualityConfig config,
@NullableDecl Iterable<M> messages) {
super(failureMetadata, messages);
this.metadata = failureMetadata;
this.actual = messages;
this.config = config;
}
/**
* Specifies a way to pair up unexpected and missing elements in the message when an assertion
* fails. For example:
*
* <pre>{@code
* assertThat(actualFoos)
* .ignoringRepeatedFieldOrder()
* .ignoringFields(Foo.BAR_FIELD_NUMBER)
* .displayingDiffsPairedBy(Foo::getId)
* .containsExactlyElementsIn(expectedFoos);
* }</pre>
*
* <p>On assertions where it makes sense to do so, the elements are paired as follows: they are
* keyed by {@code keyFunction}, and if an unexpected element and a missing element have the same
* non-null key then the they are paired up. (Elements with null keys are not paired.) The failure
* message will show paired elements together, and a diff will be shown.
*
* <p>The expected elements given in the assertion should be uniquely keyed by {@code
* keyFunction}. If multiple missing elements have the same key then the pairing will be skipped.
*
* <p>Useful key functions will have the property that key equality is less strict than the
* already specified equality rules; i.e. given {@code actual} and {@code expected} values with
* keys {@code actualKey} and {@code expectedKey}, if {@code actual} and {@code expected} compare
* equal given the rest of the directives such as {@code ignoringRepeatedFieldOrder} and {@code
* ignoringFields}, then it is guaranteed that {@code actualKey} is equal to {@code expectedKey},
* but there are cases where {@code actualKey} is equal to {@code expectedKey} but the direct
* comparison fails.
*
* <p>Note that calling this method makes no difference to whether a test passes or fails, it just
* improves the message if it fails.
*/
public IterableOfProtosUsingCorrespondence<M> displayingDiffsPairedBy(
Function<? super M, ?> keyFunction) {
return usingCorrespondence().displayingDiffsPairedBy(keyFunction);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// ProtoFluentAssertion Configuration
//////////////////////////////////////////////////////////////////////////////////////////////////
IterableOfProtosFluentAssertion<M> usingConfig(FluentEqualityConfig newConfig) {
return new IterableOfProtosFluentAssertionImpl<>(
new IterableOfProtosSubject<>(metadata, newConfig, actual));
}
/**
* Specifies that the 'has' bit of individual fields should be ignored when comparing for
* equality.
*
* <p>For version 2 Protocol Buffers, this setting determines whether two protos with the same
* value for a field compare equal if one explicitly sets the value, and the other merely
* implicitly uses the schema-defined default. This setting also determines whether unknown fields
* should be considered in the comparison. By {@code ignoringFieldAbsence()}, unknown fields are
* ignored, and value-equal fields as specified above are considered equal.
*
* <p>For version 3 Protocol Buffers, this setting does not affect primitive fields, because their
* default value is indistinguishable from unset.
*/
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsence() {
return usingConfig(config.ignoringFieldAbsence());
}
/**
* Specifies that the 'has' bit of these explicitly specified top-level field numbers should be
* ignored when comparing for equality. Sub-fields must be specified explicitly (via {@link
* FieldDescriptor}) if they are to be ignored as well.
*
* <p>Use {@link #ignoringFieldAbsence()} instead to ignore the 'has' bit for all fields.
*
* @see #ignoringFieldAbsence() for details
*/
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsenceOfFields(
int firstFieldNumber, int... rest) {
return usingConfig(config.ignoringFieldAbsenceOfFields(asList(firstFieldNumber, rest)));
}
/**
* Specifies that the 'has' bit of these explicitly specified top-level field numbers should be
* ignored when comparing for equality. Sub-fields must be specified explicitly (via {@link
* FieldDescriptor}) if they are to be ignored as well.
*
* <p>Use {@link #ignoringFieldAbsence()} instead to ignore the 'has' bit for all fields.
*
* @see #ignoringFieldAbsence() for details
*/
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsenceOfFields(
Iterable<Integer> fieldNumbers) {
return usingConfig(config.ignoringFieldAbsenceOfFields(fieldNumbers));
}
/**
* Specifies that the 'has' bit of these explicitly specified field descriptors should be ignored
* when comparing for equality. Sub-fields must be specified explicitly if they are to be ignored
* as well.
*
* <p>Use {@link #ignoringFieldAbsence()} instead to ignore the 'has' bit for all fields.
*
* @see #ignoringFieldAbsence() for details
*/
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsenceOfFieldDescriptors(
FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return usingConfig(
config.ignoringFieldAbsenceOfFieldDescriptors(asList(firstFieldDescriptor, rest)));
}
/**
* Specifies that the 'has' bit of these explicitly specified field descriptors should be ignored
* when comparing for equality. Sub-fields must be specified explicitly if they are to be ignored
* as well.
*
* <p>Use {@link #ignoringFieldAbsence()} instead to ignore the 'has' bit for all fields.
*
* @see #ignoringFieldAbsence() for details
*/
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsenceOfFieldDescriptors(
Iterable<FieldDescriptor> fieldDescriptors) {
return usingConfig(config.ignoringFieldAbsenceOfFieldDescriptors(fieldDescriptors));
}
/**
* Specifies that the ordering of repeated fields, at all levels, should be ignored when comparing
* for equality.
*
* <p>This setting applies to all repeated fields recursively, but it does not ignore structure.
* For example, with {@link #ignoringRepeatedFieldOrder()}, a repeated {@code int32} field {@code
* bar}, set inside a repeated message field {@code foo}, the following protos will all compare
* equal:
*
* <pre>{@code
* message1: {
* foo: {
* bar: 1
* bar: 2
* }
* foo: {
* bar: 3
* bar: 4
* }
* }
*
* message2: {
* foo: {
* bar: 2
* bar: 1
* }
* foo: {
* bar: 4
* bar: 3
* }
* }
*
* message3: {
* foo: {
* bar: 4
* bar: 3
* }
* foo: {
* bar: 2
* bar: 1
* }
* }
* }</pre>
*
* <p>However, the following message will compare equal to none of these:
*
* <pre>{@code
* message4: {
* foo: {
* bar: 1
* bar: 3
* }
* foo: {
* bar: 2
* bar: 4
* }
* }
* }</pre>
*
* <p>This setting does not apply to map fields, for which field order is always ignored. The
* serialization order of map fields is undefined, and it may change from runtime to runtime.
*/
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrder() {
return usingConfig(config.ignoringRepeatedFieldOrder());
}
/**
* Specifies that the ordering of repeated fields for these explicitly specified top-level field
* numbers should be ignored when comparing for equality. Sub-fields must be specified explicitly
* (via {@link FieldDescriptor}) if their orders are to be ignored as well.
*
* <p>Use {@link #ignoringRepeatedFieldOrder()} instead to ignore order for all fields.
*
* @see #ignoringRepeatedFieldOrder() for details.
*/
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrderOfFields(
int firstFieldNumber, int... rest) {
return usingConfig(config.ignoringRepeatedFieldOrderOfFields(asList(firstFieldNumber, rest)));
}
/**
* Specifies that the ordering of repeated fields for these explicitly specified top-level field
* numbers should be ignored when comparing for equality. Sub-fields must be specified explicitly
* (via {@link FieldDescriptor}) if their orders are to be ignored as well.
*
* <p>Use {@link #ignoringRepeatedFieldOrder()} instead to ignore order for all fields.
*
* @see #ignoringRepeatedFieldOrder() for details.
*/
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrderOfFields(
Iterable<Integer> fieldNumbers) {
return usingConfig(config.ignoringRepeatedFieldOrderOfFields(fieldNumbers));
}
/**
* Specifies that the ordering of repeated fields for these explicitly specified field descriptors
* should be ignored when comparing for equality. Sub-fields must be specified explicitly if their
* orders are to be ignored as well.
*
* <p>Use {@link #ignoringRepeatedFieldOrder()} instead to ignore order for all fields.
*
* @see #ignoringRepeatedFieldOrder() for details.
*/
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrderOfFieldDescriptors(
FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return usingConfig(
config.ignoringRepeatedFieldOrderOfFieldDescriptors(asList(firstFieldDescriptor, rest)));
}
/**
* Specifies that the ordering of repeated fields for these explicitly specified field descriptors
* should be ignored when comparing for equality. Sub-fields must be specified explicitly if their
* orders are to be ignored as well.
*
* <p>Use {@link #ignoringRepeatedFieldOrder()} instead to ignore order for all fields.
*
* @see #ignoringRepeatedFieldOrder() for details.
*/
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrderOfFieldDescriptors(
Iterable<FieldDescriptor> fieldDescriptors) {
return usingConfig(config.ignoringRepeatedFieldOrderOfFieldDescriptors(fieldDescriptors));
}
/**
* Specifies that, for all repeated and map fields, any elements in the 'actual' proto which are
* not found in the 'expected' proto are ignored, with the exception of fields in the expected
* proto which are empty. To ignore empty repeated fields as well, use {@link
* #comparingExpectedFieldsOnly}.
*
* <p>This rule is applied independently from {@link #ignoringRepeatedFieldOrder}. If ignoring
* repeated field order AND extra repeated field elements, all that is tested is that the expected
* elements comprise a subset of the actual elements. If not ignoring repeated field order, but
* still ignoring extra repeated field elements, the actual elements must contain a subsequence
* that matches the expected elements for the test to pass. (The subsequence rule does not apply
* to Map fields, which are always compared by key.)
*/
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElements() {
return usingConfig(config.ignoringExtraRepeatedFieldElements());
}
/**
* Specifies that extra repeated field elements for these explicitly specified top-level field
* numbers should be ignored. Sub-fields must be specified explicitly (via {@link
* FieldDescriptor}) if their extra elements are to be ignored as well.
*
* <p>Use {@link #ignoringExtraRepeatedFieldElements()} instead to ignore these for all fields.
*
* @see #ignoringExtraRepeatedFieldElements() for details.
*/
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElementsOfFields(
int firstFieldNumber, int... rest) {
return usingConfig(
config.ignoringExtraRepeatedFieldElementsOfFields(asList(firstFieldNumber, rest)));
}
/**
* Specifies that extra repeated field elements for these explicitly specified top-level field
* numbers should be ignored. Sub-fields must be specified explicitly (via {@link
* FieldDescriptor}) if their extra elements are to be ignored as well.
*
* <p>Use {@link #ignoringExtraRepeatedFieldElements()} instead to ignore these for all fields.
*
* @see #ignoringExtraRepeatedFieldElements() for details.
*/
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElementsOfFields(
Iterable<Integer> fieldNumbers) {
return usingConfig(config.ignoringExtraRepeatedFieldElementsOfFields(fieldNumbers));
}
/**
* Specifies that extra repeated field elements for these explicitly specified field descriptors
* should be ignored. Sub-fields must be specified explicitly if their extra elements are to be
* ignored as well.
*
* <p>Use {@link #ignoringExtraRepeatedFieldElements()} instead to ignore these for all fields.
*
* @see #ignoringExtraRepeatedFieldElements() for details.
*/
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElementsOfFieldDescriptors(
FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return usingConfig(
config.ignoringExtraRepeatedFieldElementsOfFieldDescriptors(
asList(firstFieldDescriptor, rest)));
}
/**
* Specifies that extra repeated field elements for these explicitly specified field descriptors
* should be ignored. Sub-fields must be specified explicitly if their extra elements are to be
* ignored as well.
*
* <p>Use {@link #ignoringExtraRepeatedFieldElements()} instead to ignore these for all fields.
*
* @see #ignoringExtraRepeatedFieldElements() for details.
*/
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElementsOfFieldDescriptors(
Iterable<FieldDescriptor> fieldDescriptors) {
return usingConfig(
config.ignoringExtraRepeatedFieldElementsOfFieldDescriptors(fieldDescriptors));
}
/**
* Compares double fields as equal if they are both finite and their absolute difference is less
* than or equal to {@code tolerance}.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingDoubleTolerance(double tolerance) {
return usingConfig(config.usingDoubleTolerance(tolerance));
}
/**
* Compares double fields with these explicitly specified top-level field numbers using the
* provided absolute tolerance.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingDoubleToleranceForFields(
double tolerance, int firstFieldNumber, int... rest) {
return usingConfig(
config.usingDoubleToleranceForFields(tolerance, asList(firstFieldNumber, rest)));
}
/**
* Compares double fields with these explicitly specified top-level field numbers using the
* provided absolute tolerance.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingDoubleToleranceForFields(
double tolerance, Iterable<Integer> fieldNumbers) {
return usingConfig(config.usingDoubleToleranceForFields(tolerance, fieldNumbers));
}
/**
* Compares double fields with these explicitly specified fields using the provided absolute
* tolerance.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingDoubleToleranceForFieldDescriptors(
double tolerance, FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return usingConfig(
config.usingDoubleToleranceForFieldDescriptors(
tolerance, asList(firstFieldDescriptor, rest)));
}
/**
* Compares double fields with these explicitly specified fields using the provided absolute
* tolerance.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingDoubleToleranceForFieldDescriptors(
double tolerance, Iterable<FieldDescriptor> fieldDescriptors) {
return usingConfig(config.usingDoubleToleranceForFieldDescriptors(tolerance, fieldDescriptors));
}
/**
* Compares float fields as equal if they are both finite and their absolute difference is less
* than or equal to {@code tolerance}.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingFloatTolerance(float tolerance) {
return usingConfig(config.usingFloatTolerance(tolerance));
}
/**
* Compares float fields with these explicitly specified top-level field numbers using the
* provided absolute tolerance.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingFloatToleranceForFields(
float tolerance, int firstFieldNumber, int... rest) {
return usingConfig(
config.usingFloatToleranceForFields(tolerance, asList(firstFieldNumber, rest)));
}
/**
* Compares float fields with these explicitly specified top-level field numbers using the
* provided absolute tolerance.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingFloatToleranceForFields(
float tolerance, Iterable<Integer> fieldNumbers) {
return usingConfig(config.usingFloatToleranceForFields(tolerance, fieldNumbers));
}
/**
* Compares float fields with these explicitly specified fields using the provided absolute
* tolerance.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingFloatToleranceForFieldDescriptors(
float tolerance, FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return usingConfig(
config.usingFloatToleranceForFieldDescriptors(
tolerance, asList(firstFieldDescriptor, rest)));
}
/**
* Compares float fields with these explicitly specified top-level field numbers using the
* provided absolute tolerance.
*
* @param tolerance A finite, non-negative tolerance.
*/
public IterableOfProtosFluentAssertion<M> usingFloatToleranceForFieldDescriptors(
float tolerance, Iterable<FieldDescriptor> fieldDescriptors) {
return usingConfig(config.usingFloatToleranceForFieldDescriptors(tolerance, fieldDescriptors));
}
/**
* Limits the comparison of Protocol buffers to the fields set in the expected proto(s). When
* multiple protos are specified, the comparison is limited to the union of set fields in all the
* expected protos.
*
* <p>The "expected proto(s)" are those passed to the method in {@link
* IterableOfProtosUsingCorrespondence} at the end of the call-chain.
*
* <p>Fields not set in the expected proto(s) are ignored. In particular, proto3 fields which have
* their default values are ignored, as these are indistinguishable from unset fields. If you want
* to assert that a proto3 message has certain fields with default values, you cannot use this
* method.
*/
public IterableOfProtosFluentAssertion<M> comparingExpectedFieldsOnly() {
return usingConfig(config.comparingExpectedFieldsOnly());
}
/**
* Limits the comparison of Protocol buffers to the defined {@link FieldScope}.
*
* <p>This method is additive and has well-defined ordering semantics. If the invoking {@link
* ProtoFluentAssertion} is already scoped to a {@link FieldScope} {@code X}, and this method is
* invoked with {@link FieldScope} {@code Y}, the resultant {@link ProtoFluentAssertion} is
* constrained to the intersection of {@link FieldScope}s {@code X} and {@code Y}.
*
* <p>By default, {@link ProtoFluentAssertion} is constrained to {@link FieldScopes#all()}, that
* is, no fields are excluded from comparison.
*/
public IterableOfProtosFluentAssertion<M> withPartialScope(FieldScope fieldScope) {
return usingConfig(config.withPartialScope(checkNotNull(fieldScope, "fieldScope")));
}
/**
* Excludes the top-level message fields with the given tag numbers from the comparison.
*
* <p>This method adds on any previous {@link FieldScope} related settings, overriding previous
* changes to ensure the specified fields are ignored recursively. All sub-fields of these field
* numbers are ignored, and all sub-messages of type {@code M} will also have these field numbers
* ignored.
*
* <p>If an invalid field number is supplied, the terminal comparison operation will throw a
* runtime exception.
*/
public IterableOfProtosFluentAssertion<M> ignoringFields(int firstFieldNumber, int... rest) {
return ignoringFields(asList(firstFieldNumber, rest));
}
/**
* Excludes the top-level message fields with the given tag numbers from the comparison.
*
* <p>This method adds on any previous {@link FieldScope} related settings, overriding previous
* changes to ensure the specified fields are ignored recursively. All sub-fields of these field
* numbers are ignored, and all sub-messages of type {@code M} will also have these field numbers
* ignored.
*
* <p>If an invalid field number is supplied, the terminal comparison operation will throw a
* runtime exception.
*/
public IterableOfProtosFluentAssertion<M> ignoringFields(Iterable<Integer> fieldNumbers) {
return usingConfig(config.ignoringFields(fieldNumbers));
}
/**
* Excludes all message fields matching the given {@link FieldDescriptor}s from the comparison.
*
* <p>This method adds on any previous {@link FieldScope} related settings, overriding previous
* changes to ensure the specified fields are ignored recursively. All sub-fields of these field
* descriptors are ignored, no matter where they occur in the tree.
*
* <p>If a field descriptor which does not, or cannot occur in the proto structure is supplied, it
* is silently ignored.
*/
public IterableOfProtosFluentAssertion<M> ignoringFieldDescriptors(
FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return ignoringFieldDescriptors(asList(firstFieldDescriptor, rest));
}
/**
* Excludes all message fields matching the given {@link FieldDescriptor}s from the comparison.
*
* <p>This method adds on any previous {@link FieldScope} related settings, overriding previous
* changes to ensure the specified fields are ignored recursively. All sub-fields of these field
* descriptors are ignored, no matter where they occur in the tree.
*
* <p>If a field descriptor which does not, or cannot occur in the proto structure is supplied, it
* is silently ignored.
*/
public IterableOfProtosFluentAssertion<M> ignoringFieldDescriptors(
Iterable<FieldDescriptor> fieldDescriptors) {
return usingConfig(config.ignoringFieldDescriptors(fieldDescriptors));
}
/**
* Excludes all specific field paths under the argument {@link FieldScope} from the comparison.
*
* <p>This method is additive and has well-defined ordering semantics. If the invoking {@link
* ProtoFluentAssertion} is already scoped to a {@link FieldScope} {@code X}, and this method is
* invoked with {@link FieldScope} {@code Y}, the resultant {@link ProtoFluentAssertion} is
* constrained to the subtraction of {@code X - Y}.
*
* <p>By default, {@link ProtoFluentAssertion} is constrained to {@link FieldScopes#all()}, that
* is, no fields are excluded from comparison.
*/
public IterableOfProtosFluentAssertion<M> ignoringFieldScope(FieldScope fieldScope) {
return usingConfig(config.ignoringFieldScope(checkNotNull(fieldScope, "fieldScope")));
}
/**
* If set, in the event of a comparison failure, the error message printed will list only those
* specific fields that did not match between the actual and expected values. Useful for very
* large protocol buffers.
*
* <p>This a purely cosmetic setting, and it has no effect on the behavior of the test.
*/
public IterableOfProtosFluentAssertion<M> reportingMismatchesOnly() {
return usingConfig(config.reportingMismatchesOnly());
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// Overrides for IterableSubject Methods
//////////////////////////////////////////////////////////////////////////////////////////////////
/**
* @deprecated Protos do not implement {@link Comparable}, so you must {@linkplain
* #isInStrictOrder(Comparator) supply a comparator}.
* @throws ClassCastException always
*/
@Override
@Deprecated
public final void isInStrictOrder() {
throw new ClassCastException(
"Protos do not implement Comparable, so you must supply a Comparator.");
}
/**
* @deprecated Protos do not implement {@link Comparable}, so you must {@linkplain
* #isInOrder(Comparator) supply a comparator}.
* @throws ClassCastException always
*/
@Override
@Deprecated
public final void isInOrder() {
throw new ClassCastException(
"Protos do not implement Comparable, so you must supply a Comparator.");
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// UsingCorrespondence Methods
//////////////////////////////////////////////////////////////////////////////////////////////////
// A forwarding implementation of IterableSubject.UsingCorrespondence which passes the expected
// protos to FluentEqualityConfig before comparing. This is required to support
// displayingDiffsPairedBy(), since we can't pass the user to a vanilla
// IterableSubject.UsingCorrespondence until we know what the expected messages are.
private static class UsingCorrespondence<M extends Message>
implements IterableOfProtosUsingCorrespondence<M> {
private final IterableOfProtosSubject<M> subject;
@NullableDecl private final Function<? super M, ? extends Object> keyFunction;
UsingCorrespondence(
IterableOfProtosSubject<M> subject,
@NullableDecl Function<? super M, ? extends Object> keyFunction) {
this.subject = checkNotNull(subject);
this.keyFunction = keyFunction;
}
private IterableSubject.UsingCorrespondence<M, M> delegate(Iterable<? extends M> messages) {
IterableSubject.UsingCorrespondence<M, M> usingCorrespondence =
subject.comparingElementsUsing(
subject
.config
.withExpectedMessages(messages)
.<M>toCorrespondence(FieldScopeUtil.getSingleDescriptor(subject.actual)));
if (keyFunction != null) {
usingCorrespondence = usingCorrespondence.displayingDiffsPairedBy(keyFunction);
}
return usingCorrespondence;
}
@Override
public IterableOfProtosUsingCorrespondence<M> displayingDiffsPairedBy(
Function<? super M, ?> keyFunction) {
return new UsingCorrespondence<M>(subject, checkNotNull(keyFunction));
}
@Override
public void contains(@NullableDecl M expected) {
delegate(Arrays.asList(expected)).contains(expected);
}
@Override
public void doesNotContain(@NullableDecl M excluded) {
delegate(Arrays.asList(excluded)).doesNotContain(excluded);
}
@Override
@CanIgnoreReturnValue
public Ordered containsExactly(@NullableDecl M... expected) {
return delegate(Arrays.asList(expected)).containsExactly(expected);
}
@Override
@CanIgnoreReturnValue
public Ordered containsExactlyElementsIn(Iterable<? extends M> expected) {
return delegate(expected).containsExactlyElementsIn(expected);
}
@Override
@CanIgnoreReturnValue
public Ordered containsExactlyElementsIn(M[] expected) {
return delegate(Arrays.asList(expected)).containsExactlyElementsIn(expected);
}
@Override
@CanIgnoreReturnValue
public Ordered containsAtLeast(
@NullableDecl M first, @NullableDecl M second, @NullableDecl M... rest) {
return delegate(Lists.asList(first, second, rest)).containsAtLeast(first, second, rest);
}
@Override
@CanIgnoreReturnValue
public Ordered containsAtLeastElementsIn(Iterable<? extends M> expected) {
return delegate(expected).containsAtLeastElementsIn(expected);
}
@Override
@CanIgnoreReturnValue
public Ordered containsAtLeastElementsIn(M[] expected) {
return delegate(Arrays.asList(expected)).containsAtLeastElementsIn(expected);
}
@Override
public void containsAnyOf(
@NullableDecl M first, @NullableDecl M second, @NullableDecl M... rest) {
delegate(Lists.asList(first, second, rest)).containsAnyOf(first, second, rest);
}
@Override
public void containsAnyIn(Iterable<? extends M> expected) {
delegate(expected).containsAnyIn(expected);
}
@Override
public void containsAnyIn(M[] expected) {
delegate(Arrays.asList(expected)).containsAnyIn(expected);
}
@Override
public void containsNoneOf(
@NullableDecl M firstExcluded,
@NullableDecl M secondExcluded,
@NullableDecl M... restOfExcluded) {
delegate(Lists.asList(firstExcluded, secondExcluded, restOfExcluded))
.containsNoneOf(firstExcluded, secondExcluded, restOfExcluded);
}
@Override
public void containsNoneIn(Iterable<? extends M> excluded) {
delegate(excluded).containsNoneIn(excluded);
}
@Override
public void containsNoneIn(M[] excluded) {
delegate(Arrays.asList(excluded)).containsNoneIn(excluded);
}
}
private IterableOfProtosUsingCorrespondence<M> usingCorrespondence() {
return new UsingCorrespondence<M>(this, /* keyFunction= */ null);
}
// The UsingCorrespondence methods have conflicting erasure with default IterableSubject methods,
// so we can't implement them both on the same class, but we want to define both so
// IterableOfProtosSubjects are interchangeable with IterableSubjects when no configuration is
// specified. So, we implement a dumb, private delegator to return instead.
private static final class IterableOfProtosFluentAssertionImpl<M extends Message>
implements IterableOfProtosFluentAssertion<M> {
private final IterableOfProtosSubject<M> subject;
IterableOfProtosFluentAssertionImpl(IterableOfProtosSubject<M> subject) {
this.subject = subject;
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsence() {
return subject.ignoringFieldAbsence();
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsenceOfFields(
int firstFieldNumber, int... rest) {
return subject.ignoringFieldAbsenceOfFields(firstFieldNumber, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsenceOfFields(
Iterable<Integer> fieldNumbers) {
return subject.ignoringFieldAbsenceOfFields(fieldNumbers);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsenceOfFieldDescriptors(
FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return subject.ignoringFieldAbsenceOfFieldDescriptors(firstFieldDescriptor, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFieldAbsenceOfFieldDescriptors(
Iterable<FieldDescriptor> fieldDescriptors) {
return subject.ignoringFieldAbsenceOfFieldDescriptors(fieldDescriptors);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrder() {
return subject.ignoringRepeatedFieldOrder();
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrderOfFields(
int firstFieldNumber, int... rest) {
return subject.ignoringRepeatedFieldOrderOfFields(firstFieldNumber, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrderOfFields(
Iterable<Integer> fieldNumbers) {
return subject.ignoringRepeatedFieldOrderOfFields(fieldNumbers);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrderOfFieldDescriptors(
FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return subject.ignoringRepeatedFieldOrderOfFieldDescriptors(firstFieldDescriptor, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringRepeatedFieldOrderOfFieldDescriptors(
Iterable<FieldDescriptor> fieldDescriptors) {
return subject.ignoringRepeatedFieldOrderOfFieldDescriptors(fieldDescriptors);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElements() {
return subject.ignoringExtraRepeatedFieldElements();
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElementsOfFields(
int firstFieldNumber, int... rest) {
return subject.ignoringExtraRepeatedFieldElementsOfFields(firstFieldNumber, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElementsOfFields(
Iterable<Integer> fieldNumbers) {
return subject.ignoringExtraRepeatedFieldElementsOfFields(fieldNumbers);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElementsOfFieldDescriptors(
FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return subject.ignoringExtraRepeatedFieldElementsOfFieldDescriptors(
firstFieldDescriptor, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringExtraRepeatedFieldElementsOfFieldDescriptors(
Iterable<FieldDescriptor> fieldDescriptors) {
return subject.ignoringExtraRepeatedFieldElementsOfFieldDescriptors(fieldDescriptors);
}
@Override
public IterableOfProtosFluentAssertion<M> usingDoubleTolerance(double tolerance) {
return subject.usingDoubleTolerance(tolerance);
}
@Override
public IterableOfProtosFluentAssertion<M> usingDoubleToleranceForFields(
double tolerance, int firstFieldNumber, int... rest) {
return subject.usingDoubleToleranceForFields(tolerance, firstFieldNumber, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> usingDoubleToleranceForFields(
double tolerance, Iterable<Integer> fieldNumbers) {
return subject.usingDoubleToleranceForFields(tolerance, fieldNumbers);
}
@Override
public IterableOfProtosFluentAssertion<M> usingDoubleToleranceForFieldDescriptors(
double tolerance, FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return subject.usingDoubleToleranceForFieldDescriptors(tolerance, firstFieldDescriptor, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> usingDoubleToleranceForFieldDescriptors(
double tolerance, Iterable<FieldDescriptor> fieldDescriptors) {
return subject.usingDoubleToleranceForFieldDescriptors(tolerance, fieldDescriptors);
}
@Override
public IterableOfProtosFluentAssertion<M> usingFloatTolerance(float tolerance) {
return subject.usingFloatTolerance(tolerance);
}
@Override
public IterableOfProtosFluentAssertion<M> usingFloatToleranceForFields(
float tolerance, int firstFieldNumber, int... rest) {
return subject.usingFloatToleranceForFields(tolerance, firstFieldNumber, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> usingFloatToleranceForFields(
float tolerance, Iterable<Integer> fieldNumbers) {
return subject.usingFloatToleranceForFields(tolerance, fieldNumbers);
}
@Override
public IterableOfProtosFluentAssertion<M> usingFloatToleranceForFieldDescriptors(
float tolerance, FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return subject.usingFloatToleranceForFieldDescriptors(tolerance, firstFieldDescriptor, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> usingFloatToleranceForFieldDescriptors(
float tolerance, Iterable<FieldDescriptor> fieldDescriptors) {
return subject.usingFloatToleranceForFieldDescriptors(tolerance, fieldDescriptors);
}
@Override
public IterableOfProtosFluentAssertion<M> comparingExpectedFieldsOnly() {
return subject.comparingExpectedFieldsOnly();
}
@Override
public IterableOfProtosFluentAssertion<M> withPartialScope(FieldScope fieldScope) {
return subject.withPartialScope(fieldScope);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFields(int firstFieldNumber, int... rest) {
return subject.ignoringFields(firstFieldNumber, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFields(Iterable<Integer> fieldNumbers) {
return subject.ignoringFields(fieldNumbers);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFieldDescriptors(
FieldDescriptor firstFieldDescriptor, FieldDescriptor... rest) {
return subject.ignoringFieldDescriptors(firstFieldDescriptor, rest);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFieldDescriptors(
Iterable<FieldDescriptor> fieldDescriptors) {
return subject.ignoringFieldDescriptors(fieldDescriptors);
}
@Override
public IterableOfProtosFluentAssertion<M> ignoringFieldScope(FieldScope fieldScope) {
return subject.ignoringFieldScope(fieldScope);
}
@Override
public IterableOfProtosFluentAssertion<M> reportingMismatchesOnly() {
return subject.reportingMismatchesOnly();
}
@Override
public IterableOfProtosUsingCorrespondence<M> displayingDiffsPairedBy(
Function<? super M, ?> keyFunction) {
return usingCorrespondence().displayingDiffsPairedBy(keyFunction);
}
@Override
public void contains(@NullableDecl M expected) {
usingCorrespondence().contains(expected);
}
@Override
public void doesNotContain(@NullableDecl M excluded) {
usingCorrespondence().doesNotContain(excluded);
}
@Override
public Ordered containsExactly(@NullableDecl M... expected) {
return usingCorrespondence().containsExactly(expected);
}
@Override
public Ordered containsExactlyElementsIn(Iterable<? extends M> expected) {
return usingCorrespondence().containsExactlyElementsIn(expected);
}
@Override
public Ordered containsExactlyElementsIn(M[] expected) {
return usingCorrespondence().containsExactlyElementsIn(expected);
}
@Override
public Ordered containsAtLeast(
@NullableDecl M first, @NullableDecl M second, @NullableDecl M... rest) {
return usingCorrespondence().containsAtLeast(first, second, rest);
}
@Override
public Ordered containsAtLeastElementsIn(Iterable<? extends M> expected) {
return usingCorrespondence().containsAtLeastElementsIn(expected);
}
@Override
public Ordered containsAtLeastElementsIn(M[] expected) {
return usingCorrespondence().containsAtLeastElementsIn(expected);
}
@Override
public void containsAnyOf(
@NullableDecl M first, @NullableDecl M second, @NullableDecl M... rest) {
usingCorrespondence().containsAnyOf(first, second, rest);
}
@Override
public void containsAnyIn(Iterable<? extends M> expected) {
usingCorrespondence().containsAnyIn(expected);
}
@Override
public void containsAnyIn(M[] expected) {
usingCorrespondence().containsAnyIn(expected);
}
@Override
public void containsNoneOf(
@NullableDecl M firstExcluded,
@NullableDecl M secondExcluded,
@NullableDecl M... restOfExcluded) {
usingCorrespondence().containsNoneOf(firstExcluded, secondExcluded, restOfExcluded);
}
@Override
public void containsNoneIn(Iterable<? extends M> excluded) {
usingCorrespondence().containsNoneIn(excluded);
}
@Override
public void containsNoneIn(M[] excluded) {
usingCorrespondence().containsNoneIn(excluded);
}
@Override
@Deprecated
public boolean equals(Object o) {
return subject.equals(o);
}
@Override
@Deprecated
public int hashCode() {
return subject.hashCode();
}
private final IterableOfProtosUsingCorrespondence<M> usingCorrespondence() {
return subject.usingCorrespondence();
}
}
}