AUI Framework  develop
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
UIMatcher.h
    1/*
    2 * AUI Framework - Declarative UI toolkit for modern C++20
    3 * Copyright (C) 2020-2025 Alex2772 and Contributors
    4 *
    5 * SPDX-License-Identifier: MPL-2.0
    6 *
    7 * This Source Code Form is subject to the terms of the Mozilla Public
    8 * License, v. 2.0. If a copy of the MPL was not distributed with this
    9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   10 */
   11
   12#pragma once
   13
   14
   15#include "IMatcher.h"
   16#include <gtest/gtest.h>
   17#include <AUI/Test/UI/Assertion/Empty.h>
   18#include <AUI/View/AViewContainer.h>
   19#include "UITestUtil.h"
   20
   21class API_AUI_UITESTS UIMatcher {
   22private:
   23    _<IMatcher> mMatcher;
   24    bool mIncludeInvisibleViews = false;
   25
   26
   27    template<typename Container>
   28    void processContainer(Container& destination, const _<AViewContainerBase>& container) const;
   29
   30    template<typename T, typename = int>
   31    struct ignores_visibility : std::false_type { };
   32
   33    template<typename T>
   34    struct ignores_visibility<T, decltype((T::IGNORE_VISIBILITY::value, 0))> : T::IGNORE_VISIBILITY { };
   35
   36    template<class Assertion>
   37    void performHintChecks(const char* msg, ASet<_<AView>>& set) {
   38        currentImpl() = this;
   39        if constexpr (ignores_visibility<Assertion>::value) {
   40            set = toSet();
   41        }
   42        uitest::frame();
   43        if (msg == nullptr) std::cout << "Assertion message is empty";
   44        if constexpr(!std::is_same_v<Assertion, empty>) {
   45            if (set.empty()) std::cout << "UIMatcher is empty so check is not performed";
   46        }
   47    }
   48
   49    static UIMatcher*& currentImpl();
   50
   51public:
   52    UIMatcher(const _<IMatcher>& matcher) : mMatcher(matcher) {}
   53
   54    ~UIMatcher() {
   55        if (current() == this) {
   56            currentImpl() = nullptr;
   57        }
   58    }
   59
   60    static UIMatcher* current() {
   61        return currentImpl();
   62    }
   63
   64    ASet<_<AView>> toSet() const;
   65    AVector<_<AView>> toVector() const;
   66
   67    _<AView> one() const {
   68        auto set = toSet();
   69        if (set.empty()) {
   70            SCOPED_TRACE("no views selected");
   71            return nullptr;
   72        }
   73        return *set.begin();
   74    }
   75
   76    UIMatcher& includeInvisibleViews() {
   77        mIncludeInvisibleViews = true;
   78        return *this;
   79    }
   80
   81    template<class Action>
   82    UIMatcher& perform(Action&& action) {
   83        auto set = toSet();
   84        if (set.empty()) std::cout << "UIMatcher is empty so action is not performed";
   85
   86        uitest::frame();
   87        for (auto& v : set) {
   88            action(v);
   89            uitest::frame();
   90        }
   91        return *this;
   92    }
   93
  113    UIMatcher findNearestTo(UIMatcher matcher) {
  114        auto mySet = toSet();
  115        auto targets = matcher.toSet();
  116
  117        if (targets.size() != 1) {
  118            throw AException("expected to match one view, matched {}"_format(mySet.size()));
  119        }
  120        if (mySet.empty()) {
  121            throw AException("findNearestTo requires at least one element to match");
  122        }
  123
  124        auto nearestToView = (*targets.begin());
  125        auto nearestToPoint = glm::vec2(nearestToView->getPositionInWindow() + nearestToView->getSize());
  126        auto target = std::min_element(mySet.begin(), mySet.end(), [&](const _<AView>& lhs, const _<AView>& rhs) {
  127            float dst1 = glm::distance2(nearestToPoint, glm::vec2(lhs->getCenterPointInWindow()));
  128            float dst2 = glm::distance2(nearestToPoint, glm::vec2(rhs->getCenterPointInWindow()));
  129            return dst1 < dst2;
  130        });
  131        EXPECT_TRUE(target != mySet.end());
  132
  133        class ToOneMatcher: public IMatcher {
  134        public:
  135            explicit ToOneMatcher(_<AView> view) : mView(std::move(view)) {}
  136
  137            bool matches(const _<AView>& view) override {
  138                return view == mView;
  139            }
  140        private:
  141            _<AView> mView;
  142        };
  143
  144        return { _new<ToOneMatcher>(std::move(*target)) };
  145    }
  146
  147
  148    template<class Assertion>
  149    UIMatcher& check(Assertion&& assertion, const char* msg = "no msg") {
  150        mIncludeInvisibleViews = ignores_visibility<Assertion>::value;
  151        auto set = toSet();
  152        EXPECT_FALSE(set.empty()) << msg << ": empty set\n" << AStacktrace::capture(1);
  153
  154        performHintChecks<Assertion>(msg, set);
  155        for (auto& s : set) {
  156            EXPECT_TRUE(assertion(s)) << msg <<  "\n" << AStacktrace::capture(1);
  157        }
  158        return *this;
  159    }
  160
  161    [[nodiscard]]
  162    UIMatcher parent() const {
  163
  164        struct ParentMatcher: public IMatcher {
  165        private:
  166            _<IMatcher> childMatcher;
  167        public:
  168            ParentMatcher(const _<IMatcher>& childMatcher) : childMatcher(childMatcher) {}
  169
  170            bool matches(const _<AView>& view) override {
  171                if (auto container = _cast<AViewContainer>(view)) {
  172                    for (const auto& childView : container) {
  173                        if (childMatcher->matches(childView)) return true;
  174                    }
  175                }
  176                return false;
  177            }
  178        };
  179        return { _new<ParentMatcher>(mMatcher) };
  180    }
  181
  182    [[nodiscard]]
  183    UIMatcher allChildren() const {
  184
  185        struct ChildMatcher: public IMatcher {
  186        private:
  187            _<IMatcher> childMatcher;
  188        public:
  189            ChildMatcher(const _<IMatcher>& childMatcher) : childMatcher(childMatcher) {}
  190
  191            bool matches(const _<AView>& view) override {
  192                return childMatcher->matches(aui::ptr::fake(view->getParent()));
  193            }
  194        };
  195
  196        return { _new<ChildMatcher>(mMatcher) };
  197    }
  198
  199private:
  200
  201    template<class BinaryOperator>
  202    struct BinaryOperatorMatcher: public IMatcher {
  203    private:
  204        _<IMatcher> lhs;
  205        _<IMatcher> rhs;
  206    public:
  207        BinaryOperatorMatcher(const _<IMatcher>& lhs, const _<IMatcher>& rhs) : lhs(lhs), rhs(rhs) {}
  208
  209        bool matches(const _<AView>& view) override {
  210            return BinaryOperator()(lhs->matches(view), rhs->matches(view));
  211        }
  212    };
  213
  214public:
  215
  216    UIMatcher operator|(const UIMatcher& matcher) const {
  217        struct compare_or {
  218            bool operator()(bool lhs, bool rhs) const {
  219                return lhs || rhs;
  220            }
  221        };
  222        return { _new<BinaryOperatorMatcher<compare_or>>(mMatcher, matcher.mMatcher) };
  223    }
  224
  225    UIMatcher operator&(const UIMatcher& matcher) const {
  226        struct compare_and {
  227            bool operator()(bool lhs, bool rhs) const {
  228                return lhs && rhs;
  229            }
  230        };
  231        return { _new<BinaryOperatorMatcher<compare_and>>(mMatcher, matcher.mMatcher) };
  232    }
  233};
  234
  235
Abstract AUI exception.
Definition AException.h:28
A std::set with AUI extensions.
Definition ASet.h:25
static AStacktrace capture(unsigned skipFrames=0, unsigned maxFrames=128) noexcept
Creates stacktrace of the current thread.
A std::vector with AUI extensions.
Definition AVector.h:39
Definition IMatcher.h:18
Definition UIMatcher.h:21
UIMatcher findNearestTo(UIMatcher matcher)
Finds the nearest view to the specified one.
Definition UIMatcher.h:113
An std::weak_ptr with AUI extensions.
Definition SharedPtrTypes.h:179
static _< T > fake(T *raw)
Creates fake shared pointer to T* raw with empty destructor, which does nothing. It's useful when som...
Definition SharedPtrTypes.h:429