One approach that could address both these problems is to bypass the readline
 emulation and use the libedit API (histedit.h) directly.
-
-----------
-
-* migrate C++ tests to Catch2 v3
-
-It's been almost two years since Catch2 v3.0 was released. It's not backward
-compatible and requires some rework of the C++ test-suite.
 
 AM_CXXFLAGS = -I$(top_srcdir)/bindings/cxx/ -I$(top_srcdir)/include
 AM_CXXFLAGS += -I$(top_srcdir)/tests/gpiosim/
 AM_CXXFLAGS += -Wall -Wextra -g -std=gnu++17 $(CATCH2_CFLAGS)
-AM_LDFLAGS = -pthread
+AM_LDFLAGS = -pthread $(CATCH2_LIBS)
 LDADD = $(top_builddir)/bindings/cxx/libgpiodcxx.la
 LDADD += $(top_builddir)/tests/gpiosim/libgpiosim.la
 
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
 
-#define CATCH_CONFIG_MAIN
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 
 #ifndef __GPIOD_CXX_TEST_HELPERS_HPP__
 #define __GPIOD_CXX_TEST_HELPERS_HPP__
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <regex>
 #include <string>
 #include <sstream>
 #include <system_error>
 
-class system_error_matcher : public Catch::MatcherBase<::std::system_error>
+class system_error_matcher : public Catch::Matchers::MatcherBase<::std::system_error>
 {
 public:
        explicit system_error_matcher(int expected_errno);
        ::std::error_condition _m_cond;
 };
 
-class regex_matcher : public Catch::MatcherBase<::std::string>
+class regex_matcher : public Catch::Matchers::MatcherBase<::std::string>
 {
 public:
        explicit regex_matcher(const ::std::string& pattern);
        ::std::string _m_repr;
 };
 
