001/*
002 * Copyright 2023 the original author or authors.
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * https://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.test.valueobjects.property.util;
017
018import java.util.Collection;
019
020import lombok.experimental.UtilityClass;
021
022/**
023 * Helper class for asserts on Collection level
024 *
025 * @author Oliver Wolff
026 */
027@UtilityClass
028public final class CollectionAsserts {
029
030    private static final String NOT_EQUAL = "The given object for property %s are not equal: expected=%s , actual=%s";
031
032    private static final String NO_COLLECTION = "The given objects for property %s are to be at least a Collection: expected=%s , actual=%s";
033
034    private static final String DIFFERENT_SIZES = "The given objects for property %s do not have the same size: expected=%s , actual=%s";
035
036    /**
037     * Checks whether two Collection elements are equal ignoring the order.
038     *
039     * @param propertyName the name of the property, used for creating the
040     *                     error-message, must not be null
041     * @param expected
042     * @param actual
043     */
044    public static void assertListsAreEqualIgnoringOrder(final String propertyName, final Object expected,
045            final Object actual) {
046        // Same instance or both null
047        if (expected == actual) {
048            return;
049        }
050
051        if (expected == null || actual == null) {
052            fail(NOT_EQUAL, propertyName, expected, actual);
053        } else {
054            if (!(expected instanceof Iterable) || !(actual instanceof Iterable)) {
055                fail(NO_COLLECTION, propertyName, expected, actual);
056            }
057            handleAssert(propertyName, expected, actual);
058        }
059    }
060
061    private static void handleAssert(final String propertyName, final Object expected, final Object actual) {
062        final Collection<?> expectedIterable = (Collection<?>) expected;
063        final Collection<?> actualIterable = (Collection<?>) actual;
064
065        if (expectedIterable.size() != actualIterable.size()) {
066            fail(DIFFERENT_SIZES, propertyName, expected, actual);
067        }
068        if (expectedIterable.isEmpty()) {
069            return;
070        }
071        for (final Object object : expectedIterable) {
072            if (!actualIterable.contains(object)) {
073                fail(NOT_EQUAL, propertyName, expected, actual);
074            }
075        }
076        for (final Object object : actualIterable) {
077            if (!expectedIterable.contains(object)) {
078                fail(NOT_EQUAL, propertyName, expected, actual);
079            }
080        }
081    }
082
083    private static void fail(final String template, final String propertyName, final Object expected,
084            final Object actual) {
085        final var expectedString = String.valueOf(expected);
086        final var actualString = String.valueOf(actual);
087        throw new AssertionError(template.formatted(propertyName, expectedString, actualString));
088
089    }
090}