diff --git a/java/com/google/gerrit/truth/NullAwareCorrespondence.java b/java/com/google/gerrit/truth/NullAwareCorrespondence.java new file mode 100644 index 0000000000..687ad94ac5 --- /dev/null +++ b/java/com/google/gerrit/truth/NullAwareCorrespondence.java @@ -0,0 +1,92 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// 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 +// Copyright (C) 2020 The Android Open Source Project +// +// 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.gerrit.truth; + +import com.google.common.base.Function; +import com.google.common.truth.Correspondence; +import java.util.Optional; + +/** Utility class for constructing null aware {@link Correspondence}s. */ +public class NullAwareCorrespondence { + /** + * Constructs a {@link Correspondence} that compares elements by transforming the actual elements + * using the given function and testing for equality with the expected elements. + * + *

If the actual element is null, it will correspond to a null expected element. This is + * different to {@link Correspondence#transforming(Function, String)} which would invoke the + * function with a {@code null} argument, requiring the function being able to handle {@code + * null}. + * + * @param actualTransform a {@link Function} taking an actual value and returning a new value + * which will be compared with an expected value to determine whether they correspond + * @param description should fill the gap in a failure message of the form {@code "not true that + * is an element that "}, e.g. + * {@code "has an ID of"} + */ + public static Correspondence transforming( + Function actualTransform, String description) { + return Correspondence.transforming( + actualValue -> Optional.ofNullable(actualValue).map(actualTransform).orElse(null), + description); + } + + /** + * Constructs a {@link Correspondence} that compares elements by transforming the actual elements + * using the given function and testing for equality with the expected elements. + * + *

If the actual element is null, it will correspond to a null expected element. This is + * different to {@link Correspondence#transforming(Function, Function, String)} which would invoke + * the function with a {@code null} argument, requiring the function being able to handle {@code + * null}. + * + *

If the expected element is null, it will correspond to a new null expected element. This is + * different to {@link Correspondence#transforming(Function, Function, String)} which would invoke + * the function with a {@code null} argument, requiring the function being able to handle {@code + * null}. + * + * @param actualTransform a {@link Function} taking an actual value and returning a new value + * which will be compared with an expected value to determine whether they correspond + * @param expectedTransform a {@link Function} taking an expected value and returning a new value + * which will be compared with a transformed actual value + * @param description should fill the gap in a failure message of the form {@code "not true that + * is an element that "}, e.g. + * {@code "has an ID of"} + */ + public static Correspondence transforming( + Function actualTransform, + Function expectedTransform, + String description) { + return Correspondence.transforming( + actualValue -> Optional.ofNullable(actualValue).map(actualTransform).orElse(null), + expectedValue -> Optional.ofNullable(expectedValue).map(expectedTransform).orElse(null), + description); + } + + /** + * Private constructor to prevent instantiation of this class. + * + *

This class contains only static method and hence never needs to be instantiated. + */ + private NullAwareCorrespondence() {} +} diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java index f9ba8a2272..0cd6182e03 100644 --- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java +++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java @@ -148,6 +148,7 @@ import com.google.gerrit.server.validators.AccountActivationValidationListener; import com.google.gerrit.server.validators.ValidationException; import com.google.gerrit.testing.ConfigSuite; import com.google.gerrit.testing.FakeEmailSender.Message; +import com.google.gerrit.truth.NullAwareCorrespondence; import com.google.inject.Inject; import com.google.inject.Provider; import com.jcraft.jsch.KeyPair; @@ -161,7 +162,6 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -2901,12 +2901,7 @@ public class AccountIT extends AbstractDaemonTest { } private static Correspondence getGroupToNameCorrespondence() { - return Correspondence.from( - (actualGroup, expectedName) -> { - String groupName = actualGroup == null ? null : actualGroup.name; - return Objects.equals(groupName, expectedName); - }, - "has name"); + return NullAwareCorrespondence.transforming(groupInfo -> groupInfo.name, "has name"); } private void assertSequenceNumbers(List sshKeys) { diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java index dcf2afdbba..8bc9cd1fa8 100644 --- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java +++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java @@ -100,6 +100,7 @@ import com.google.gerrit.server.notedb.Sequences; import com.google.gerrit.server.util.MagicBranch; import com.google.gerrit.server.util.time.TimeUtil; import com.google.gerrit.testing.GerritJUnit.ThrowingRunnable; +import com.google.gerrit.truth.NullAwareCorrespondence; import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.google.inject.Module; @@ -111,7 +112,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.stream.Stream; import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; @@ -1517,12 +1517,8 @@ public class GroupsIT extends AbstractDaemonTest { } private static Correspondence getAccountToUsernameCorrespondence() { - return Correspondence.from( - (actualAccount, expectedName) -> { - String username = actualAccount == null ? null : actualAccount.username; - return Objects.equals(username, expectedName); - }, - "has username"); + return NullAwareCorrespondence.transforming( + accountInfo -> accountInfo.username, "has username"); } private void assertStaleGroupAndReindex(AccountGroup.UUID groupUuid) throws IOException { diff --git a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java index 96864d93db..a003f9de5d 100644 --- a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java +++ b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java @@ -29,9 +29,9 @@ import com.google.gerrit.extensions.api.groups.GroupInput; import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.truth.NullAwareCorrespondence; import com.google.inject.Inject; import java.sql.Timestamp; -import java.util.Objects; import java.util.Optional; import org.junit.Test; @@ -616,28 +616,11 @@ public class GroupOperationsImplTest extends AbstractDaemonTest { } private static Correspondence getAccountToIdCorrespondence() { - return Correspondence.from( - (actualAccount, expectedId) -> { - Account.Id accountId = - Optional.ofNullable(actualAccount) - .map(account -> account._accountId) - .map(Account::id) - .orElse(null); - return Objects.equals(accountId, expectedId); - }, - "has ID"); + return NullAwareCorrespondence.transforming( + account -> Account.id(account._accountId), "has ID"); } private static Correspondence getGroupToUuidCorrespondence() { - return Correspondence.from( - (actualGroup, expectedUuid) -> { - AccountGroup.UUID groupUuid = - Optional.ofNullable(actualGroup) - .map(group -> group.id) - .map(AccountGroup::uuid) - .orElse(null); - return Objects.equals(groupUuid, expectedUuid); - }, - "has UUID"); + return NullAwareCorrespondence.transforming(group -> AccountGroup.uuid(group.id), "has UUID"); } }