From: Boyuan Yang <byang@debian.org>
Date: Wed, 27 Sep 2023 14:37:28 -0400
Subject: Add back vendor ftest test framework

A tiny test framework used by utfcpp project, handled
as git submodule. This patch adds the files back.

Forwarded: not-needed
---
 extern/ftest/.gitignore           |   4 +
 extern/ftest/LICENSE              |  25 ++++
 extern/ftest/README.md            |   1 +
 extern/ftest/ftest.h              | 232 ++++++++++++++++++++++++++++++++++++++
 extern/ftest/tests/CMakeLists.txt |  23 ++++
 extern/ftest/tests/smoke.cpp      |  83 ++++++++++++++
 6 files changed, 368 insertions(+)
 create mode 100644 extern/ftest/.gitignore
 create mode 100644 extern/ftest/LICENSE
 create mode 100644 extern/ftest/README.md
 create mode 100644 extern/ftest/ftest.h
 create mode 100644 extern/ftest/tests/CMakeLists.txt
 create mode 100644 extern/ftest/tests/smoke.cpp

diff --git a/extern/ftest/.gitignore b/extern/ftest/.gitignore
new file mode 100644
index 0000000..db63970
--- /dev/null
+++ b/extern/ftest/.gitignore
@@ -0,0 +1,4 @@
+# VS Code:
+.vscode/
+# Often used by CMake 
+build/
diff --git a/extern/ftest/LICENSE b/extern/ftest/LICENSE
new file mode 100644
index 0000000..f1bad2a
--- /dev/null
+++ b/extern/ftest/LICENSE
@@ -0,0 +1,25 @@
+// Copyright 2021 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
diff --git a/extern/ftest/README.md b/extern/ftest/README.md
new file mode 100644
index 0000000..7a0cd70
--- /dev/null
+++ b/extern/ftest/README.md
@@ -0,0 +1 @@
+This is a small and limited testing framework for C++.
\ No newline at end of file
diff --git a/extern/ftest/ftest.h b/extern/ftest/ftest.h
new file mode 100644
index 0000000..a7840af
--- /dev/null
+++ b/extern/ftest/ftest.h
@@ -0,0 +1,232 @@
+// Copyright 2021 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef F_TEST_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define F_TEST_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include <string>
+#include <vector>
+#include <iostream>
+
+namespace ftest
+{
+
+enum TestStatus {None, Success, Failed};
+
+class Test
+{
+public:
+    Test(const char* name, const char* test_case_name) : m_name(name),
+            m_status(None), m_test_case_name(test_case_name) {}
+    virtual ~Test() {}
+    const std::string& name() const { return m_name; }
+    const std::string& case_name() const { return m_test_case_name; }
+    TestStatus status() const { return m_status; }
+    virtual void run()
+    {
+        std::cout << "[ RUN      ] " << m_test_case_name << "." << m_name << std::endl;
+        m_status = Success;
+        run_internal(m_status);
+        if (m_status == Success) {
+            std::cout << "[       OK ] " << m_test_case_name << "." << m_name << std::endl;
+        } else {
+            std::cout << "[  FAILED  ] " << m_test_case_name << "." << m_name << std::endl;
+        }
+    }
+protected:
+    virtual void run_internal(TestStatus& status) = 0;
+private:
+    std::string m_name;
+    TestStatus m_status;
+    std::string m_test_case_name;
+};
+
+class TestCase
+{
+public: 
+    explicit TestCase(const char* name) : m_name(name) {}
+    const std::string& name() const { return m_name; }
+    void add_test(Test* test) { m_tests.push_back(test); }
+    size_t tests_count() const { return m_tests.size(); }
+    size_t status_count(TestStatus status) const
+    {
+        size_t ret = 0;
+        for (size_t i = 0; i < m_tests.size(); ++i)
+            if (m_tests[i]->status() == status)
+                ++ret;
+        return ret;
+    }
+    void run()
+    {
+        std::cout << "[----------] " << m_tests.size() << " tests from " << name() << std::endl;
+        for (size_t i = 0; i < m_tests.size(); ++i)
+            m_tests[i]->run();
+        std::cout << "[----------] " << m_tests.size() << " tests from " << name() << std::endl;
+    }
+    void print_failed()
+    {
+        for (size_t i = 0; i < m_tests.size(); ++i)
+            if (m_tests[i]->status() == Failed)
+                std::cout << "[  FAILED  ] " << m_name << "." << m_tests[i]->name() << std::endl;
+    }
+private:
+    std::string        m_name;
+    std::vector<Test*> m_tests;
+};
+
+inline TestCase* find_test_case(const std::string& name, const std::vector<TestCase*>& test_cases)
+{
+    for (size_t i = 0; i < test_cases.size(); ++i) {
+        if (test_cases[i]->name() == name)
+            return test_cases[i];
+    }
+    return 0;
+}
+
+class TestDriver
+{
+public:
+    bool add_test(Test* test)
+    {
+        m_tests.push_back(test);
+        TestCase* test_case = find_test_case(test->case_name(), m_test_cases);
+        if (!test_case) {
+            test_case = new TestCase(test->case_name().c_str());
+            m_test_cases.push_back(test_case);
+        }
+        test_case->add_test(test);
+        return true;
+    }
+
+    int run_tests() 
+    {
+        std::cout << "[==========] Running " << m_tests.size() <<
+            " tests from " << m_test_cases.size() << " test cases.\n";
+        
+        for (size_t i = 0; i < m_test_cases.size(); ++i)
+            m_test_cases[i]->run();
+
+        std::cout << "[==========] " << m_tests.size() <<
+            " tests from " << m_test_cases.size() << " test cases ran.\n";
+        size_t passed_count = 0, failed_count = 0;
+        for (size_t i = 0; i < m_test_cases.size(); ++i) {
+            passed_count += m_test_cases[i]->status_count(Success);
+            failed_count += m_test_cases[i]->status_count(Failed);
+        }
+        if (passed_count)
+            std::cout << "[  PASSED  ] " << passed_count << " tests.\n";
+        if (failed_count) {
+            std::cout << "[  FAILED  ] " << failed_count << " tests, listed below:\n";
+            for (size_t i = 0; i < m_test_cases.size(); ++i)
+                m_test_cases[i]->print_failed();
+            std::cout << "\n " << failed_count << " FAILED TESTS\n";
+        }
+
+        return static_cast<int>(failed_count);
+    }
+
+    const std::vector<TestCase*>& test_cases() const { return m_test_cases; }
+
+    ~TestDriver()
+    {
+        for (size_t i = 0; i < m_test_cases.size(); ++i)
+            delete m_test_cases[i];
+        for (size_t i = 0; i < m_tests.size(); ++i)
+            delete m_tests[i];
+    }
+private:
+    std::vector<TestCase*>  m_test_cases;
+    std::vector<Test*>      m_tests;
+};
+
+#ifdef F_TEST_NO_MAIN
+extern TestDriver testdriver;
+#else
+TestDriver testdriver;
+#endif
+
+} // namespace ftest
+
+#define EXPECT_TRUE(what) \
+do { \
+  if (!(what)) {\
+    std::cout << __FILE__ << ":" << __LINE__ << ": Failure\n"; \
+    std::cout << "  Expected : true\n    Actual : "; \
+    std::cout << (what ? "true" : "false"); \
+    std::cout << std::endl; \
+    status = ftest::Failed; \
+  } \
+} while (false)
+
+#define EXPECT_FALSE(what) \
+do { \
+  if (what) {\
+    std::cout << __FILE__ << ":" << __LINE__ << ": Failure\n"; \
+    std::cout << "  Expected : false\n    Actual : "; \
+    std::cout << (what ? "true" : "false"); \
+    std::cout << std::endl; \
+    status = ftest::Failed; \
+  } \
+} while (false)
+
+#define FTEST_EXPECT_PREDICATE(a, b, predicate_operator) \
+do { \
+  if (!(a predicate_operator b)) { \
+    std::cout << __FILE__ << ":" << __LINE__ << ": Failure\n"; \
+    status = ftest::Failed; \
+  } \
+} while (false)
+
+#define EXPECT_EQ(a, b) FTEST_EXPECT_PREDICATE(a, b, ==)
+#define EXPECT_NE(a, b) FTEST_EXPECT_PREDICATE(a, b, !=)
+#define EXPECT_LT(a, b) FTEST_EXPECT_PREDICATE(a, b, <)
+#define EXPECT_LE(a, b) FTEST_EXPECT_PREDICATE(a, b, <=)
+#define EXPECT_GT(a, b) FTEST_EXPECT_PREDICATE(a, b, >)
+#define EXPECT_GE(a, b) FTEST_EXPECT_PREDICATE(a, b, >=)
+
+#define TEST(f_test_case_name, f_test_name) \
+class F_TEST_##f_test_case_name##f_test_name : public ftest::Test \
+{ \
+public: \
+    F_TEST_##f_test_case_name##f_test_name(const char* test_name, const char* test_case_name) : \
+        ftest::Test(test_name, test_case_name) {} \
+    void run_internal(ftest::TestStatus& status); \
+    static bool m_registered; \
+}; \
+bool F_TEST_##f_test_case_name##f_test_name::m_registered = \
+    ftest::testdriver.add_test \
+    (new F_TEST_##f_test_case_name##f_test_name(#f_test_name, #f_test_case_name)); \
+void F_TEST_##f_test_case_name##f_test_name::run_internal(ftest::TestStatus& status)
+
+#ifndef F_TEST_NO_MAIN
+int main (int /*argc*/, const char** /*argv*/)
+{    
+    return ftest::testdriver.run_tests();
+}
+#endif //F_TEST_NO_MAIN
+
+#endif // F_TEST_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
diff --git a/extern/ftest/tests/CMakeLists.txt b/extern/ftest/tests/CMakeLists.txt
new file mode 100644
index 0000000..dac5274
--- /dev/null
+++ b/extern/ftest/tests/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required (VERSION 3.0.2)
+project (ftest VERSION 0.0.1 LANGUAGES CXX)
+
+enable_testing()
+
+add_executable(smoke smoke.cpp)
+target_include_directories (smoke PUBLIC
+    ../
+)
+
+set_target_properties(smoke
+                      PROPERTIES
+                      CXX_STANDARD 98
+                      CXX_STANDARD_REQUIRED YES
+                      CXX_EXTENSIONS NO)
+
+target_compile_options(smoke PRIVATE
+    $<$<CXX_COMPILER_ID:MSVC>:/W4>
+    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic>)
+                    
+
+
+add_test(smoke_test smoke)
diff --git a/extern/ftest/tests/smoke.cpp b/extern/ftest/tests/smoke.cpp
new file mode 100644
index 0000000..5f2f4d8
--- /dev/null
+++ b/extern/ftest/tests/smoke.cpp
@@ -0,0 +1,83 @@
+// Copyright 2021 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "ftest.h"
+#include <iostream>
+
+TEST(Simple, simple)
+{
+    EXPECT_TRUE(true);
+    EXPECT_TRUE(1);
+    EXPECT_TRUE(1 < 2);
+    EXPECT_TRUE(2.0 >= 0);
+}
+
+TEST(Simple, expectfalse)
+{
+    EXPECT_FALSE(false);
+    EXPECT_FALSE(1 == 0);
+    EXPECT_FALSE(1 > 2);
+    EXPECT_FALSE(3.4 == 3.41);
+}
+
+TEST(Predicates, expectequal)
+{
+    EXPECT_EQ(1, 1);
+    EXPECT_EQ(true, true);
+}
+
+TEST(Predicates, expectlessthan)
+{
+    EXPECT_LT(-1, 1);
+    EXPECT_LT(2., 2.1);
+}
+
+TEST(Predicates, expectlessorequal)
+{
+    EXPECT_LE(-1, 1);
+    EXPECT_LE(2., 2.1);
+    EXPECT_LE(1, 1);
+}
+
+TEST(Predicates, expectgreaterthan)
+{
+    EXPECT_GT(1, -1);
+    EXPECT_GT(2.2, 2.1);
+}
+
+TEST(Predicates, expectgreaterorequal)
+{
+    EXPECT_GE(1, -1);
+    EXPECT_GE(2.2, 2.1);
+    EXPECT_GE(1, 1);
+}
+
+TEST(Negative, expectnotequal)
+{
+    EXPECT_NE(1, 2);
+    EXPECT_NE(1., 2.);
+    EXPECT_NE(false, true);
+}
