All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.test.LambdaMatchers Maven / Gradle / Ivy

There is a newer version: 8.15.1
Show newest version
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the "Elastic License
 * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
 * Public License v 1"; you may not use this file except in compliance with, at
 * your election, the "Elastic License 2.0", the "GNU Affero General Public
 * License v3.0 only", or the "Server Side Public License, v 1".
 */

package org.elasticsearch.test;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

public class LambdaMatchers {

    private static class TransformMatcher extends BaseMatcher {
        private final Matcher matcher;
        private final Function transform;

        private TransformMatcher(Matcher matcher, Function transform) {
            this.matcher = matcher;
            this.transform = transform;
        }

        @Override
        @SuppressWarnings("unchecked")
        public boolean matches(Object actual) {
            U u;
            try {
                u = transform.apply((T) actual);
            } catch (ClassCastException e) {
                throw new AssertionError(e);
            }

            return matcher.matches(u);
        }

        @Override
        @SuppressWarnings("unchecked")
        public void describeMismatch(Object item, Description description) {
            U u;
            try {
                u = transform.apply((T) item);
            } catch (ClassCastException e) {
                description.appendValue(item).appendText(" is not of the correct type (").appendText(e.getMessage()).appendText(")");
                return;
            }

            description.appendText("transformed value ");
            matcher.describeMismatch(u, description);
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("transformed to match ").appendDescriptionOf(matcher);
        }
    }

    public static  Matcher transformedMatch(Function function, Matcher matcher) {
        return new TransformMatcher<>(matcher, function);
    }

    private static class ListTransformMatcher extends TypeSafeMatcher> {
        private final Matcher> matcher;
        private final Function transform;

        private ListTransformMatcher(Matcher> matcher, Function transform) {
            this.matcher = matcher;
            this.transform = transform;
        }

        @Override
        protected boolean matchesSafely(Iterable item) {
            List us = new ArrayList<>();
            for (T i : item) {
                try {
                    us.add(transform.apply(i)); // this might not actually be a T
                } catch (ClassCastException e) {
                    throw new AssertionError(e);
                }
            }

            return matcher.matches(us);
        }

        @Override
        protected void describeMismatchSafely(Iterable item, Description description) {
            List us = new ArrayList<>();
            int i = 0;
            for (Iterator iterator = item.iterator(); iterator.hasNext(); i++) {
                try {
                    us.add(transform.apply(iterator.next())); // this might not actually be a T
                } catch (ClassCastException e) {
                    description.appendValue(i)
                        .appendText(" at index " + i)
                        .appendText(" is not of the correct type (")
                        .appendText(e.getMessage())
                        .appendText(")");
                    return;
                }
            }

            description.appendText("transformed item ");
            matcher.describeMismatch(us, description);
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("iterable with transformed items to match ").appendDescriptionOf(matcher);
        }
    }

    public static  Matcher> transformedItemsMatch(Function function, Matcher> matcher) {
        return new ListTransformMatcher<>(matcher, function);
    }

    private static class ArrayTransformMatcher extends TypeSafeMatcher {
        private final Matcher matcher;
        private final Function transform;

        private ArrayTransformMatcher(Matcher matcher, Function transform) {
            this.matcher = matcher;
            this.transform = transform;
        }

        @Override
        @SuppressWarnings("unchecked")
        protected boolean matchesSafely(T[] item) {
            U[] us = null;
            for (int i = 0; i < item.length; i++) {
                U u;
                try {
                    u = transform.apply(item[i]);   // this might not actually be a T
                } catch (ClassCastException e) {
                    throw new AssertionError(e);
                }
                if (us == null) {
                    // now we actually have a U, we can create an array of the correct type
                    us = (U[]) Array.newInstance(u.getClass(), item.length);
                }
                us[i] = u;
            }

            return matcher.matches(us);
        }

        @Override
        @SuppressWarnings("unchecked")
        protected void describeMismatchSafely(T[] item, Description description) {
            U[] us = null;
            for (int i = 0; i < item.length; i++) {
                U u;
                try {
                    u = transform.apply(item[i]);   // this might not actually be a T
                } catch (ClassCastException e) {
                    description.appendValue(i)
                        .appendText(" at index " + i)
                        .appendText(" is not of the correct type (")
                        .appendText(e.getMessage())
                        .appendText(")");
                    return;
                }
                if (us == null) {
                    // now we actually have a U, we can create an array of the correct type
                    us = (U[]) Array.newInstance(u.getClass(), item.length);
                }
                us[i] = u;
            }

            description.appendText("transformed item ");
            matcher.describeMismatch(us, description);
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("array with transformed items to match ").appendDescriptionOf(matcher);
        }
    }

    public static  Matcher transformedArrayItemsMatch(Function function, Matcher matcher) {
        return new ArrayTransformMatcher<>(matcher, function);
    }

    private static class PredicateMatcher extends BaseMatcher> {
        final T item;

        private PredicateMatcher(T item) {
            this.item = item;
        }

        @Override
        @SuppressWarnings({ "rawtypes" })
        public boolean matches(Object actual) {
            Predicate p = (Predicate) actual;
            try {
                return predicateMatches(p);
            } catch (ClassCastException e) {
                return false;
            }
        }

        @SuppressWarnings({ "rawtypes", "unchecked" })
        protected boolean predicateMatches(Predicate predicate) {
            return predicate.test(item);
        }

        @Override
        @SuppressWarnings({ "rawtypes", "unchecked" })
        public void describeMismatch(Object item, Description description) {
            Predicate p = (Predicate) item;
            try {
                boolean result = p.test(this.item);
                description.appendText("predicate with argument ").appendValue(this.item).appendText(" evaluated to ").appendValue(result);
            } catch (ClassCastException e) {
                description.appendText("predicate did not accept argument of type ")
                    .appendValue(this.item.getClass())
                    .appendText(" (")
                    .appendText(e.getMessage())
                    .appendText(")");
            }
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("predicate evaluates to  with argument ").appendValue(item);
        }
    }

    public static  Matcher> trueWith(T item) {
        return new PredicateMatcher<>(item);
    }

    private static class PredicateFalseMatcher extends PredicateMatcher {
        private PredicateFalseMatcher(T item) {
            super(item);
        }

        @SuppressWarnings({ "rawtypes", "unchecked" })
        protected boolean predicateMatches(Predicate predicate) {
            return predicate.test(item) == false;
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("predicate evaluates to  with argument ").appendValue(item);
        }
    }

    public static  Matcher> falseWith(T item) {
        return new PredicateFalseMatcher<>(item);
    }
}