-template<class T> class stringify_matcher : public Catch::MatcherBase<T>
+template<class T> class stringify_matcher : public Catch::Matchers::MatcherBase<T>
 {
 public:
        explicit stringify_matcher(const ::std::string& expected) : _m_expected(expected)
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2021-2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <gpiod.hpp>
 #include <sstream>
 
 
        SECTION("get chip name")
        {
-               REQUIRE_THAT(info.name(), Catch::Equals(sim.name()));
+               REQUIRE_THAT(info.name(), Catch::Matchers::Equals(sim.name()));
        }
 
        SECTION("get chip label")
        {
-               REQUIRE_THAT(info.label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(info.label(), Catch::Matchers::Equals("foobar"));
        }
 
        SECTION("get num_lines")
        {
                auto copy(info);
 
-               REQUIRE_THAT(copy.name(), Catch::Equals(sim.name()));
-               REQUIRE_THAT(copy.label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(copy.name(), Catch::Matchers::Equals(sim.name()));
+               REQUIRE_THAT(copy.label(), Catch::Matchers::Equals("foobar"));
                REQUIRE(copy.num_lines() == 4);
 
-               REQUIRE_THAT(info.name(), Catch::Equals(sim.name()));
-               REQUIRE_THAT(info.label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(info.name(), Catch::Matchers::Equals(sim.name()));
+               REQUIRE_THAT(info.label(), Catch::Matchers::Equals("foobar"));
                REQUIRE(info.num_lines() == 4);
        }
 
 
                copy = info;
 
-               REQUIRE_THAT(copy.name(), Catch::Equals(sim.name()));
-               REQUIRE_THAT(copy.label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(copy.name(), Catch::Matchers::Equals(sim.name()));
+               REQUIRE_THAT(copy.label(), Catch::Matchers::Equals("foobar"));
                REQUIRE(copy.num_lines() == 4);
 
-               REQUIRE_THAT(info.name(), Catch::Equals(sim.name()));
-               REQUIRE_THAT(info.label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(info.name(), Catch::Matchers::Equals(sim.name()));
+               REQUIRE_THAT(info.label(), Catch::Matchers::Equals("foobar"));
                REQUIRE(info.num_lines() == 4);
        }
 
        {
                auto moved(std::move(info));
 
-               REQUIRE_THAT(moved.name(), Catch::Equals(sim.name()));
-               REQUIRE_THAT(moved.label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(moved.name(), Catch::Matchers::Equals(sim.name()));
+               REQUIRE_THAT(moved.label(), Catch::Matchers::Equals("foobar"));
                REQUIRE(moved.num_lines() == 4);
        }
 
 
                moved = ::std::move(info);
 
-               REQUIRE_THAT(moved.name(), Catch::Equals(sim.name()));
-               REQUIRE_THAT(moved.label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(moved.name(), Catch::Matchers::Equals(sim.name()));
+               REQUIRE_THAT(moved.label(), Catch::Matchers::Equals("foobar"));
                REQUIRE(moved.num_lines() == 4);
        }
 }
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2021-2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <gpiod.hpp>
 #include <sstream>
 #include <system_error>
                        .build();
 
                ::gpiod::chip first(sim.dev_path());
-               REQUIRE_THAT(first.get_info().label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(first.get_info().label(), Catch::Matchers::Equals("foobar"));
                ::gpiod::chip second(::std::move(first));
-               REQUIRE_THAT(second.get_info().label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(second.get_info().label(), Catch::Matchers::Equals("foobar"));
        }
 }
 
 
                ::gpiod::chip moved_chip(moved_sim.dev_path());
 
-               REQUIRE_THAT(chip.get_info().label(), Catch::Equals("foobar"));
+               REQUIRE_THAT(chip.get_info().label(), Catch::Matchers::Equals("foobar"));
                chip = ::std::move(moved_chip);
-               REQUIRE_THAT(chip.get_info().label(), Catch::Equals("moved"));
+               REQUIRE_THAT(chip.get_info().label(), Catch::Matchers::Equals("moved"));
        }
 
        SECTION("boolean operator")
 
        SECTION("get device path")
        {
-               REQUIRE_THAT(chip.path(), Catch::Equals(sim.dev_path()));
+               REQUIRE_THAT(chip.path(), Catch::Matchers::Equals(sim.dev_path()));
        }
 
        SECTION("get file descriptor")
                            "\", label=\"foobar\", num_lines=4))";
 
                buf << chip;
-               REQUIRE_THAT(buf.str(), Catch::Equals(expected.str()));
+               REQUIRE_THAT(buf.str(), Catch::Matchers::Equals(expected.str()));
        }
 
        SECTION("closed chip")
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <chrono>
 #include <gpiod.hpp>
 #include <sstream>
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <chrono>
 #include <filesystem>
 #include <gpiod.hpp>
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <gpiod.hpp>
 
 #include "gpiosim.hpp"
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <gpiod.hpp>
 #include <string>
 
                auto info = chip.get_line_info(0);
 
                REQUIRE(info.offset() == 0);
-               REQUIRE_THAT(info.name(), Catch::Equals("foobar"));
+               REQUIRE_THAT(info.name(), Catch::Matchers::Equals("foobar"));
                REQUIRE(info.used());
-               REQUIRE_THAT(info.consumer(), Catch::Equals("hog"));
+               REQUIRE_THAT(info.consumer(), Catch::Matchers::Equals("hog"));
                REQUIRE(info.direction() == ::gpiod::line::direction::OUTPUT);
                REQUIRE_FALSE(info.active_low());
                REQUIRE(info.bias() == ::gpiod::line::bias::UNKNOWN);
                auto info6 = chip.get_line_info(6);
 
                REQUIRE(info4.offset() == 4);
-               REQUIRE_THAT(info4.name(), Catch::Equals("baz"));
+               REQUIRE_THAT(info4.name(), Catch::Matchers::Equals("baz"));
                REQUIRE(info4.used());
-               REQUIRE_THAT(info4.consumer(), Catch::Equals("hog4"));
+               REQUIRE_THAT(info4.consumer(), Catch::Matchers::Equals("hog4"));
                REQUIRE(info4.direction() == direction::OUTPUT);
                REQUIRE(info4.edge_detection() == edge::NONE);
                REQUIRE_FALSE(info4.active_low());
        {
                auto copy(info);
                REQUIRE(copy.offset() == 2);
-               REQUIRE_THAT(copy.name(), Catch::Equals("foobar"));
+               REQUIRE_THAT(copy.name(), Catch::Matchers::Equals("foobar"));
                /* info can still be used */
                REQUIRE(info.offset() == 2);
-               REQUIRE_THAT(info.name(), Catch::Equals("foobar"));
+               REQUIRE_THAT(info.name(), Catch::Matchers::Equals("foobar"));
        }
 
        SECTION("assignment operator works")
                auto copy = chip.get_line_info(0);
                copy = info;
                REQUIRE(copy.offset() == 2);
-               REQUIRE_THAT(copy.name(), Catch::Equals("foobar"));
+               REQUIRE_THAT(copy.name(), Catch::Matchers::Equals("foobar"));
                /* info can still be used */
                REQUIRE(info.offset() == 2);
-               REQUIRE_THAT(info.name(), Catch::Equals("foobar"));
+               REQUIRE_THAT(info.name(), Catch::Matchers::Equals("foobar"));
        }
 
        SECTION("move constructor works")
        {
                auto copy(::std::move(info));
                REQUIRE(copy.offset() == 2);
-               REQUIRE_THAT(copy.name(), Catch::Equals("foobar"));
+               REQUIRE_THAT(copy.name(), Catch::Matchers::Equals("foobar"));
        }
 
        SECTION("move assignment operator works")
                auto copy = chip.get_line_info(0);
                copy = ::std::move(info);
                REQUIRE(copy.offset() == 2);
-               REQUIRE_THAT(copy.name(), Catch::Equals("foobar"));
+               REQUIRE_THAT(copy.name(), Catch::Matchers::Equals("foobar"));
        }
 }
 
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <gpiod.hpp>
 #include <sstream>
 #include <stdexcept>
 
 namespace {
 
-class value_matcher : public Catch::MatcherBase<value>
+class value_matcher : public Catch::Matchers::MatcherBase<value>
 {
 public:
        value_matcher(pull pull, bool active_low = false)
                auto info = chip.get_line_info(2);
 
                REQUIRE(info.used());
-               REQUIRE_THAT(info.consumer(), Catch::Equals("foobar"));
+               REQUIRE_THAT(info.consumer(), Catch::Matchers::Equals("foobar"));
        }
 
        SECTION("empty consumer")
                auto info = chip.get_line_info(2);
 
                REQUIRE(info.used());
-               REQUIRE_THAT(info.consumer(), Catch::Equals("?"));
+               REQUIRE_THAT(info.consumer(), Catch::Matchers::Equals("?"));
        }
 }
 
                auto moved(::std::move(request));
 
                REQUIRE(moved.fd() == fd);
-               REQUIRE_THAT(moved.offsets(), Catch::Equals(offs));
+               REQUIRE_THAT(moved.offsets(), Catch::Matchers::Equals(offs));
        }
 
        SECTION("move assignment operator works")
                another = ::std::move(request);
 
                REQUIRE(another.fd() == fd);
-               REQUIRE_THAT(another.offsets(), Catch::Equals(offs));
+               REQUIRE_THAT(another.offsets(), Catch::Matchers::Equals(offs));
        }
 }
 
        {
                buf << request;
 
-               REQUIRE_THAT(buf.str(), Catch::Equals(expected.str()));
+               REQUIRE_THAT(buf.str(), Catch::Matchers::Equals(expected.str()));
        }
 
        SECTION("request released")
 
                buf << request;
 
-               REQUIRE_THAT(buf.str(), Catch::Equals("gpiod::line_request(released)"));
+               REQUIRE_THAT(buf.str(), Catch::Matchers::Equals("gpiod::line_request(released)"));
        }
 }
 
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <gpiod.hpp>
 
 #include "helpers.hpp"
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2021-2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <gpiod.hpp>
 
 #include "helpers.hpp"
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2021-2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <filesystem>
 #include <gpiod.hpp>
 #include <string>
 
 // SPDX-License-Identifier: GPL-2.0-or-later
 // SPDX-FileCopyrightText: 2021-2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-#include <catch2/catch.hpp>
+#include <catch2/catch_all.hpp>
 #include <cstddef>
 #include <gpiod.hpp>
 #include <string>
        SECTION("move constructor works")
        {
                auto moved(::std::move(cfg));
-               REQUIRE_THAT(moved.consumer(), Catch::Equals("foobar"));
+               REQUIRE_THAT(moved.consumer(), Catch::Matchers::Equals("foobar"));
                REQUIRE(moved.event_buffer_size() == 64);
        }
 
 
                moved = ::std::move(cfg);
 
-               REQUIRE_THAT(moved.consumer(), Catch::Equals("foobar"));
+               REQUIRE_THAT(moved.consumer(), Catch::Matchers::Equals("foobar"));
                REQUIRE(moved.event_buffer_size() == 64);
        }
 }
        SECTION("set consumer")
        {
                cfg.set_consumer("foobar");
-               REQUIRE_THAT(cfg.consumer(), Catch::Equals("foobar"));
+               REQUIRE_THAT(cfg.consumer(), Catch::Matchers::Equals("foobar"));
        }
 
        SECTION("set event_buffer_size")
 
        ::std::string expected("gpiod::request_config(consumer='foobar', event_buffer_size=32)");
 
-       REQUIRE_THAT(buf.str(), Catch::Equals(expected));
+       REQUIRE_THAT(buf.str(), Catch::Matchers::Equals(expected));
 }
 
 } /* namespace */
 
 
        if test "x$with_tests" = xtrue
        then
-               PKG_CHECK_MODULES([CATCH2], [catch2],, [
+               PKG_CHECK_MODULES([CATCH2], [catch2-with-main >= 3.0],, [
                        AC_LANG_PUSH([C++])
-                       AC_CHECK_HEADERS([catch2/catch.hpp], [], [HEADER_NOT_FOUND_CXX([catch2/catch.hpp])])
+                       AC_CHECK_HEADERS([catch2/catch_all.hpp], [],
+                                        [HEADER_NOT_FOUND_CXX([catch2/catch_all.hpp])])
                        AC_LANG_POP([C++])
                ])
        fi