From 36d36d49b10caeafd49b7b22a76ce7f80f671eb9 Mon Sep 17 00:00:00 2001 From: Liviu Ionescu Date: Sat, 18 Nov 2023 21:03:53 +0200 Subject: [PATCH] add website --- .github/workflows/publish-github-pages.yml | 122 + .gitignore | 2 + README.md | 1232 +-------- website/README.md | 53 + website/assets/wall-e-icon-64.png | Bin 0 -> 8785 bytes website/config.doxyfile | 246 ++ website/package-lock.json | 2302 +++++++++++++++++ website/package.json | 45 + website/pages/change-log.md | 32 + website/pages/credits.md | 21 + website/pages/developer.md | 896 +++++++ website/pages/home.md | 69 + website/pages/install.md | 236 ++ website/pages/license.md | 9 + .../pages/maintainer.md | 78 +- website/pages/testing.md | 616 +++++ 16 files changed, 4708 insertions(+), 1251 deletions(-) create mode 100644 .github/workflows/publish-github-pages.yml create mode 100644 website/README.md create mode 100644 website/assets/wall-e-icon-64.png create mode 100644 website/config.doxyfile create mode 100644 website/package-lock.json create mode 100644 website/package.json create mode 100644 website/pages/change-log.md create mode 100644 website/pages/credits.md create mode 100644 website/pages/developer.md create mode 100644 website/pages/home.md create mode 100644 website/pages/install.md create mode 100644 website/pages/license.md rename README-MAINTAINER.md => website/pages/maintainer.md (72%) create mode 100644 website/pages/testing.md diff --git a/.github/workflows/publish-github-pages.yml b/.github/workflows/publish-github-pages.yml new file mode 100644 index 0000000..8595466 --- /dev/null +++ b/.github/workflows/publish-github-pages.yml @@ -0,0 +1,122 @@ +# Simple workflow for deploying static content to GitHub Pages +name: GitHub Pages + +# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions + +# https://github.com/micro-os-plus/utils-lists-xpack/actions/workflows/publish-github-pages.yml +# https://micro-os-plus.github.io/utils-lists-xpack/ + +# TODO: remove xpack-develop + +on: + # Runs on pushes, if all conditions are met: + push: + # ... on the master branch ... + branches: + - 'xpack' + - 'xpack-develop' + # ... skip tags only ... + tags-ignore: + - '**' + # ... any of these files changes ... + paths: + - 'website/**' + - 'src/**' + - 'include/**' + - '.github/workflows/publish-github-pages.yml' + - 'package.json' + +# Allow one concurrent deployment +# https://docs.github.com/en/actions/using-jobs/using-concurrency +concurrency: + group: "pages" + cancel-in-progress: true + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +jobs: + + build: + name: Build the Doxygen pages + + strategy: + matrix: + # https://www.npmjs.com/package/xpm + xpm-version: [ '0.18.0' ] + # https://nodejs.org/en - use LTS + node-version: [ '18' ] + + # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + # https://github.com/actions/checkout + uses: actions/checkout@v4 + with: + fetch-depth: 3 + + - name: Use Node.js + # https://github.com/actions/setup-node + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Doxygen + # https://www.doxygen.nl/download.html + env: + DOXYGEN_VERSION: "1.9.8" + run: | + curl https://www.doxygen.nl/files/doxygen-$DOXYGEN_VERSION.linux.bin.tar.gz --output doxygen-$DOXYGEN_VERSION.linux.bin.tar.gz + tar xf doxygen-$DOXYGEN_VERSION.linux.bin.tar.gz + ls -l doxygen-$DOXYGEN_VERSION + mkdir -pv .local/bin + ln -sv $(pwd)/doxygen-$DOXYGEN_VERSION/bin/doxygen .local/bin/doxygen + ls -l + + - name: Install xpm@${{ matrix.xpm-version }} + run: | + npm install -g xpm@${{ matrix.xpm-version }} + + - name: Install dependencies + # For the build helper and the Doxygen theme. + run: | + xpm install + + - name: Build site with Doxygen + run: .local/bin/doxygen website/config.doxyfile + + - name: Configure Pages + # https://github.com/actions/configure-pages + uses: actions/configure-pages@v3 + + - name: Upload Pages artifact + # https://github.com/actions/upload-pages-artifact + uses: actions/upload-pages-artifact@v1 + with: + # Upload entire repository + path: './website/html' + + deploy: + name: Deploy to GitHub Pages + + needs: build + + # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources + runs-on: ubuntu-22.04 + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Deploy to GitHub Pages + id: deployment # referred by environment above, to get the URL. + # https://github.com/actions/deploy-pages + uses: actions/deploy-pages@v2 + with: + artifact_name: github-pages diff --git a/.gitignore b/.gitignore index e5d2223..a0e2df2 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,6 @@ build/ tests/top +website/html/ + # end diff --git a/README.md b/README.md index c9b1e75..8f0e07a 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![npm (scoped)](https://img.shields.io/npm/v/@micro-os-plus/micro-test-plus.svg?color=blue)](https://www.npmjs.com/package/@micro-os-plus/micro-test-plus/) [![license](https://img.shields.io/badge/license-MIT-blue)](https://github.com/micro-os-plus/micro-test-plus-xpack/blob/xpack/LICENSE) [![CI on Push](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/ci.yml/badge.svg)](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/ci.yml) +[![Website](https://img.shields.io/website?url=https%3A%2F%2Fmicro-os-plus.github.io%2Fmicro-test-plus-xpack%2F)](https://micro-os-plus.github.io/micro-test-plus-xpack/) # A source code library with µTest++, a lightweight testing framework for embedded platforms @@ -10,1235 +11,10 @@ The **µTest++** project (_micro test plus_) provides a small memory footprint testing framework, intended for running unit tests on embedded platforms. -The project is hosted on GitHub as -[micro-os-plus/micro-test-plus-xpack](https://github.com/micro-os-plus/micro-test-plus-xpack). +## [Project documentation](https://micro-os-plus.github.io/micro-test-plus-xpack/) -## Maintainer info - -This page is addressed to developers who plan to include this source -library into their own projects. - -For maintainer info, please see the -[README-MAINTAINER](README-MAINTAINER.md) file. - -## Install - -As a source library xPack, the easiest way to add it to a project is via -**xpm**, but it can also be used as any Git project, for example as a submodule. - -### Prerequisites - -A recent [xpm](https://xpack.github.io/xpm/), -which is a portable [Node.js](https://nodejs.org/) command line application. - -It is recommended to update to the latest version with: - -```sh -npm install --global xpm@latest -``` - -For details please follow the instructions in the -[xPack install](https://xpack.github.io/install/) page. - -### xpm - -This package is available as -[`@micro-os-plus/micro-test-plus`](https://www.npmjs.com/package/@micro-os-plus/micro-test-plus) -from the `npmjs.com` registry: - -```sh -cd my-project -xpm init # Unless a package.json is already present - -xpm install @micro-os-plus/micro-test-plus@latest - -ls -l xpacks/@micro-os-plus/micro-test-plus -``` - -### Git submodule - -If, for any reason, **xpm** is not available, the next recommended -solution is to link it as a Git submodule, preferably below an `xpacks` -folder. - -```sh -cd my-project -git init # Unless already a Git project -mkdir -p xpacks - -git submodule add \ - https://github.com/micro-os-plus/micro-test-plus-xpack.git \ - xpacks/@micro-os-plus/micro-test-plus -``` - -## Branches - -Apart from the unused `master` branch, there are two active branches: - -- `xpack`, with the latest stable version (default) -- `xpack-develop`, with the current development version - -All development is done in the `xpack-develop` branch, and contributions via -Pull Requests should be directed to this branch. - -When new releases are published, the `xpack-develop` branch is merged -into `xpack`. - -## Developer info - -The xPack Build Framework already includes ready to use -support for several testing frameworks: - -- [Google Test](https://github.com/xpack-3rd-party/googletest-xpack) -- [Catch2](https://github.com/xpack-3rd-party/catch2-xpack) -- [Boost UT](https://github.com/xpack-3rd-party/boost-ut-xpack) - -However, they all are quite heavy in terms of memory resources; also the -learning curve for mastering them is quite steep. - -Thus, for embedded projects, a simpler solution, with a smaller -memory footprint, was considered a useful addition. - -### Overview - -The initial version of the **µTest++** framework was inspired mainly by -[Node tap](https://node-tap.org) and aimed for simplicity. -The later v3.x was a full rework inspired by -[Boost UT](https://boost-ext.github.io/ut/). - -The main characteristics of µTest++, basically inherited from Boost UT, are: - -- intended to test **both C and C++** projects -- modern C++ 20 code (this was also the reason - to raise the bar to C++ 20 for the entire µOS++ project) -- **macro free** (while preserving the nice feature of being able to report - the file name and line number for failed tests) -- **expectations**, **assumptions**, **exceptions** -- **test cases**, **test suites** -- automatic test suites registration - -As major differentiator from Boost UT: - -- **reduced memory footprint**, since there are no dependencies on - the standard C++ stream library -- a slightly **simplified API** - -### Concepts and features - -- for complex applications, test cases can be grouped in test suites -- test suites can be located in separate compilation units; they automatically - register themselves to the runner; -- a **test suite** is a named sequence of test cases; -- a **test case** is a sequence of **test conditions** - (or simply **tests**, or **checks**), - which are expectations/assumptions, - i.e. conditions expected to be true; -- tests are based on logical expressions, which usually - compute a result and compare it to an expected value -- for C++ projects: it is also possible to check if, while evaluating - an expression, **exceptions** are thrown or not; -- each test either succeeds or fails; -- for **expectations**, the runner keeps counts of them; -- **assumptions** are hard conditions expected to be true in order for the test - to be able to run; -- failed assumptions abort the test; -- the test progress is shown on STDOUT, with each tests on a - separate line, prefixed with either a check sign (✓) or a cross sign (✗); -- failed tests display the location in the file and, if possible, - the actual values used in the expression evaluation; -- the main result of the test is passed back to the system as the process - exit code. - -A test suite is considered successful -if there is at least one successful expectation -and there are no failed tests. - -If all tests suites are successful, the process returns 0 as exit value. - -### ISTQB Glossary - -The **International Software Testing Qualification Board** defines some terms -used in testing frameworks: - -- **test condition**: a testable aspect of a component or system identified - as a basis for testing (implemented in µTest++ as calls to `expect()` or - `assume()` functions); -- **test case**: a set of preconditions, inputs, actions (where applicable), - expected results and postconditions, developed based on test conditions - (implemented in µTest++ as calls to the `test_case()` function) -- **test suite**: a set of test scripts or test procedures to be executed in - a specific test run (implemented in µTest++ as instances of the - `test_suite` class). - -For more details see: . - -### Getting started - -The absolute minimal test has a single test case, with a single expectation; -for example: - -```c++ -#include - -int -main(int argc, char* argv[]) -{ - using namespace micro_os_plus::micro_test_plus; - - initialize(argc, argv, "Minimal"); - - test_case ("Check truth", [] { - expect (true); - }) - - return exit_code (); -} -``` - -When running this test, the output looks like: - -```console -• Minimal - test suite started - - ✓ Check truth - test case passed (1 check) - -✓ Minimal - test suite passed (1 check in 1 test case) -``` - -A slightly more useful example would check the result of a computed value; -for example: - -```c++ -#include - -static int -compute_answer() -{ - return 42; -} - -int -main(int argc, char* argv[]) -{ - using namespace micro_os_plus::micro_test_plus; - - initialize(argc, argv, "The Answer"); - - test_case ("Check answer", [] { - expect (compute_answer() == 42) << "answer is 42"; - }); - - return exit_code (); -} -``` - -```console -• The Answer - test suite started - - ✓ Check answer - test case passed (1 check) - -✓ The Answer - test suite passed (1 check passed, 0 checks failed, in 1 test case) -``` - -In case the function returns the wrong answer, the test will fail; -for example: - -```c++ -static int -compute_answer() -{ - return 42 + 1; -} -``` - -In this case the test will fail with: - -```console -• The Answer - test suite started - - • Check answer - test case started - ✗ answer is 42 FAILED (answer.cpp:17) - ✗ Check answer - test case FAILED (0 checks passed, 1 check failed) - -✗ The Answer - test suite FAILED (0 checks passed, 1 check failed, in 1 test case) -``` - -The output identifies the failed test as located at line 17, but does not -provide more details, for example it does not tell what was the actual -wrong answer. - -To get such a useful information, the test should be slightly more elaborate, -and must use some custom comparators or operators; for example: - -```c++ -// ... - -int -main(int argc, char* argv[]) -{ - using namespace micro_os_plus::micro_test_plus; - - initialize(argc, argv, "The Answer"); - - test_case ("Check answer with comparator", [] { - expect (eq (compute_answer (), 42)) << "answer is 42"; - }); - - test_case ("Check answer with operator", [] { - using namespace micro_os_plus::micro_test_plus::operators; - using namespace micro_os_plus::micro_test_plus::literals; - - expect (compute_answer () == 42_i) << "answer is 42"; - expect (_i {compute_answer ()} == 42) << "answer is 42"; - }); - - return exit_code (); -} -``` - -The result would look like: - -```console -• The Answer - test suite started - - • Check answer with comparator - test case started - ✗ answer is 42 FAILED (answer.cpp:17, 43 == 42) - ✗ Check answer with comparator - test case FAILED (0 checks passed, 1 check failed) - - • Check answer with operator - test case started - ✗ answer is 42 FAILED (answer.cpp:24, 43 == 42) - ✗ answer is 42 FAILED (answer.cpp:25, 43 == 42) - ✗ Check answer with operator - test case FAILED (0 checks passed, 1 check failed) - -✗ The Answer - test suite FAILED (0 checks passed, 3 checks failed, in 2 test cases) -``` - -In the first case, `eq()` is a function that basically compares almost -everything and is able to keep track of the values of its operands. -There are similar functions for all comparisons. - -In the second case, a custom operator is used. To avoid interferences -with other operators, this custom operator is defined in a separate namespace -(which must -be explicitly referred to as shown) and matches only operands of -some specific types. - -To cast the integer constant `42` to such a specific type, a custom literal -is available (`_i`), which is also defined in a separate namespace. - -In addition to literals used to define constants, there are also definitions -which can be used to cast expressions. - -For the custom operators to match, it is necessary for at least one of -the operands -to be of the specific type, usually the constant using a literal, but if both -are expression, at least one of them must be casted. - -### C++ API - -Aiming simplicity, µTest++ provides only a very limited number of primitives -used to check expectations and assumptions. - -#### Expectations - -Expectations are checks whose results are counted and do not -break the test (as opposed to assumptions, which abort the test). - -```C++ -template > -bool expect(const Expr_T& expr); -``` - -The template matches only expressions that evaluate to -a boolean or use custom comparators/operators derived from an -internal `detail::op` type. - -For generic checks performed outside the testing framework, the results can -be reported with `expect(true)` or `expect(false)` (see the example testing -multiple exceptions below). - -#### Assumptions - -Assumptions are checks that abort the test if the results are false. - -```C++ -template > -bool assume(const Expr_T& expr); -``` - -Similarly, the template matches only expressions that evaluate to -a boolean and use custom comparators/operators derived from a -internal `detail::op` type. - -#### Function comparators - -Expectations and assumptions can test any expression evaluating to a -boolean value, but in order to nicely report the difference between expected -and actual values in failed -conditions, the following generic comparators are available: - -```c++ -template -auto eq(const Lhs_T& lhs, const Rhs_T& rhs); - -template -auto ne(const Lhs_T& lhs, const Rhs_T& rhs); - -template -auto lt(const Lhs_T& lhs, const Rhs_T& rhs); - -template -auto le(const Lhs_T& lhs, const Rhs_T& rhs); - -template -auto gt(const Lhs_T& lhs, const Rhs_T& rhs); - -template -auto ge(const Lhs_T& lhs, const Rhs_T& rhs); -``` - -Similar templates are defined for pointer comparators. - -Examples: - -```c++ -expect (eq (compute_answer (), 42)) << "answer is 42"; -expect (ne (compute_answer (), 43)) << "answer is not 43"; -expect (lt (compute_answer (), 43)) << "answer is < 43"; -expect (le (compute_answer (), 43)) << "answer is <= 42"; -expect (gt (compute_answer (), 41)) << "answer is > 43"; -expect (ge (compute_answer (), 42)) << "answer is >= 42"; - -expect (compute_condition ()) << "condition is true"; -``` - -When such comparator functions are used, failed checks also display the -actual values compared during the test; for example: - -```console - Check failed comparisons - ✗ actual != 42 FAILED (unit-test.cpp:286, 42 != 42) - ✗ FAILED (unit-test.cpp:307, 42 != 42) - ✗ 42 != 42_i FAILED (unit-test.cpp:310, 42 != 42) - ✗ (actual == 42) and (actual != 42.0) FAILED (unit-test.cpp:781, (42 == 42 and 42.000000 != 42.000000)) -``` - -### Logical function operators - -Complex expressions can be checked in a single line, using the logical -`_and()`, `_or()` and `_not()` functions. The names are prefixed with -underscore since -`and`, `or` and `not` are reserved words in C/C++. - -```c++ -template -auto _and (const Lhs_T& lhs, const Rhs_T& rhs); - -template -auto _or (const Lhs_T& lhs, const Rhs_T& rhs); - -template -auto _not (const Expr_T& expr); -``` - -Examples: - -```c++ -expect(_and (eq (compute_answer (), 42), eq (compute_float (), 42.0))); -``` - -A slightly more readable syntax is available with custom operators, -as shown below. - -#### Comparing strings - -In C/C++, plain strings are actually pointers to characters, and simply -comparing them does not compare the content but the memory addresses. - -For string comparisons to compare the content, use `string_view` objects: - -```c++ -#include -using namespace std::literals; // For the "sv" literal. -// ... - -expect (eq (std::string_view{ compute_ultimate_answer () }, "forty-two"sv)) - << "ultimate_answer is 'forty-two'"; -``` - -#### Comparing containers - -Containers can be compared for equality. The comparison -is done by iterating and comparing each member. - -```c++ -expect (eq (std::vector{ 1, 2 }, std::vector{ 1, 2 })) - << "vector{ 1, 2 } eq vector{ 1, 2 }"; - -expect (ne (std::vector{ 1, 2, 3 }, std::vector{ 1, 2, 4 }) - << "vector{ 1, 2, 3 } ne vector{ 1, 2, 4 }"; -``` - -#### Operators - -As in most other C++ test frameworks, it is -also possible to overload the `==`, `!=`, `<`, `>`, `<=`, `>=` operators. - -To avoid possible interferences with other operators -defined by the application, these operators match only for operands of -specific types and are located in a separate namespace -(`micro_test_plus::operators`); when applied to regular values, the -standard operands are used; the comparisons are performed properly, -but in case of failures the actual values are not shown. - -The following operators match only operands derived from the local -`detail::op` type, which can be enforced for constant values by using the -provided literals (like `1_i`) or, for dynamic values, by using the -provided casts (like `_i {expression}`, which are actually the -constructors of the internal classes): - -```c++ -template > -bool operator== (const Lhs_T& lhs, const Rhs_T& rhs); - -template > -bool operator!= (const Lhs_T& lhs, const Rhs_T& rhs); - -template > -bool operator< (const Lhs_T& lhs, const Rhs_T& rhs); - -template > -bool operator<= (const Lhs_T& lhs, const Rhs_T& rhs); - -template > -bool operator> (const Lhs_T& lhs, const Rhs_T& rhs); - -template > -bool operator>= (const Lhs_T& lhs, const Rhs_T& rhs); -``` - -Examples: - -```c++ -test_case ("Operators", [] { - using namespace micro_test_plus::operators; - using namespace micro_test_plus::literals; - - expect (compute_answer () == 42_i) << "answer is 42 (with literal)"; - expect (_i {compute_answer ()} == 42) << "answer is 42 (with cast)"; - expect (compute_answer () != 43_i) << "answer is not 43"; - expect (compute_answer () < 43_i) << "answer is < 43"; - expect (compute_answer () <= 43_i) << "answer is <= 42"; - expect (compute_answer () > 41_i) << "answer is > 43"; - expect (compute_answer () >= 42_i) << "answer is >= 42"; -}); -``` - -In addition, equality operators are also provided for `string_view` -objects and for iterable containers: - -```c++ -bool operator== (std::string_view lhs, std::string_view rhs); -bool operator!= (std::string_view lhs, std::string_view rhs); - -template >> -bool operator== (T&& lhs, T&& rhs); - -template >> -bool operator!= (T&& lhs, T&& rhs); -``` - -Examples: - -```c++ -#include -using namespace std::literals; // For the "sv" literal. -// ... - -test_case ("Operators", [] { - using namespace micro_test_plus::operators; - - expect (std::string_view{ compute_ultimate_answer () } == "forty-two"sv) - << "ultimate answer == 'forty-two'"; - - expect (std::vector{ 1, 2 } == std::vector{ 1, 2 }) - << "vector{ 1, 2 } == vector{ 1, 2 }"; - - expect (std::vector{ 1, 2, 3 } != std::vector{ 1, 2, 4 }) - << "vector{ 1, 2, 3 } != vector{ 1, 2, 4 }"; -}); -``` - -#### Logical operators - -Similarly, logical operators are defined: - -```c++ -template > -bool operator and (const Lhs_T& lhs, const Rhs_T& rhs); - -template > -bool operator or (const Lhs_T& lhs, const Rhs_T& rhs); - -template > -bool operator not (const T& t); -``` - -They can be used in exactly the same way as standard operators, but the -additional functionality is enabled only when matching the typed operands. - -Examples: - -```c++ -expect (compute_answer () == 42_i && compute_float () == 42.0_f); -``` - -#### Literals and wrappers - -For converting constants to recognised typed operands, the following -literal operators are available in the separate namespace `literals`: - -```c++ -namespace literals { - auto operator""_i (); // int - auto operator""_s (); // short - auto operator""_c (); // char - auto operator""_sc () // signed char - auto operator""_l (); // long - auto operator""_ll (); // long long - auto operator""_u (); // unsigned - auto operator""_uc (); // unsigned char - auto operator""_us (); // unsigned short - auto operator""_ul (); // unsigned long - auto operator""_ull (); // unsigned long long - auto operator""_i8 (); // int8_t - auto operator""_i16 (); // int16_t - auto operator""_i32 (); // int32_t - auto operator""_i64 (); // int64_t - auto operator""_u8 (); // uint8_t - auto operator""_u16 (); // uint16_t - auto operator""_u32 (); // uint32_t - auto operator""_u64 (); // uint64_t - auto operator""_f (); // float - auto operator""_d (); // double - auto operator""_ld (); // long double - auto operator""_b (); // bool -} -``` - -Similarly, for dynamic values, there are wrappers that convert them to -recognised types: - -```c++ - using _b = type_traits::value; - using _c = type_traits::value; - using _sc = type_traits::value; - using _s = type_traits::value; - using _i = type_traits::value; - using _l = type_traits::value; - using _ll = type_traits::value; - using _u = type_traits::value; - using _uc = type_traits::value; - using _us = type_traits::value; - using _ul = type_traits::value; - using _ull = type_traits::value; - using _i8 = type_traits::value; - using _i16 = type_traits::value; - using _i32 = type_traits::value; - using _i64 = type_traits::value; - using _u8 = type_traits::value; - using _u16 = type_traits::value; - using _u32 = type_traits::value; - using _u64 = type_traits::value; - using _f = type_traits::value; - using _d = type_traits::value; - using _ld = type_traits::value; - - // Template for wrapping any other type. - template - struct _t : type_traits::value - { - constexpr explicit _t (const T& t) : type_traits::value{ t } - { - } - }; -``` - -Examples: - -```c++ -expect (_i {answer} == 42_i); -expect (_f {expression} == 42_f); -``` - -#### Function comparators vs. operators & literals - -Which to use, functions like `eq()` or the -overloaded operators? A very good question! - -Functions guarantee that the nice feature of showing the actual values -when expectations fail is always available. Also the syntax is more on the -traditional side, and for some it may look simpler and easier to read. - -Operators are generally easier to recognise than function calls, -but require the hack with the type wrappers and literals to enforce the -types, otherwise the actual values will not be displayed when the -expectations fail. - -Both syntaxes are functional, and, once the differences understood, -the issue is a matter of personal preferences. - -For example, the µOS++ projects favour explicit comparator functions. - -#### Explicit namespace - -If for any reasons, the definitions in the `micro_test_plus` namespace -interfere with application definitions, it is recommended to -use the comparator functions, which can be more easily invoked -with explicit namespaces, possibly aliased to shorter names. - -Example: - -```c++ -{ - namespace mt = micro_os_plus::micro_test_plus; - - mt::test_case ("Check answer", [] { - mt::expect (mt::eq (compute_answer (), 42)) << "answer is 42"; - }); -} -``` - -#### Exceptions - -A C++ testing framework must be able to check if an expression -(usually a function call), throws or not an exception. - -The following function templates allow to check various exceptions related -conditions: - -```C++ -// Check for any exception. -template -auto throws (const Callable_T& expr); - -// Check for a specific exception. -template -auto throws (const Callable_T& expr); - -// Check for no exception at all. -template -auto nothrow (const Callable_T& expr); -``` - -Examples: - -```c++ -expect (throws ([] { exercise_throw (true); })) << "exception thrown"; - -expect (throws ([] { throw std::runtime_error{ "" }; })) - << "std::runtime_error thrown"; - -expect (nothrow ([] { exercise_throw (false); })) << "exception not thrown"; -``` - -If a more elaborate logic is required, for example for expecting multiple -exceptions, use an explicit `try` with multiple `catch` statements and -report the results with `expect(true)` or `expect(false)`. - -```c++ -try - { - compute_answer (); - } -catch (const std::overflow_error& e) - { - expect (true) << "std::overflow_error thrown"; - } -catch (const std::runtime_error& e) - { - expect (true) << "std::runtime_error thrown"; - } -catch (...) - { - expect (false) << "known exception thrown"; - } -``` - -#### Test cases - -Test cases group several checks done in the same environment. - -There can be any number of test cases, and each test case is performed -by invoking -a function, parametrised with a name, a callable (usually a lambda), -and optional arguments: - -```C++ -template -void test_case (const char* name, Callable_T&& func, Args_T&&... arguments); -``` - -Examples: - -```c++ -using namespace micro_os_plus::micro_test_plus; - -test_case ("Check various conditions", [] { - expect (eq (compute_answer (), 42)) << "answer eq 42"; - expect (ne (compute_answer (), 43)) << "answer ne 43"; -}); - -test_case ("Check various conditions with operators", [] { - using namespace micro_os_plus::micro_test_plus::operators; - using namespace micro_os_plus::micro_test_plus::literals; - - expect (compute_answer () == 42_i) << "answer == 42"; - expect (compute_answer () != 43_i) << "answer != 43"; -}); -``` - -#### Test runner initialization - -The test runner is initialised with the process arguments and a -name, which is used for the default test suite: - -```C++ -void initialize (int argc, char* argv[], const char* name = "Main"); -``` - -The arguments can be used for controlling the verbosity level. - -#### Return the test result - -The final test result that must be returned to the system -(0 for pass, 1 for fail), can be obtained with: - -```C++ -int exit_code (void); -``` - -This call also triggers the execution of all global test suites. - -For examples, see before. - -#### Test suites - -Test suites are named sequences of test cases. - -The test cases defined in `main()` are considered to be part of -the default (or main) test suite, and are executed immediately -when invoked. - -For complex applications there can be multiple test -suites, usually in separate source files. - -In order to make self-registration possible, test suites are classes, -constructed with a name, a callable (usually a lambda -which chains the execution of the test cases) and optional -arguments: - -```C++ -class test_suite : public test_suite_base -{ -public: - template - test_suite (const char* name, Callable_T&& callable, - Args_T&&... arguments); - // ... -} -``` - -It is recommended to instantiate the test suites as static objects. - -The self-registration is done in the constructor. -Test suites defined in different compilation units can be executed in any -order (since the order in which the -static constructors are invoked is not specified); -thus there should be no dependencies between test suites. - -The registered test suites are executed when the function -`exit_code()` is invoked. - -Examples: - -```c++ -// Test suite with generic parameters. -static void -test_suite_args (int ic, int iv, int& ir, int* ip1, int* ip2) -{ - using namespace micro_os_plus::micro_test_plus; - - test_case ("args", [&] { - expect (eq (ic, 42)) << "ic is 42"; - expect (eq (iv, 43)) << "iv is 43"; - expect (eq (ir, 44)) << "ir is 44"; - expect (eq (*ip1, 45)) << "*ip1 is 45"; - expect (eq (*ip2, 46)) << "*ip2 is 46"; - }); -} - -static int in = 43; -static int in44 = 44; -static int& ir = in44; -static int in45 = 45; -static int in46 = 46; -static int* ip2 = &in46; - -static micro_os_plus::micro_test_plus::test_suite ts_args - = { "Args", test_suite_args, 42, in, ir, &in45, ip2 }; -``` - -#### Utility functions - -For tests comparing strings, in addition to exact matches, it is also possible -to check matches with patterns like `*` (for any characters) and `?` (for a -single character): - -```c++ -namespace utility { - bool is_match (std::string_view input, std::string_view pattern); -} -``` - -Examples: - -```c++ -expect (utility::is_match ("abc", "a?c")) << "abc matches a?c"; -expect (utility::is_match ("abc", "a*c")) << "abc matches a*c"; -``` - -Also for tests handling strings, the following function template allows to -split a string into a vector of substrings, using a delimiter: - -```c++ -namespace utility { - template - auto split (T input, Delim_T delim) -> std::vector; -} -``` - -Example: - -```c++ -expect (std::vector{ "a", "b" } - == utility::split ("a.b", ".")) - << "a.b splits into [a,b]"; -``` - -#### Custom types - -It is possible to extend the comparators with templates matching custom -types, but this is a non-trivial task and requires a good knowledge of -C++. - -TODO: add a test to show how to do this. - -### Command line options - -#### Verbosity - -By default, the test reporter shows detailed results only for the failed -test cases; successful test cases are shown as a single line with -the total counts of passed/failed checks. - -To control the verbosity, use one of the following command line options: - -- `--verbose` - show all expectations, regardless of the result -- `--quiet` - show only the test suite totals -- `--silent` - suppress all output and only return the exit code - -### Memory footprint - -The memory footprint of unit tests based on µTest++ is definitely smaller than -that of traditional C++ testing framework, mainly because the `iostream` -library is not used. - -However, the use of templates for implementing the comparators and -operators should be carefully observed for platforms with really -limited amounts of memory, since each pair of different operands -contributes to the program size. - -At the limit, µTest++ can be used -without custom comparators and operators -(only with regular boolean expressions), and still be able to provide -the basic functionality of testing various conditions, but without -the optional features of displaying the actual values compared. - -Also, please note that the memory footprint on `debug` (built with `-O0`), -is significantly larger than on `release`. If necessary, the optimization -for the `debug` build can be increased to `-Og`, to save some memory. - -### Status - -The **µTest++** source library is fully functional and is used to test the -µOS++ packages. - -### Build & integration info - -The project is written in C++, and the tests are expected to be -written in C++ too, but the tested code can also be written in plain C. -The framework source code was compiled with GCC 11, clang 12 -and arm-none-eabi-gcc 10, and should be warning free. - -To run **on embedded platforms**, the test framework requires a minimum -of support from the system, like writing to the -output stream. Any such environments are acceptable, but for standalone -tests the most common solution is to use **Arm semihosting**. - -To ease the integration of this package into user projects, there -are already made **CMake** and **meson** configuration files (see below). - -For other build systems, consider the following details: - -#### Include folders - -The following folders should be passed to the compiler during the build: - -- `include` - -The header files to be included in user projects are: - -```c++ -#include -``` - -#### Source files - -The source files to be added to user projects are: - -- `src/micro-test-plus.cpp` -- `src/test-reporter.cpp` -- `src/test-runner.cpp` -- `src/test-suite.cpp` - -#### Preprocessor definitions - -- `MICRO_OS_PLUS_INCLUDE_CONFIG_H` - to include `` -- `MICRO_OS_PLUS_TRACE` - to include the trace calls -- `MICRO_TEST_PLUS_TRACE` - to enable some tracing messages - -#### Compiler options - -- `-std=c++20` or higher for C++ sources - -#### C++ Namespaces - -- `micro_os_plus::micro_test_plus` -- `micro_os_plus::micro_test_plus::operators` -- `micro_os_plus::micro_test_plus::literals` -- `micro_os_plus::micro_test_plus::utility` - -`micro_os_plus` is the top µOS++ namespace, and `micro_test_plus` is the -µTest++ namespace. - -The `operators` namespace defines the custom operators, and the `literals` -namespace defines the literals (like `1_i`); - -#### C++ Classes - -- `micro_os_plus::micro_test_plus::test_suite` - -#### Dependencies - -- none - -#### CMake - -To integrate the µTest++ source library into a CMake application, -add this folder to the build: - -```cmake -add_subdirectory("xpacks/@micro-os-plus/micro-test-plus")` -``` - -The result is an interface library that can be added as an application -dependency with: - -```cmake -target_link_libraries(your-target PRIVATE - - micro-os-plus::micro-test-plus -) -``` - -#### meson - -To integrate the µTest++ source library into a meson application, -add this folder to the build: - -```meson -subdir('xpacks/@micro-os-plus/micro-test-plus') -``` - -The result is a dependency object that can be added -to an application with: - -```meson -exe = executable( - your-target, - link_with: [ - # Nothing, not static. - ], - dependencies: [ - micro_os_plus_micro_test_plus_dependency, - ] -) -``` - -### Examples - -An example showing how to use the µTest++ framework is -available in -[tests/src/sample-test.cpp](tests/src/sample-test.cpp). - -Here are some excerpts: - -```c++ -#include - -// ---------------------------------------------------------------------------- - -// ... - -// The test suite. -int -main (int argc, char* argv[]) -{ - using namespace micro_os_plus::micro_test_plus; - - initialize (argc, argv, "Sample"); - - test_case ("Check various conditions", [] { - expect (eq (compute_answer (), 42)) << "answer is 42"; - expect (ne (compute_answer (), 43)) << "answer is not 43"; - expect (compute_condition ()) << "condition() is true"; - }); - - test_case ("Check various conditions with operators", [] { - using namespace micro_test_plus::operators; - using namespace micro_test_plus::literals; - - expect (compute_answer () == 42_i) << "answer == 42 (with literal)"; - expect (_i {compute_answer ()} == 42) << "answer == 42 (with cast)"; - expect (compute_answer () != 43_i) << "answer != 43"; - }); - - test_case ("Check parametrised", [] { - auto f = [] (int i) { return i + 42; }; - expect (eq (f (1), 43)) << "lambda == 43"; - }); - -#if defined(__EXCEPTIONS) - - test_case ("Check exceptions", [] { - auto exercise_throw = [] { throw std::runtime_error{ "" }; } - expect (throws (exercise_throw)) - << "std::runtime_error thrown"; - }); - -#endif // defined(__EXCEPTIONS) - - return exit_code (); -} - -// ---------------------------------------------------------------------------- -``` - -The output of running such a test looks like: - -```console -$ cd micro-test-plus-xpack.git/tests -$ xpm run install-all -... -$ xpm run test-native -... -> Executing task: xpm run test --config native-cmake-sys-release < - -> cd build/native-cmake-sys-release && ctest -V -... - Start 1: sample-test - -1: Test command: /Users/ilg/My\ Files/WKS\ Projects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/build/native-cmake-release/platform-bin/sample-test "one" "two" -1: Test timeout computed to be: 10000000 -1: Built with clang Apple LLVM 13.0.0 (clang-1300.0.29.30), no FP, with exceptions. -1: -1: • Sample - test suite started -1: -1: • Check various conditions - test case started -1: ✓ compute_one() == 1 -1: ✓ compute_aaa() == 'aaa' -1: ✓ condition() is true -1: ✓ Check various conditions - test case passed (3 checks) -1: -1: • Check parametrised - test case started -1: ✓ lambda == 43 -1: ✓ Check parametrised - test case passed (1 check) -1: -1: • Check exceptions - test case started -1: ✓ std::runtime_error thrown -1: ✓ Check exceptions - test case passed (1 check) -1: -1: ✓ Sample - test suite passed (5 tests in 3 test cases) -1/2 Test #1: sample-test ...................... Passed 0.00 sec -... -``` - -### Known problems - -- none - -### TODO - -- add code to show how to define custom comparators -- move documentation to future µOS++ web site - -### Tests - -The project is fully tested via GitHub -[Actions](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/) -on each push. - -The test platforms are **GNU/Linux**, **macOS** and **Windows**; native tests -are compiled with GCC and clang; tests for embedded platforms are compiled -with **arm-none-eabi-gcc** and run via **QEMU**. - -There are two sets of tests, one that runs on every push, with a -limited number of tests, and a set that is triggered manually, -usually before releases, and runs all tests on all supported -platforms. - -The full set can be run manually with the following commands: - -```sh -xpm run install-all -C ~/Work/micro-os-plus/micro-test-plus-xpack.git/tests -xpm run test-all -C ~/Work/micro-os-plus/micro-test-plus-xpack.git/tests -``` - -## Change log - incompatible changes - -According to [semver](https://semver.org) rules: - -> Major version X (X.y.z | X > 0) MUST be incremented if any -backwards incompatible changes are introduced to the public API. - -The incompatible changes, in reverse chronological order, -are: - -- **v3.x**: major rework, with full set of comparators, exceptions, - function templates for test cases and class templates for test suites; -- **v2.3.x**: deprecate `run_test_case(func, name)` in favour of - `run_test_case(name, func)`, to prepare for variadic templates -- **v2.x**: the C++ namespace was renamed from `os` to `micro_os_plus`; -- **v1.x**: the initial code (inspired in its simplicity by node-tap) - was extracted from the mono-repo µOS++ project. - -## Credits - -Many thanks to the [Boost UT](https://github.com/boost-ext/ut) project -for the inspiration and for major parts of the code, and to the -[node-tap](https://github.com/tapjs/node-tap) project for the -inspiration for the initial versions. +For information on how to install and use this library, please refer to the +[project documentation](https://micro-os-plus.github.io/micro-test-plus-xpack/) web site. ## License diff --git a/website/README.md b/website/README.md new file mode 100644 index 0000000..ca6b26e --- /dev/null +++ b/website/README.md @@ -0,0 +1,53 @@ +# Website + +The project website is generated with [Doxygen](https://www.doxygen.nl). + +## Build + +To build the site, run the following from the top project folder: + +```sh +doxygen website/config.doxygen +``` + +The result is in `website/html`. + +## Content + +The input folders are: + +- `src` +- `include` +- `website/pages/...` + +The order of listing the `pages` is also the order of rendering the +entries in the sidebar. + +## GitHub Pages + +The web site is published by the `publish-github-pages.yml` GitHub Actions workflow: + +- + +The project GitHub Pages address is: + +- + +## Doxygen usual commands + +``` +@n (
) +@note +@warning +@todo +``` + +No `@example`. + +## Theme + +The project uses the custom theme. + +## TODO + +- nothing diff --git a/website/assets/wall-e-icon-64.png b/website/assets/wall-e-icon-64.png new file mode 100644 index 0000000000000000000000000000000000000000..167927ea252ece80debfdd64753442a55291d21b GIT binary patch literal 8785 zcmY*AwX~o?hXNhI|O$N5D4;;d+)pV zz0<2s?Nevhu2a4GN3Yc#t)?Q2fl7)B0|SF0FDIq($BX?nWW+ynrQOHPKMvMiLskN& z<{SC(pGBCJp1ifPG7Rt!MutIv#f5?YOZjubz>>ls{0qatD8Q2a7i+*W{)Ynx0~2Wn z^X@+!gFpW7ko#kQvj5}Z3t|7KSP1uDwpbzj|M1_mW(QE?ABW;9r|%8}gNFClU}3Uz ziT|kS+iB@}=qW1+ShzT`nOVA+Td{$iT>oal2!RFuKqo5?Gb*r?qqDmJSeWKt41quR zZx}>F^)HHtgD{PrvKp17i<=b{FB>}>JB2__Tm?WN zFE1}PFD^D0HyaQKKR-W+ofE{#$@+)E>hA6AVFqS(cBlQf$p6)mvU0a@vvc*Zb8)8n zt7~TN;^`qwL-V)Lf5*Si>0xL6ze>*T|7q(_gP^}VAPzQm(0{f6qze6w3aGl-S^cT} zS6_rf=wHnLFZQ20LZH9J|DVJByVHL~|FkNCDg^rPZ4*HyG1)fUwJZ{(Gtpjd{^Bpc^PCG7Ma&5=mwK0HTpxp?@o3Hc6B&(4k< zMPi~e#-K%wxx?s(%L&k7M|s$s)8}n=Xc})cUrto}Uj8mn0OGNWNfZ}s3BKeBJm}>4 zUOc+=+`hd{+ij~FJ;CIgj8*yj`g(QaBAOvpR_vGJ-M*7-m5A(Z^H!T{7Kf`h#+t5%_qFtqm_r zzxVM2nT1*-9hv9S&8_EjcZRAsukYX2Y;lu}t>aGvYE{bI9pIWhCA@67G!RH4v9NY8 zYmFnawDd`*9KLR!!MWHU?XVIu7vE~ydb!sLk{@k(!Nj_A6%eD*2gpcqnQ=^g{k&hC zB?kGW+wFd+Y?ASkTmim!Zq)a(qN_srX>>;|GE?79KIL}O5EMT_6e|~gU{+Z{V7<%V z{lRhD?;ntPGCfZgL@Q56O}F+W?c@Ybf-|qj-p~3Kwpc**t{64$V@ky$0#U?~(#^nw zO9yM(q_96@Zil13ZaMDs#Qy5NqE(4W?Kf3&A|go~v$sC^1e5W2*1@(iSNx(fx;+WZ z4zKR0#{r}^i{GDaBNGy?5j$i%?6pm%$8p9l1gs@VN2z>%Ury0g41Sf#1e|JScI`M@ug_F%%qYW#&D)O zv&TF^b4Tog?&LbDh&;|%aXu6VYpW}MzMn;iHtTn%RpHwsB7mtkh8~S7Rj?r|Y^}V! zQ6{FWIBQX(^#ghYb^ZJE1Er~EGuM@sKqHoHDQ*bJuqg?F(}}G)YWD?1%Lcis5EMZu zSVGIwLCcvMC;nhT`Es@^jPFC;>afJ7uHA}u_B2V&rUzdKiCBYOZ-Tme0dpdY-8wg{ zLl^9aFy0$QUMH(7QGzjc%DC+_cs^%*Ir(AY{BL1y>&M_f!rPS9q8s+8eWPSYpd=25@id{B5_|Oie zJ20@pJ%fpkfVbl(q%}%%dgp{03AFbxiY+JfH+^G3N5=#TvF=Y8=6qec$J&mk9dUN9 zczDyM%Ud{ZZXbRTEZCf1ScrZw4#Y6}B-n6_uMsOwXDNTS)pbac{R|=*lX^ZbgKYFP zW1IN}28t$bbkvvC*7}`Rx9U7CnT76*8Jm7xwdHhTP8-L~-(5 zQNc2N5cS$!H9g=ySgZLxdOieA|72$N5e2pu$pE%k$@5^eYFeBlneivg~ugw zMhez%A$Mh*&OHYwn^?(n+Lu4QIXFGO^f_ZaCC9HVd3PAY%U0PhSNpBXP58RG+qS*MD7_KDT?(*lGok1ew;M z&%ISYXg+OGY0(X{{6N4H2^y70`i8AlQW3^zUrd%%(?4f z2j{3Ogi$3$M`jdPZk(jWW|6yi@((?#p}U}?$Hl2Ck*H2}{4mz81${oJ)rA134M>kr zD#4#j5jvLX7g-!0bqg||qEu&2@_pc?IgFh}Tv?t!J{=dlq{GU5$cieDK&gULyR^N8 zB>b!orDlhOA7_y?Cxy@2LySDDbJ3Gy!w$lg)N#pP0%9t)(n`%++C+LESX^G$B}LyB z%6k?UhWt`ub)Qslr{}dl%$ibaFJ7FS614tQ?xwyTw=r@ysVAEV$z|#*lHQTn>h?Un zSBO(<{bFSewxuEz{HR20lNvHqh-2?BQmVm3o|8NL?N#@BEf16<)%7uJZuf2U&@#+>Z}ck`+0CzdiHg~WBu4#XBT~n@OYM)d2QMI2 zzyVhrqI^}KXb(sI_WZ{NEe)wDv|38~5eF1BMYFTV7l&o=qmR|V>Ow>1c(zi_heTF3 zpQE-iQAGGn?yc8XlbH8U69YZUwdaoWEP@SsTAsP1<##~*;GT4Wz~e;<$o@G+@P%?{ ze!Ce8WcsHtqcPGKL3Tdyz|(}z?#xa}cI1VBE=ut2O7qF^9oZh1i5IKxt_P}vj_38Y z9?P$J_nzNtltMp`W@Ax8Y;q_^OCMPcpR(YK1Fe7mfd9ULsPxV4MWLPvm>J-mp? zkq0t=3#KhW!lLN9WPZB|jkN7tudveet|b+_2&7CUju<5XzyYnme3QnWH@NAm+z&td z#Slh^KQ#qDm{KA)2OQ^1sV}0mCs(Y`I!(+^nJ!1N44`;CXufXeEj?-u=wzo_FX&#M zwF`EGZ}}a)UlP4_F@1@c*zdAldWFvppDE@f8xy8frK9xQ)>Rdj#?rkC z;Yd8YX|e$yEEB!>Y$axkByX;jiCo|Lz>$mFKR9S*c5Vr-#m=8&6Mx882-ha|YYFK1 zbf^99;Y_&Tbum`-CH-j#>x@GICP;F+0b))%ZcBtV< zBB2jb;_>{1ffCrsCYTI`DDAZNN>x8jg1;c+VK5!F!*nf+#E%Y-w4{EEjsT)mB8q+? z0w@0+8zAhq4J2WBj0}9+5?ODgxjMCZyDa(sb+vHzx32T+Y~sb%?V@+1(2y(NbIa?X zcNPqWPV{fXFZq$@h||iQC1st~!cwh@Lq{|C0NK#xF43?(NAmCnL^31pUFqtlqecXB zCpEqjpgBNqyIdA(u=M4wsB5Zz(nEfV!hwyof->G`M04y%uQ^<;2>%z~moknK_SO;$|VLaS8rT(qkQnbykI+sauDv9?UDfmmvCe!lSaflwnL=1!lc z7Q3_Q+Ii5Lo%$W3_PIST(?pKZ=UUGw#4;HE7Gw*4#XdD0MGhunbXlx;)v?+L{4+A) zAX?B&!%&2VSM54^xvF=0TsEKXw(Rp-#ZtG0y9sGf z?W~@E(2B{P{ZdV^DQ9JTdY6%T01;9CXK6X7kfJsmj>Aa5kbEdZTwNIQ8VwvS#=V}-B= zFcZ~|i4s4AfGJ{dq|c*^B!69i@_Su-TTH_V zg!LJ3s0az+v8$&tb2EoEL72cbO8(w8Aw(9oGt9Qa1``uqIA?I zLDb3Uw?lq0sv4WF58$uss|Jxt%U1(fpWeAJ*_^V!CzN$ZCxHV1fx&mnFMl<0U5 zd))DHCzqozrn~2>&A`HWO1nGnfqKuI53VtffC|%LvO#uaE@WoWlcdF{hr~w`g@+AG z$(qb4?b_e$!Q^ zk9X-@wD=$fZFNE^IIZ&x60=E?lCqmlG((NeX7=M+J&rFzzHgA6y0qU0Zh#WLb%DN} zx}uUY66YL*Hb?Sk;tJsDwm+mvq8C};v$V~HB;Sx@>EgonoX{xlteN@^Xb?! z@Rymcj~~9yd|!s~ibsI|-F(GsYinYbMpI2^h>4jK&wOwLEJ$$kk!Goip+Yv;LGlt3 zc}(p#i7N2Fe=eIw7G=P2?HC}?tp9ux2~SAS{zj^XYhmFR&k!${B&#clmQ8UkXX!oY z=FI|NM8i*@RWT&lm@rU-D!BktWqo~_jjSn{>~ZW@6o56w9TkYWF5TpJ)#r7~XY5}) ze^oXjCW^k&1&BWPz`{owM{?SxV2rX5x-!jcU^ldEl*ixCyLoZZbT4AHB^q}|x~ZTS z20_gYvx*z|O&GURWVqf7miM2!7oFnbu^LKSN}yPp!=XweRru5LoumHj2_VMm;q$t&;kLWLo|gh#G}=lF@)k&p~G^xU;+^(wl> zn|skxd$!6Igg)K`O*i8p7U|jFIewQavh-$+b>sKzss{at_As)t_#&|9a`upx!kx?e zCrdh0+jO5fz1|EkNt>c+3XplKZL492j~KzxMVTo;O|{LP^0RR~x{X%BGQePN!B)(s z198zAW#mwY(ZZxWVjuT^&nT2M^d?3;Rpz%%vYU%&V^!dUl#{El8Up+0(X9>~} z8F*IM!ZD|i#p1~FfyDJm+^|B#m=qcPg&(xa@X5%CxR`E@@a)f zQUY{7br4|}Vi3gU%9OOgD0X39o5P|ZMy9Z>gqibuKG&}dT2Ja&PU67>dyU$B6Dcpp zVcv&-bv#qzr2Md^HOH&koV+Ix_TSZwSSwQ2v=GRrfsu!zTo(`N_!h`tLcae}MW>24%d zuqeQsxzLVRax!edk?mn~0lIeoa*f*o?LY8Rads(t7)zX+?qr6q#IiD{_Z8BX=&Jd` z%YDS$l#QOYsG+#mJ7X?UMJ^jGV2QEPgBrR8*Jd;rykt7!$y~c1_$(FY!u7uR`kUg+ z%1W4=O23~iEK=mKnhWFZcoi=m6l_+?>IT2kGhi$8y%85rQ{L!8xENIdkpZ_|dfe(m znL}s$W&xm7VA{)eYqizwtp)u<4#mcX8Jm!lAgk61@`zGQ80#q*4hVUEpu20iF?)DftKn2Hoao>of->Z9BG7VB-c=g&}NyJU#nXgr(+tr#zd=oE}IQxN2 z^dk!fdI0-hRbT*W&L!px# z|K7uO)SB?aM?Xw{^~ggc%3|X!Dn@0reZHBQ_2?%t!TZfSc}Cub!0gmq)gLb^2wv;o zWr8xqV;jR{6!Sa#nOwPtzYWUBS)nncIQX+{=pm8Ix{hGe7AnlF1l2u}(dI&ceJ(Gr zSSn^8)4u75)CERwi&`Dd8~bEAN8G7%g-iD6r6~@+I@AQ1;!My&E>aFxzL?{B z6#P_xLJ0B4EekuxHhVI+3ig1j*z;coV5Z)ZFmpzFiNJ|T^6APh* zUT>5@BWFleSa-8kPrb&u>aefO@wxC=(u&7UAi9bSMcOw8Tjp`vT}_4VFO^7Gl>N-V z?^B#ek$+hVLGN)UO{#_s^ZkQl8KCGr_ar#rGA3hk4W_#rAr}Fc} z#zXJSg?KWI2J?4)Dg!YO0}Uw?Rkr3Dw`td~!xT9iQZ?Fc%CSe1J^Oa=e4G4^ryl2a zvx&Ri-QA&!3+DrnZnPO|Nu>6tDS^fogUXlX7dtM#R9L!~7}=qzh&e;ix|*z(dRZPx zSu8XMLBn5ufT4OxEI8J#G-nw)i2#A}fsu}zUl%s-3x{xI8oh~iJ?%#I>3`U+)rV@B zM`9&Ui9X|trC@-ZtBw&Nw9=~nZ zjkM2VlD%>r(rT(oc(lb)t4xLlDaqdsq_m+!&03BHd8BVx*_Wntsq(DN+GIB1Uf=`@ zyJ*pquyKADJ5L4Sr)t~yC)HW%5LVVH)`q?hdj-!MRY%54@LUzoKP89_sxe6f9%yGB zSgi3(=pq@oxKPUnVhCU{DjG@nSAtDNU*83Ya7T;DG7F5E-S9Nwr8cmS0Fufwx8=UV zFw1Y*;m_>h?li`H5QKQDBJ9Wh}P6P_vYyNw`!4~_njOlruoUT;!;f-b*oJj!OU8?xPKFA0HFddWIz(jc_Xn~>Hg zsf~ZH86p8pQ>RIss;f-pB`xt7m5;T|kK-f^9yyoHOyD!VOzd~ryN1XTUY%!1eD+L! zZ_|oBN6KWXmRLWlA>Dk;my4Si+<0U5&DM=tToaRsksy6re2gxv5jDh;&HWmekU$tg zJk4CynnR#NOcOpE-Z>39kWYJuKwAB-czD-i)g@sF*KsM*3>J3O?nf2+hp++&`03Sy zUCS(J!M3x{d_J0a?PWh`gzZzErb#`%$x#p}0 z8hj^f#HSr~2Nq6-mbm$3GJRvm-6hE4lr3GcijhJP*HW~KPF2_^J5CnXTG@#M-f;b> z3hQCLoQu ztFK?&f!Q00`;1tEzsWFI#h{GYaWzPdVGc*lW+jovL;3KeV?%V@g|bt*(RRMRUd?>G z+m=d-#n~WaRkg1P88o=2TbcKis#6FApw@YW3F1~j<(#2XN~8D+Gp13p%nJ-Iw#XQK0077fhAsPA^#0&f%gc{OL-v&o5=s*@YS z9C-8JyI%;(LZqnEfO>I=nXG#UxcThZ#ayWn{l=y&EjGLogym|&cLSD!gV@Od3wR4< zDTCL=pG)1O-SyKYf7(J&P};3W>{nbb8U3HSXuKC2B#_JmB#eClkhO8SEtE;OWt6wqV zd#dVN7B?2Re`k$23mTn%v?C=uoiHhom4|Xcq8kPo1eN<@#6FMK;H8<<4o2qLVZYQQ zO{PjKR5W%GR7}q1T2;#ooJB5hYXe@~Y4V@)?z5Mhm#mw8nn-`tkGrcI= zV2M>sU-)q~!?^$Ej}L*2iPHT9RX}!Y(ff6)jl@;r)6?ZKIs91^0y91=-@&yVPwmpxFV!QM(pw58NdtkMqZ$v2@Y2ACLhk5qXmMy1Mee){T1 zO2FDOO`t&Rezy_t<0Yu4^C3*8gkukx-L8JoMxpG_*M)$A;w5Ip(7yGv2Yr7whD*<7 zK28cY$OLmZZXPMa@Auz_BO|+)Vv4hs=(RdlN>4lmOiE^N==;M{v;qW7S%hW2ke1K~ zt-SH$nNnwq0F@X#(blRL5z3RX@MFB;?BLDA`T-jLI)t?PG>hR9(KI|MKL@e1DD33Y zIR$8f^H%I-HT_|l63cOaP!kJ$^t;$D+L|q4Q^$AO>(vr!)a08c;mN*^{&4iq(}rww zW65S9lf2Ku&A~9VCLPeF6hvJ9Y)+RPwzaQ?mxV4nSzI(W3&ckqQ@WSqOSgpHIaH?c zRiu zTbhXoOScaWAVNMvPeV+#?Z5RP6;ecIwdc+?N}rsy^oa-o_}5DXoXJBhSEqEy^Pe<< z>LsM_sK=ScXEcCahf;j)4mX4Smv+xo2yzNtOAd||RXj-=9|z_d!zJpu38Kpb@L~qL z6`3&vEw9P7p-+>qTblgQd<+a_U`N|?lV&?t$&4-1tKairp>JK!tKtfdEd5f2hj$(7 z05a3fS(VKlI#|;Pt@(G_$219fyTg?DDr~b7wVNWA%0fJ6i0b8)Ly`Z z0iq6iwLZkwGx^k!H@r@qBD=uG{;+ISp*bIDLDhJRQ#q%VH8P^$`FhCun40*;fcUw~ zL&xT5*+Yv}u>nY0(@X-k^YF_KYzPdxopn~>PL}SyEIkKWAQYyM2%w2ofLOyDcn#oU z@h3*)j5RChMvN@=|2+PV0r_XK62++3lg3tqoTb(uh8CT4t8>eb8P(jby{$Kx+d$5nhs~q#4#)emHyIc)2}M>T0n035SQa9*0^POI}y=7 z{MNs?u4XH)LZ}d;blF+jr!c%KVe^Xuygx%cY{RHQ$6ksj`%@L$ItI>@z)3FU9QTIV zB9&`WdPCYX7ntK02BiIZ(!n*>ZCIi3I2k~s1oo^$MbvWnYz1OgUv9GqFblqPblF^= zT^#RlT)f#yR{v(l<%-cn#w|itI+Z{ktd267{84}f!l+KIISQTID{fdo_oK-YP}s## z%PSct_ZfZ%?LOQqQ|}S=@n(i!M$7Wy7(2_(%-XWy%wAkVdcK(O(V@bbuAIpBH}W@j zLU=hA6)B{50qLe`xhWEaMoS2Z@7eBCjp)Rykzd7$<0GLALhXdCQCalNzIWK-me#X< z&QJr;a#>c23OLqq->j!Pmw?^vx0}oQ^mAe(xxulapQSSxlohcSr=v9wfiR}irZFCZ+ z=t@dEG%T+qTI)beq=2H-2JsZR8Kos_aEphEe=YV+YpW{{G50Y5ilSuyrA7UvAfWd4 zX2{lF0VYsNXJ0Zc-fUtHrE+0EnvFR;{>_H|TJu)m5^9gTfW(yHyV##O)D+Qt*uUP0 xc~ky;s4;+6qz3O +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +## -e 's|```.*|```|' +INPUT_FILTER = "sed -e 's|__attribute__\s*[(]\s*[(].*[)]\s*[)]||g'" + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = \ + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = \ +xpacks/@micro-os-plus/build-helper/doxygen/common-header-micro-os-plus.html + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = \ +xpacks/@jothepro/doxygen-awesome-css/doxygen-awesome.css \ +xpacks/@micro-os-plus/build-helper/doxygen/common-custom.css \ +xpacks/@micro-os-plus/build-helper/doxygen/cards.css \ + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = \ +xpacks/@jothepro/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js \ +xpacks/@jothepro/doxygen-awesome-css/doxygen-awesome-fragment-copy-button.js \ +xpacks/@jothepro/doxygen-awesome-css/doxygen-awesome-paragraph-link.js \ +xpacks/@jothepro/doxygen-awesome-css/doxygen-awesome-interactive-toc.js \ +xpacks/@jothepro/doxygen-awesome-css/doxygen-awesome-tabs.js \ + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = \ +__DOXYGEN__ \ +__cplusplus \ + +#--------------------------------------------------------------------------- diff --git a/website/package-lock.json b/website/package-lock.json new file mode 100644 index 0000000..b9ea7a7 --- /dev/null +++ b/website/package-lock.json @@ -0,0 +1,2302 @@ +{ + "name": "website", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "website", + "version": "0.0.0", + "license": "MIT", + "devDependencies": { + "del-cli": "^5.0.0", + "open-cli": "^7.2.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/aggregate-error": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", + "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "dev": true, + "dependencies": { + "clean-stack": "^4.0.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", + "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", + "dev": true, + "dependencies": { + "camelcase": "^6.3.0", + "map-obj": "^4.1.0", + "quick-lru": "^5.1.1", + "type-fest": "^1.2.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/clean-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", + "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-7.1.0.tgz", + "integrity": "sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==", + "dev": true, + "dependencies": { + "globby": "^13.1.2", + "graceful-fs": "^4.2.10", + "is-glob": "^4.0.3", + "is-path-cwd": "^3.0.0", + "is-path-inside": "^4.0.0", + "p-map": "^5.5.0", + "rimraf": "^3.0.2", + "slash": "^4.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del-cli": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/del-cli/-/del-cli-5.1.0.tgz", + "integrity": "sha512-xwMeh2acluWeccsfzE7VLsG3yTr7nWikbfw+xhMnpRrF15pGSkw+3/vJZWlGoE4I86UiLRNHicmKt4tkIX9Jtg==", + "dev": true, + "dependencies": { + "del": "^7.1.0", + "meow": "^10.1.3" + }, + "bin": { + "del": "cli.js", + "del-cli": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-type": { + "version": "18.7.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz", + "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==", + "dev": true, + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-3.0.0.tgz", + "integrity": "sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/open-cli/-/open-cli-7.2.0.tgz", + "integrity": "sha512-1ANJc8oJ92FiaNZ0o2Hw4WBvDJoXs1P74aFMtpAvlbkIPV4uPcQvDz7V6kMOrsZkmB4tglrHVMlLQaafuUuxXg==", + "dev": true, + "dependencies": { + "file-type": "^18.2.1", + "get-stdin": "^9.0.0", + "meow": "^11.0.0", + "open": "^9.0.0", + "tempy": "^3.0.0" + }, + "bin": { + "open-cli": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/camelcase-keys": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-8.0.2.tgz", + "integrity": "sha512-qMKdlOfsjlezMqxkUGGMaWWs17i2HoL15tM+wtx8ld4nLrUwU58TFdvyGOz/piNP842KeO8yXvggVQSdQ828NA==", + "dev": true, + "dependencies": { + "camelcase": "^7.0.0", + "map-obj": "^4.3.0", + "quick-lru": "^6.1.1", + "type-fest": "^2.13.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/camelcase-keys/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/hosted-git-info": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/open-cli/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/open-cli/node_modules/meow": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-11.0.0.tgz", + "integrity": "sha512-Cl0yeeIrko6d94KpUo1M+0X1sB14ikoaqlIGuTH1fW4I+E3+YljL54/hb/BWmVfrV9tTV9zU04+xjw08Fh2WkA==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.2", + "camelcase-keys": "^8.0.2", + "decamelize": "^6.0.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^4.0.1", + "read-pkg-up": "^9.1.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^3.1.0", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/open-cli/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/open-cli/node_modules/quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/read-pkg": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", + "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^2.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/read-pkg-up": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", + "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", + "dev": true, + "dependencies": { + "find-up": "^6.3.0", + "read-pkg": "^7.1.0", + "type-fest": "^2.5.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/open-cli/node_modules/read-pkg/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/open-cli/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/open-cli/node_modules/read-pkg/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/open-cli/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", + "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", + "dev": true, + "dependencies": { + "aggregate-error": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", + "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", + "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0", + "read-pkg": "^6.0.0", + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dev": true, + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/redent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", + "dev": true, + "dependencies": { + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/trim-newlines": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/website/package.json b/website/package.json new file mode 100644 index 0000000..5097bf8 --- /dev/null +++ b/website/package.json @@ -0,0 +1,45 @@ +{ + "name": "website", + "version": "0.0.0", + "description": "website", + "main": "", + "scripts": {}, + "author": { + "name": "Liviu Ionescu", + "email": "ilg@livius.net", + "url": "https://github.com/ilg-ul" + }, + "license": "MIT", + "config": {}, + "bundleDependencies": [], + "$devDependenciesUrls": [ + "https://www.npmjs.com/package/del-cli", + "https://www.npmjs.com/package/open-cli" + ], + "devDependencies": { + "del-cli": "^5.0.0", + "open-cli": "^7.2.0" + }, + "xpack": { + "minimumXpmRequired": "0.17.0", + "actions": { + "deep-clean": "del-cli node_modules html package-lock.json", + "install": [ + "npm install", + "xpm install" + ], + "doxygen": [ + "del-cli html", + "doxygen config.doxyfile" + ], + "browser": "open-cli html/index.html" + }, + "dependencies": {}, + "devDependencies": { + "@jothepro/doxygen-awesome-css": "github:jothepro/doxygen-awesome-css#v2.2.1", + "@micro-os-plus/build-helper": "github:micro-os-plus/build-helper-xpack#xpack-develop" + }, + "properties": {}, + "buildConfigurations": {} + } +} diff --git a/website/pages/change-log.md b/website/pages/change-log.md new file mode 100644 index 0000000..4120611 --- /dev/null +++ b/website/pages/change-log.md @@ -0,0 +1,32 @@ +# Change log + +@tableofcontents + +## Breaking changes + +According to the [semver](https://semver.org) rules: + +> Major version X (X.y.z | X > 0) MUST be incremented if any +> backwards incompatible changes are introduced to the public API. + +The breaking changes introduced by each major release, +in reverse chronological order, are: + +### v3.x (2022-04-03) + +Major rework, with full set of comparators, exceptions, +function templates for test cases and class templates for test suites. + +### v2.3.x (2022-92-19) + +Deprecate `run_test_case(func, name)` in favour of +`run_test_case(name, func)`, to prepare for variadic templates. + +### v2.x (2021-03-01) + +The C++ namespace was renamed from `os` to `micro_os_plus`. + +### v1.x (2021-02-04) + +The initial code (inspired in its simplicity by node-tap) +was extracted from the mono-repo µOS++ project. diff --git a/website/pages/credits.md b/website/pages/credits.md new file mode 100644 index 0000000..cb096fe --- /dev/null +++ b/website/pages/credits.md @@ -0,0 +1,21 @@ +# Credits + +Many thanks to: + +@htmlonly + + MacStadium + +@endhtmlonly + +- [Node tap](https://node-tap.org) - for the great example showing that + testing frameworks do not need to be very complicated and for the + inspiration in writing the initial versions +- [Boost UT](https://boost-ext.github.io/ut/) - for showing that in + C++ getting the file name and line number is possible without the + ugly C macros and for major parts of the code +- [Doxygen](https://www.doxygen.nl) - for the site generator +- [doxygen-awesome-css](https://jothepro.github.io/doxygen-awesome-css/) - +for the nice Doxygen theme +- [SVG REPO](https://www.svgrepo.com/collection/scarlab-duotone-line-vectors/) - for the SVG icons +- [MacStadium](https://www.macstadium.com) - for providing the Apple Silicon development infrastructure as part of their [Open Source Program](https://www.macstadium.com/opensource/members). diff --git a/website/pages/developer.md b/website/pages/developer.md new file mode 100644 index 0000000..ec391e0 --- /dev/null +++ b/website/pages/developer.md @@ -0,0 +1,896 @@ + +# How to Use + +@tableofcontents + +The xPack Build Framework already includes ready to use +support for several testing frameworks: + +- [Google Test](https://github.com/xpack-3rd-party/googletest-xpack) +- [Catch2](https://github.com/xpack-3rd-party/catch2-xpack) +- [Boost UT](https://github.com/xpack-3rd-party/boost-ut-xpack) + +However, they all are quite heavy in terms of memory resources; also the +learning curve for mastering them is quite steep. + +Thus, for embedded projects, a simpler solution, with a smaller +memory footprint, was considered a useful addition. + +## Overview + +The initial version of the **µTest++** framework was inspired mainly by +[Node tap](https://node-tap.org) and aimed for simplicity. +The later v3.x was a full rework inspired by +[Boost UT](https://boost-ext.github.io/ut/). + +The main characteristics of µTest++, basically inherited from Boost UT, are: + +- intended to test **both C and C++** projects +- modern C++ 20 code (this was also the reason + to raise the bar to C++ 20 for the entire µOS++ project) +- **macro free** (while preserving the nice feature of being able to report + the file name and line number for failed tests) +- **expectations**, **assumptions**, **exceptions** +- **test cases**, **test suites** +- automatic test suites registration + +As major differentiator from Boost UT: + +- **reduced memory footprint**, since there are no dependencies on + the standard C++ stream library +- a slightly **simplified API** + +## Concepts and features + +- for complex applications, test cases can be grouped in test suites +- test suites can be located in separate compilation units; they automatically + register themselves to the runner; +- a **test suite** is a named sequence of test cases; +- a **test case** is a sequence of **test conditions** + (or simply **tests**, or **checks**), + which are expectations/assumptions, + i.e. conditions expected to be true; +- tests are based on logical expressions, which usually + compute a result and compare it to an expected value +- for C++ projects: it is also possible to check if, while evaluating + an expression, **exceptions** are thrown or not; +- each test either succeeds or fails; +- for **expectations**, the runner keeps counts of them; +- **assumptions** are hard conditions expected to be true in order for the test + to be able to run; +- failed assumptions abort the test; +- the test progress is shown on STDOUT, with each tests on a + separate line, prefixed with either a check sign (✓) or a cross sign (✗); +- failed tests display the location in the file and, if possible, + the actual values used in the expression evaluation; +- the main result of the test is passed back to the system as the process + exit code. + +A test suite is considered successful +if there is at least one successful expectation +and there are no failed tests. + +If all tests suites are successful, the process returns 0 as exit value. + +## ISTQB Glossary + +The **International Software Testing Qualification Board** defines some terms +used in testing frameworks: + +- **test condition**: a testable aspect of a component or system identified + as a basis for testing (implemented in µTest++ as calls to `expect()` or + `assume()` functions); +- **test case**: a set of preconditions, inputs, actions (where applicable), + expected results and postconditions, developed based on test conditions + (implemented in µTest++ as calls to the `test_case()` function) +- **test suite**: a set of test scripts or test procedures to be executed in + a specific test run (implemented in µTest++ as instances of the + `test_suite` class). + +For more details see: [ISTBQ](http://glossary.istqb.org/en/search/test%20case). + +## Getting started + +The absolute minimal test has a single test case, with a single expectation; +for example: + +```cpp +#include + +int +main(int argc, char* argv[]) +{ + using namespace micro_os_plus::micro_test_plus; + + initialize(argc, argv, "Minimal"); + + test_case ("Check truth", [] { + expect (true); + }) + + return exit_code (); +} +``` + +When running this test, the output looks like: + +```console +• Minimal - test suite started + + ✓ Check truth - test case passed (1 check) + +✓ Minimal - test suite passed (1 check in 1 test case) +``` + +A slightly more useful example would check the result of a computed value; +for example: + +```cpp +#include + +static int +compute_answer() +{ + return 42; +} + +int +main(int argc, char* argv[]) +{ + using namespace micro_os_plus::micro_test_plus; + + initialize(argc, argv, "The Answer"); + + test_case ("Check answer", [] { + expect (compute_answer() == 42) << "answer is 42"; + }); + + return exit_code (); +} +``` + +```console +• The Answer - test suite started + + ✓ Check answer - test case passed (1 check) + +✓ The Answer - test suite passed (1 check passed, 0 checks failed, in 1 test case) +``` + +In case the function returns the wrong answer, the test will fail; +for example: + +```cpp +static int +compute_answer() +{ + return 42 + 1; +} +``` + +In this case the test will fail with: + +```console +• The Answer - test suite started + + • Check answer - test case started + ✗ answer is 42 FAILED (answer.cpp:17) + ✗ Check answer - test case FAILED (0 checks passed, 1 check failed) + +✗ The Answer - test suite FAILED (0 checks passed, 1 check failed, in 1 test case) +``` + +The output identifies the failed test as located at line 17, but does not +provide more details, for example it does not tell what was the actual +wrong answer. + +To get such a useful information, the test should be slightly more elaborate, +and must use some custom comparators or operators; for example: + +```cpp +// ... + +int +main(int argc, char* argv[]) +{ + using namespace micro_os_plus::micro_test_plus; + + initialize(argc, argv, "The Answer"); + + test_case ("Check answer with comparator", [] { + expect (eq (compute_answer (), 42)) << "answer is 42"; + }); + + test_case ("Check answer with operator", [] { + using namespace micro_os_plus::micro_test_plus::operators; + using namespace micro_os_plus::micro_test_plus::literals; + + expect (compute_answer () == 42_i) << "answer is 42"; + expect (_i {compute_answer ()} == 42) << "answer is 42"; + }); + + return exit_code (); +} +``` + +The result would look like: + +```console +• The Answer - test suite started + + • Check answer with comparator - test case started + ✗ answer is 42 FAILED (answer.cpp:17, 43 == 42) + ✗ Check answer with comparator - test case FAILED (0 checks passed, 1 check failed) + + • Check answer with operator - test case started + ✗ answer is 42 FAILED (answer.cpp:24, 43 == 42) + ✗ answer is 42 FAILED (answer.cpp:25, 43 == 42) + ✗ Check answer with operator - test case FAILED (0 checks passed, 1 check failed) + +✗ The Answer - test suite FAILED (0 checks passed, 3 checks failed, in 2 test cases) +``` + +In the first case, `eq()` is a function that basically compares almost +everything and is able to keep track of the values of its operands. +There are similar functions for all comparisons. + +In the second case, a custom operator is used. To avoid interferences +with other operators, this custom operator is defined in a separate namespace +(which must +be explicitly referred to as shown) and matches only operands of +some specific types. + +To cast the integer constant `42` to such a specific type, a custom literal +is available (`_i`), which is also defined in a separate namespace. + +In addition to literals used to define constants, there are also definitions +which can be used to cast expressions. + +For the custom operators to match, it is necessary for at least one of +the operands +to be of the specific type, usually the constant using a literal, but if both +are expression, at least one of them must be casted. + +## C++ API + +Aiming simplicity, µTest++ provides only a very limited number of primitives +used to check expectations and assumptions. + +### Expectations + +Expectations are checks whose results are counted and do not +break the test (as opposed to assumptions, which abort the test). + +```cpp +template > +bool expect(const Expr_T& expr); +``` + +The template matches only expressions that evaluate to +a boolean or use custom comparators/operators derived from an +internal `detail::op` type. + +For generic checks performed outside the testing framework, the results can +be reported with `expect(true)` or `expect(false)` (see the example testing +multiple exceptions below). + +### Assumptions + +Assumptions are checks that abort the test if the results are false. + +```cpp +template > +bool assume(const Expr_T& expr); +``` + +Similarly, the template matches only expressions that evaluate to +a boolean and use custom comparators/operators derived from a +internal `detail::op` type. + +### Function comparators + +Expectations and assumptions can test any expression evaluating to a +boolean value, but in order to nicely report the difference between expected +and actual values in failed +conditions, the following generic comparators are available: + +```cpp +template +auto eq(const Lhs_T& lhs, const Rhs_T& rhs); + +template +auto ne(const Lhs_T& lhs, const Rhs_T& rhs); + +template +auto lt(const Lhs_T& lhs, const Rhs_T& rhs); + +template +auto le(const Lhs_T& lhs, const Rhs_T& rhs); + +template +auto gt(const Lhs_T& lhs, const Rhs_T& rhs); + +template +auto ge(const Lhs_T& lhs, const Rhs_T& rhs); +``` + +Similar templates are defined for pointer comparators. + +Examples: + +```cpp +expect (eq (compute_answer (), 42)) << "answer is 42"; +expect (ne (compute_answer (), 43)) << "answer is not 43"; +expect (lt (compute_answer (), 43)) << "answer is < 43"; +expect (le (compute_answer (), 43)) << "answer is <= 42"; +expect (gt (compute_answer (), 41)) << "answer is > 43"; +expect (ge (compute_answer (), 42)) << "answer is >= 42"; + +expect (compute_condition ()) << "condition is true"; +``` + +When such comparator functions are used, failed checks also display the +actual values compared during the test; for example: + +```console + Check failed comparisons + ✗ actual != 42 FAILED (unit-test.cpp:286, 42 != 42) + ✗ FAILED (unit-test.cpp:307, 42 != 42) + ✗ 42 != 42_i FAILED (unit-test.cpp:310, 42 != 42) + ✗ (actual == 42) and (actual != 42.0) FAILED (unit-test.cpp:781, (42 == 42 and 42.000000 != 42.000000)) +``` + +### Logical function operators + +Complex expressions can be checked in a single line, using the logical +`_and()`, `_or()` and `_not()` functions. The names are prefixed with +underscore since +`and`, `or` and `not` are reserved words in C/C++. + +```cpp +template +auto _and (const Lhs_T& lhs, const Rhs_T& rhs); + +template +auto _or (const Lhs_T& lhs, const Rhs_T& rhs); + +template +auto _not (const Expr_T& expr); +``` + +Examples: + +```cpp +expect(_and (eq (compute_answer (), 42), eq (compute_float (), 42.0))); +``` + +A slightly more readable syntax is available with custom operators, +as shown below. + +### Comparing strings + +In C/C++, plain strings are actually pointers to characters, and simply +comparing them does not compare the content but the memory addresses. + +For string comparisons to compare the content, use `string_view` objects: + +```cpp +#include +using namespace std::literals; // For the "sv" literal. +// ... + +expect (eq (std::string_view{ compute_ultimate_answer () }, "forty-two"sv)) + << "ultimate_answer is 'forty-two'"; +``` + +### Comparing containers + +Containers can be compared for equality. The comparison +is done by iterating and comparing each member. + +```cpp +expect (eq (std::vector{ 1, 2 }, std::vector{ 1, 2 })) + << "vector{ 1, 2 } eq vector{ 1, 2 }"; + +expect (ne (std::vector{ 1, 2, 3 }, std::vector{ 1, 2, 4 }) + << "vector{ 1, 2, 3 } ne vector{ 1, 2, 4 }"; +``` + +### Operators + +As in most other C++ test frameworks, it is +also possible to overload the `==`, `!=`, `<`, `>`, `<=`, `>=` operators. + +To avoid possible interferences with other operators +defined by the application, these operators match only for operands of +specific types and are located in a separate namespace +(`micro_test_plus::operators`); when applied to regular values, the +standard operands are used; the comparisons are performed properly, +but in case of failures the actual values are not shown. + +The following operators match only operands derived from the local +`detail::op` type, which can be enforced for constant values by using the +provided literals (like `1_i`) or, for dynamic values, by using the +provided casts (like `_i {expression}`, which are actually the +constructors of the internal classes): + +```cpp +template > +bool operator== (const Lhs_T& lhs, const Rhs_T& rhs); + +template > +bool operator!= (const Lhs_T& lhs, const Rhs_T& rhs); + +template > +bool operator< (const Lhs_T& lhs, const Rhs_T& rhs); + +template > +bool operator<= (const Lhs_T& lhs, const Rhs_T& rhs); + +template > +bool operator> (const Lhs_T& lhs, const Rhs_T& rhs); + +template > +bool operator>= (const Lhs_T& lhs, const Rhs_T& rhs); +``` + +Examples: + +```cpp +test_case ("Operators", [] { + using namespace micro_test_plus::operators; + using namespace micro_test_plus::literals; + + expect (compute_answer () == 42_i) << "answer is 42 (with literal)"; + expect (_i {compute_answer ()} == 42) << "answer is 42 (with cast)"; + expect (compute_answer () != 43_i) << "answer is not 43"; + expect (compute_answer () < 43_i) << "answer is < 43"; + expect (compute_answer () <= 43_i) << "answer is <= 42"; + expect (compute_answer () > 41_i) << "answer is > 43"; + expect (compute_answer () >= 42_i) << "answer is >= 42"; +}); +``` + +In addition, equality operators are also provided for `string_view` +objects and for iterable containers: + +```cpp +bool operator== (std::string_view lhs, std::string_view rhs); +bool operator!= (std::string_view lhs, std::string_view rhs); + +template >> +bool operator== (T&& lhs, T&& rhs); + +template >> +bool operator!= (T&& lhs, T&& rhs); +``` + +Examples: + +```cpp +#include +using namespace std::literals; // For the "sv" literal. +// ... + +test_case ("Operators", [] { + using namespace micro_test_plus::operators; + + expect (std::string_view{ compute_ultimate_answer () } == "forty-two"sv) + << "ultimate answer == 'forty-two'"; + + expect (std::vector{ 1, 2 } == std::vector{ 1, 2 }) + << "vector{ 1, 2 } == vector{ 1, 2 }"; + + expect (std::vector{ 1, 2, 3 } != std::vector{ 1, 2, 4 }) + << "vector{ 1, 2, 3 } != vector{ 1, 2, 4 }"; +}); +``` + +### Logical operators + +Similarly, logical operators are defined: + +```cpp +template > +bool operator and (const Lhs_T& lhs, const Rhs_T& rhs); + +template > +bool operator or (const Lhs_T& lhs, const Rhs_T& rhs); + +template > +bool operator not (const T& t); +``` + +They can be used in exactly the same way as standard operators, but the +additional functionality is enabled only when matching the typed operands. + +Examples: + +```cpp +expect (compute_answer () == 42_i && compute_float () == 42.0_f); +``` + +### Literals and wrappers + +For converting constants to recognised typed operands, the following +literal operators are available in the separate namespace `literals`: + +```cpp +namespace literals { + auto operator""_i (); // int + auto operator""_s (); // short + auto operator""_c (); // char + auto operator""_sc () // signed char + auto operator""_l (); // long + auto operator""_ll (); // long long + auto operator""_u (); // unsigned + auto operator""_uc (); // unsigned char + auto operator""_us (); // unsigned short + auto operator""_ul (); // unsigned long + auto operator""_ull (); // unsigned long long + auto operator""_i8 (); // int8_t + auto operator""_i16 (); // int16_t + auto operator""_i32 (); // int32_t + auto operator""_i64 (); // int64_t + auto operator""_u8 (); // uint8_t + auto operator""_u16 (); // uint16_t + auto operator""_u32 (); // uint32_t + auto operator""_u64 (); // uint64_t + auto operator""_f (); // float + auto operator""_d (); // double + auto operator""_ld (); // long double + auto operator""_b (); // bool +} +``` + +Similarly, for dynamic values, there are wrappers that convert them to +recognised types: + +```cpp + using _b = type_traits::value; + using _c = type_traits::value; + using _sc = type_traits::value; + using _s = type_traits::value; + using _i = type_traits::value; + using _l = type_traits::value; + using _ll = type_traits::value; + using _u = type_traits::value; + using _uc = type_traits::value; + using _us = type_traits::value; + using _ul = type_traits::value; + using _ull = type_traits::value; + using _i8 = type_traits::value; + using _i16 = type_traits::value; + using _i32 = type_traits::value; + using _i64 = type_traits::value; + using _u8 = type_traits::value; + using _u16 = type_traits::value; + using _u32 = type_traits::value; + using _u64 = type_traits::value; + using _f = type_traits::value; + using _d = type_traits::value; + using _ld = type_traits::value; + + // Template for wrapping any other type. + template + struct _t : type_traits::value + { + constexpr explicit _t (const T& t) : type_traits::value{ t } + { + } + }; +``` + +Examples: + +```cpp +expect (_i {answer} == 42_i); +expect (_f {expression} == 42_f); +``` + +### Function comparators vs. operators & literals + +Which to use, functions like `eq()` or the +overloaded operators? A very good question! + +Functions guarantee that the nice feature of showing the actual values +when expectations fail is always available. Also the syntax is more on the +traditional side, and for some it may look simpler and easier to read. + +Operators are generally easier to recognise than function calls, +but require the hack with the type wrappers and literals to enforce the +types, otherwise the actual values will not be displayed when the +expectations fail. + +Both syntaxes are functional, and, once the differences understood, +the issue is a matter of personal preferences. + +For example, the µOS++ projects favour explicit comparator functions. + +### Explicit namespace + +If for any reasons, the definitions in the `micro_test_plus` namespace +interfere with application definitions, it is recommended to +use the comparator functions, which can be more easily invoked +with explicit namespaces, possibly aliased to shorter names. + +Example: + +```cpp +{ + namespace mt = micro_os_plus::micro_test_plus; + + mt::test_case ("Check answer", [] { + mt::expect (mt::eq (compute_answer (), 42)) << "answer is 42"; + }); +} +``` + +### Exceptions + +A C++ testing framework must be able to check if an expression +(usually a function call), throws or not an exception. + +The following function templates allow to check various exceptions related +conditions: + +```cpp +// Check for any exception. +template +auto throws (const Callable_T& expr); + +// Check for a specific exception. +template +auto throws (const Callable_T& expr); + +// Check for no exception at all. +template +auto nothrow (const Callable_T& expr); +``` + +Examples: + +```cpp +expect (throws ([] { exercise_throw (true); })) << "exception thrown"; + +expect (throws ([] { throw std::runtime_error{ "" }; })) + << "std::runtime_error thrown"; + +expect (nothrow ([] { exercise_throw (false); })) << "exception not thrown"; +``` + +If a more elaborate logic is required, for example for expecting multiple +exceptions, use an explicit `try` with multiple `catch` statements and +report the results with `expect(true)` or `expect(false)`. + +```cpp +try + { + compute_answer (); + } +catch (const std::overflow_error& e) + { + expect (true) << "std::overflow_error thrown"; + } +catch (const std::runtime_error& e) + { + expect (true) << "std::runtime_error thrown"; + } +catch (...) + { + expect (false) << "known exception thrown"; + } +``` + +### Test cases + +Test cases group several checks done in the same environment. + +There can be any number of test cases, and each test case is performed +by invoking +a function, parametrised with a name, a callable (usually a lambda), +and optional arguments: + +```cpp +template +void test_case (const char* name, Callable_T&& func, Args_T&&... arguments); +``` + +Examples: + +```cpp +using namespace micro_os_plus::micro_test_plus; + +test_case ("Check various conditions", [] { + expect (eq (compute_answer (), 42)) << "answer eq 42"; + expect (ne (compute_answer (), 43)) << "answer ne 43"; +}); + +test_case ("Check various conditions with operators", [] { + using namespace micro_os_plus::micro_test_plus::operators; + using namespace micro_os_plus::micro_test_plus::literals; + + expect (compute_answer () == 42_i) << "answer == 42"; + expect (compute_answer () != 43_i) << "answer != 43"; +}); +``` + +### Test runner initialization + +The test runner is initialised with the process arguments and a +name, which is used for the default test suite: + +```cpp +void initialize (int argc, char* argv[], const char* name = "Main"); +``` + +The arguments can be used for controlling the verbosity level. + +### Return the test result + +The final test result that must be returned to the system +(0 for pass, 1 for fail), can be obtained with: + +```cpp +int exit_code (void); +``` + +This call also triggers the execution of all global test suites. + +For examples, see before. + +### Test suites + +Test suites are named sequences of test cases. + +The test cases defined in `main()` are considered to be part of +the default (or main) test suite, and are executed immediately +when invoked. + +For complex applications there can be multiple test +suites, usually in separate source files. + +In order to make self-registration possible, test suites are classes, +constructed with a name, a callable (usually a lambda +which chains the execution of the test cases) and optional +arguments: + +```cpp +class test_suite : public test_suite_base +{ +public: + template + test_suite (const char* name, Callable_T&& callable, + Args_T&&... arguments); + // ... +} +``` + +@note +It is recommended to instantiate the test suites as static objects. + +The self-registration is done in the constructor. +Test suites defined in different compilation units can be executed in any +order (since the order in which the +static constructors are invoked is not specified); +thus there should be no dependencies between test suites. + +The registered test suites are executed when the function +`exit_code()` is invoked. + +Examples: + +```cpp +// Test suite with generic parameters. +static void +test_suite_args (int ic, int iv, int& ir, int* ip1, int* ip2) +{ + using namespace micro_os_plus::micro_test_plus; + + test_case ("args", [&] { + expect (eq (ic, 42)) << "ic is 42"; + expect (eq (iv, 43)) << "iv is 43"; + expect (eq (ir, 44)) << "ir is 44"; + expect (eq (*ip1, 45)) << "*ip1 is 45"; + expect (eq (*ip2, 46)) << "*ip2 is 46"; + }); +} + +static int in = 43; +static int in44 = 44; +static int& ir = in44; +static int in45 = 45; +static int in46 = 46; +static int* ip2 = &in46; + +static micro_os_plus::micro_test_plus::test_suite ts_args + = { "Args", test_suite_args, 42, in, ir, &in45, ip2 }; +``` + +### Utility functions + +For tests comparing strings, in addition to exact matches, it is also possible +to check matches with patterns like `*` (for any characters) and `?` (for a +single character): + +```cpp +namespace utility { + bool is_match (std::string_view input, std::string_view pattern); +} +``` + +Examples: + +```cpp +expect (utility::is_match ("abc", "a?c")) << "abc matches a?c"; +expect (utility::is_match ("abc", "a*c")) << "abc matches a*c"; +``` + +Also for tests handling strings, the following function template allows to +split a string into a vector of substrings, using a delimiter: + +```cpp +namespace utility { + template + auto split (T input, Delim_T delim) -> std::vector; +} +``` + +Example: + +```cpp +expect (std::vector{ "a", "b" } + == utility::split ("a.b", ".")) + << "a.b splits into [a,b]"; +``` + +### Custom types + +It is possible to extend the comparators with templates matching custom +types, but this is a non-trivial task and requires a good knowledge of +C++. + +TODO: add a test to show how to do this. + +## C API + +There are no C equivalents for the C++ definitions. + +## Known problems + +None. + +## Command line options + +### Verbosity + +By default, the test reporter shows detailed results only for the failed +test cases; successful test cases are shown as a single line with +the total counts of passed/failed checks. + +To control the verbosity, use one of the following command line options: + +- `--verbose` - show all expectations, regardless of the result +- `--quiet` - show only the test suite totals +- `--silent` - suppress all output and only return the exit code + +## Memory footprint + +The memory footprint of unit tests based on µTest++ is definitely smaller than +that of traditional C++ testing framework, mainly because the `iostream` +library is not used. + +However, the use of templates for implementing the comparators and +operators should be carefully observed for platforms with really +limited amounts of memory, since each pair of different operands +contributes to the program size. + +At the limit, µTest++ can be used +without custom comparators and operators +(only with regular boolean expressions), and still be able to provide +the basic functionality of testing various conditions, but without +the optional features of displaying the actual values compared. + +Also, please note that the memory footprint on `debug` (built with `-O0`), +is significantly larger than on `release`. If necessary, the optimization +for the `debug` build can be increased to `-Og`, to save some memory. diff --git a/website/pages/home.md b/website/pages/home.md new file mode 100644 index 0000000..509911e --- /dev/null +++ b/website/pages/home.md @@ -0,0 +1,69 @@ + +# A source code library with the µOS++ C++ intrusive lists + +[![GitHub package.json version](https://img.shields.io/github/package-json/v/micro-os-plus/micro-test-plus-xpack)](https://github.com/micro-os-plus/micro-test-plus-xpack/blob/xpack/package.json) +[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/micro-os-plus/micro-test-plus-xpack)](https://github.com/micro-os-plus/micro-test-plus-xpack/tags/) +[![npm (scoped)](https://img.shields.io/npm/v/@micro-os-plus/micro-test-plus.svg?color=blue)](https://www.npmjs.com/package/@micro-os-plus/micro-test-plus/) +[![license](https://img.shields.io/github/license/micro-os-plus/micro-test-plus-xpack)](https://github.com/micro-os-plus/micro-test-plus-xpack/blob/xpack/LICENSE) +[![CI on Push](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/ci.yml/badge.svg)](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/ci.yml) +[![Website](https://img.shields.io/website?url=https%3A%2F%2Fmicro-os-plus.github.io%2Fmicro-test-plus-xpack%2F)](https://micro-os-plus.github.io/micro-test-plus-xpack/) + +The **µTest++** project (_micro test plus_) provides a small memory footprint +testing framework, intended for running unit tests on embedded +platforms. + +The **open-source** project is hosted on GitHub as +[micro-os-plus/micro-test-plus-xpack](https://github.com/micro-os-plus/micro-test-plus-xpack), +and can be installed as an +[xpm](https://xpack.github.io/xpm/)/[npm](https://docs.npmjs.com) dependency. + +@htmlonly + +
+
+
xpm install @micro-os-plus/micro-test-plus@latest --verbose
+
+
+ + + + + +@endhtmlonly diff --git a/website/pages/install.md b/website/pages/install.md new file mode 100644 index 0000000..9d7bb84 --- /dev/null +++ b/website/pages/install.md @@ -0,0 +1,236 @@ +# Getting Started + +[![GitHub package.json version](https://img.shields.io/github/package-json/v/micro-os-plus/micro-test-plus-xpack)](https://github.com/micro-os-plus/micro-test-plus-xpack/blob/xpack/package.json) +[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/micro-os-plus/micro-test-plus-xpack)](https://github.com/micro-os-plus/micro-test-plus-xpack/tags/) +[![npm (scoped)](https://img.shields.io/npm/v/@micro-os-plus/micro-test-plus.svg?color=blue)](https://www.npmjs.com/package/@micro-os-plus/micro-test-plus/) + +@tableofcontents + +As a source code library, this project can be integrated into another project +in the traditional way, +by either copying the relevant files into the target project, or by linking +the entire project as a Git submodule. + +However, things can be further automated and the most convenient way is +to **add it as a dependency** to the project via **xpm**. + +## Install with xpm/npm + +Along with the source files, this project also includes a +`package.json` file with the metadata that allows it to be identified as an +**xpm/npm** package so that it can be directly installed from GitHub or +from the [npmjs.com](https://www.npmjs.com) registry as +[`@micro-os-plus/micro-test-plus`](https://www.npmjs.com/package/@micro-os-plus/micro-test-plus). + +### Prerequisites + +A recent [xpm](https://xpack.github.io/xpm/), +which is a portable [Node.js](https://nodejs.org/) command line application +that complements [npm](https://docs.npmjs.com) +with several extra features specific to +**C/C++ projects**. + +It is recommended to install/update to the latest version with: + +```sh +npm install --global xpm@latest +``` + +For details please follow the instructions in the +[xPack install](https://xpack.github.io/install/) page. + +@warning +Be sure **xpm** is not installed with administrative rights. + +### xpm + +This project can be installed as a package from the +`npmjs.com` registry with: + +```sh +cd my-project +xpm init # Unless a package.json is already present + +xpm install @micro-os-plus/micro-test-plus@latest + +ls -l xpacks/@micro-os-plus/micro-test-plus +``` + +### npm + +The package can also be installed with [npm](https://docs.npmjs.com) +or related, but +the features specific to C/C++ projects will not be available; +therefore, at least for consistency reasons, it is recommended +to use **xpm**. + +## Add as a Git submodule + +Besides manually copying the relevant files to the target +project, which will later require extra maintenance efforts to keep the +project up to date, a more convenient +solution is to link the entire project as a **Git submodule**, +for example below an `xpacks` folder: + +```sh +cd my-project +git init # Unless already a Git project +mkdir -p xpacks + +git submodule add https://github.com/micro-os-plus/micro-test-plus-xpack.git \ + xpacks/@micro-os-plus/micro-test-plus +``` + +## Project repository + +The project uses Git and is hosted on GitHub as +. + +## Branches + +Apart from the unused `master` branch, there are two active branches: + +- `xpack`, with the latest stable version (default) +- `xpack-develop`, with the current development version + +All development is done in the `xpack-develop` branch, and contributions via +Pull Requests should be directed to this branch. + +When new releases are published, the `xpack-develop` branch is merged +into the `xpack` branch. + +## Build & integration info + +The project is written in C++, and the tests are expected to be +written in C++ too, but the tested code can also be written in plain C. + +The source code was compiled natively with **GCC** and **LLVM/clang** and cross +compiled on embedded **Arm** and **RISC-V** targets, +and is expected to be warnings free. + +To run **on embedded platforms**, the test framework requires a minimum +of support from the system, like writing to the +output stream. Any such environments are acceptable, but for standalone +tests the most common solution is to use **Arm semihosting**. + +To ease the integration of this library into user projects, there +are already made **CMake** and **meson** configuration files (see below). + +For other build systems, consider the following details: + +## Include folders + +The following folders should be passed to the compiler during the build: + +- `include` + +The header files to be included in user projects are: + +```cpp +#include +``` + +## Source files + +The source files to be added to user projects are: + +- `src/micro-test-plus.cpp` +- `src/test-reporter.cpp` +- `src/test-runner.cpp` +- `src/test-suite.cpp` + +## Preprocessor definitions + +There are several preprocessor definitions used to configure the build: + +- `MICRO_OS_PLUS_INCLUDE_CONFIG_H` - to include `` +- `MICRO_OS_PLUS_TRACE` - to include the trace calls +- `MICRO_TEST_PLUS_TRACE` - to enable some tracing messages + +## Compiler options + +The following options must be passed to the compiler and linker: + +- `-std=c++20` or higher for C++ sources + +## C++ Namespaces + +The code is grouped in the following namespaces: + +- `micro_os_plus::micro_test_plus` +- `micro_os_plus::micro_test_plus::operators` +- `micro_os_plus::micro_test_plus::literals` +- `micro_os_plus::micro_test_plus::utility` + +`micro_os_plus` is the top µOS++ namespace, and `micro_test_plus` is the +µTest++ namespace. + +The `operators` namespace defines the custom operators, and the `literals` +namespace defines the literals (like `1_i`); + +## C++ Classes + +The following classes are defined: + +- `micro_os_plus::micro_test_plus::test_suite` + +## Dependencies + +The library has the following dependencies: + +- none + +## CMake + +To integrate the **micro-test-plus** library into a CMake application, +add the folder where this project is located to the build: + +```cmake +add_subdirectory("xpacks/@micro-os-plus/micro-test-plus") +``` + +The result is **an interface library** that can be added as an application +dependency with: + +```cmake +target_link_libraries(your-target PRIVATE + + micro-os-plus::micro-test-plus +) +``` + +## Meson Build + +To integrate the **micro-test-plus** library into a +[meson](https://mesonbuild.com) application, +add the folder where this project is located to the build: + +```meson +subdir('xpacks/@micro-os-plus/micro-test-plus') +``` + +The result is **a dependency object** that can be added +to an application with: + +```meson +exe = executable( + your-target, + link_with: [ + # Nothing, not static. + ], + dependencies: [ + micro_os_plus_micro_test_lists_dependency, + ] +) +``` + +## Status + +@note +The **micro-test-plus** library is fully functional, and +it is used to test several projects in the µOS++ framework. +@n +@n +The project is CI tested on 32 and 64-bit bare-metal platforms (Arm Cortex-M0, +Cortex-M3, Cortex-M4F, Cortex-M7F, Cortex-A15, Cortex-A72, RISC-V RV32IMAC, +RV64IMAFDC), and natively, with GCC and LLVM/clang. diff --git a/website/pages/license.md b/website/pages/license.md new file mode 100644 index 0000000..be580fc --- /dev/null +++ b/website/pages/license.md @@ -0,0 +1,9 @@ +# License + +Unless otherwise stated, the content is released under the terms of the +[MIT License](https://opensource.org/licenses/mit/), +with all rights reserved to +[Liviu Ionescu](https://github.com/ilg-ul). + +The code from Boost UT is released under the terms of the +[Boost Version 1.0 Software License](https://www.boost.org/LICENSE_1_0.txt). diff --git a/README-MAINTAINER.md b/website/pages/maintainer.md similarity index 72% rename from README-MAINTAINER.md rename to website/pages/maintainer.md index a953833..7ec7987 100644 --- a/README-MAINTAINER.md +++ b/website/pages/maintainer.md @@ -1,15 +1,17 @@ -[![license](https://img.shields.io/badge/license-MIT-blue)](https://github.com/micro-os-plus/micro-test-plus-xpack/blob/xpack/LICENSE) +# Maintainer Info + +@tableofcontents + +[![license](https://img.shields.io/github/license/micro-os-plus/micro-test-plus-xpack)](https://github.com/micro-os-plus/micro-test-plus-xpack/blob/xpack/LICENSE) [![CI on Push](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/ci.yml/badge.svg)](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/ci.yml) -[![GitHub issues](https://img.shields.io/github/issues/micro-os-plus/micro-test-plus-xpack.svg)](https://github.com/micro-os-plus/micro-test-plus-xpack/issues/) +[![GitHub issues](https://img.shields.io/github/issues/micro-os-plus/micro-test-plus-xpack.svg)](https://github.com/micro-os-plus/micro-test-plus-xpack/issues) [![GitHub pulls](https://img.shields.io/github/issues-pr/micro-os-plus/micro-test-plus-xpack.svg)](https://github.com/micro-os-plus/micro-test-plus-xpack/pulls) - -# Maintainer info +[![Website](https://img.shields.io/website?url=https%3A%2F%2Fmicro-os-plus.github.io%2Fmicro-test-plus-xpack%2F)](https://micro-os-plus.github.io/micro-test-plus-xpack/) ## Project repository -The project is hosted on GitHub as: - -- +The project uses Git and is hosted on GitHub as +. To clone the stable branch (`xpack`), run the following commands in a terminal (on Windows use the _Git Bash_ console): @@ -33,10 +35,10 @@ git clone \ ~/Work/micro-os-plus/micro-test-plus-xpack.git ``` -Or, if the repo was already cloned: +To link it to the central xPack store: ```sh -git -C ~/Work/micro-os-plus/micro-test-plus-xpack.git pull +xpm link -C ~/Work/micro-os-plus/micro-test-plus-xpack.git ``` ## Get helper sources @@ -74,7 +76,26 @@ Code formatting is done using `clang-format --style=file`, either manually from a script, or automatically from Visual Studio Code, or the Eclipse CppStyle plug-in. -Always reformat the source files that were changed. +@note +Always reformat the source files that were changed before committing +them to the repository. + +## How to update the website + +The project [website](https://micro-os-plus.github.io/micro-test-plus-xpack/) +is automatically generated and published by the +`publish-github-pages.yml` workflow on Git push. The GitHub Action that +runs the workflow is +. + +@todo When ready, restrict the workflow to run only on pushes to the +`xpack` branch. + +For development, there are two xPack actions that can be used +to automate the workflow: + +- `doxygen` - to build the site with Doxygen locally +- `browser` - to start the default browser with the `website/html` folder ## How to make new releases @@ -100,16 +121,20 @@ Determine the upstream version (like `3.1.1`) and eventually update the Check GitHub issues and pull requests: -- +- -and fix them; assign them to a milestone (like `3.1.1`). +and fix them; assign them to a milestone (like `3.1.1`, without **v**). -### Update `README-MAINTAINER.md` +### Update 'website/pages/maintainer.md' -Update the `README-MAINTAINER.md` file to reflect the changes +Update the `maintainer.md` file to reflect the changes related to the new version. -### Update `CHANGELOG.md` +### Update 'website/config.doxyfile' + +Update the `PROJECT_NUMBER` variable to the new version. + +### Update 'CHANGELOG.md' - open the `CHANGELOG.md` file - check if all previous fixed issues are in @@ -123,15 +148,15 @@ related to the new version. ### Manual tests -To run the tests manually on the local machine: +To run the tests manually on the local development machine: ```sh -xpm run deep-clean -C ~/Work/micro-os-plus/micro-test-plus-xpack.git/tests -xpm run install-all -C ~/Work/micro-os-plus/micro-test-plus-xpack.git/tests -xpm run test-all -C ~/Work/micro-os-plus/micro-test-plus-xpack.git/tests +xpm run deep-clean -C ~/Work/micro-os-plus/micro-test-plus-xpack.git +xpm run install-all -C ~/Work/micro-os-plus/micro-test-plus-xpack.git +xpm run test-all -C ~/Work/micro-os-plus/micro-test-plus-xpack.git ``` -### Publish on the npmjs.com server +### Publish on the 'npmjs.com' server - select the `xpack-develop` branch - commit all changes @@ -153,7 +178,8 @@ and macOS Apple Silicon. For this: -- start the `~/actions-runners/micro-os-plus/run.sh &` runner on `xbbma` and `xbbla` +- start the `~/actions-runners/micro-os-plus/run.sh &` runner on + `xbbma`, `xbbla64` and `xbbla32` - ensure that the `xpack-develop` branch is pushed - run the `trigger-workflow-test-all` action - wait for the **test-all** job to complete @@ -168,7 +194,7 @@ The version is visible at: - -## Update the repo +### Update the repo When the package is considered stable: @@ -177,7 +203,7 @@ When the package is considered stable: - push to GitHub - select `xpack-develop` -## Tag the npm package as `latest` +### Tag the npm package as 'latest' When the release is considered stable, promote it as `latest`: @@ -192,3 +218,9 @@ When the release is considered stable, promote it as `latest`: - paste the release name like **µOS++ µTest++ v3.1.1 released** - paste the link to the npmjs release - click the **Tweet** button + +## TODO + +@todo Possibly split the content into separate classes. + +@todo Make a new clang15 release to generate runnable binaries to silence meson. diff --git a/website/pages/testing.md b/website/pages/testing.md new file mode 100644 index 0000000..c25a556 --- /dev/null +++ b/website/pages/testing.md @@ -0,0 +1,616 @@ +# Testing + +@tableofcontents + +[![CI on Push](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/ci.yml/badge.svg)](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/ci.yml) +[![Test on all platforms](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/test-all.yml/badge.svg)](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/test-all.yml) + +## Overview + +The µOS++ testing strategy is to compile the sources with as many +toolchains as possible, and run them on as many platforms as possible. + +## Platforms + +The supported test platforms are: + +- `platform-native` - run the test applications as **native processes** + on the development machine +- `platform-qemu-cortex-m0` - run the tests as fully semihosted + **Cortex-M0** applications on a QEMU **mps2-an385** emulated board + (an Arm Cortex-M3 development board) +- `platform-qemu-cortex-m3` - run the tests as fully semihosted + **Cortex-M3** applications on a QEMU **mps2-an385** emulated board + (an Arm Cortex-M3 development board) +- `platform-qemu-cortex-m4f` - run the tests as fully semihosted + **Cortex-M4** applications on a QEMU **mps2-an386** emulated board + (an Arm Cortex-M4 development board) +- `platform-qemu-cortex-m7f` - run the tests as fully semihosted + **Cortex-M7** applications on a QEMU **mps2-an500** emulated board + (an Arm Cortex-M7 development board) +- `platform-qemu-cortex-a15` - run the tests as fully semihosted + **Cortex-A15** applications on a QEMU **virt** emulated board +- `platform-qemu-cortex-a72` - run the tests as fully semihosted + **Cortex-A72** (64-bit) applications on a QEMU **virt** emulated board +- `platform-qemu-riscv-rv32imac` - run the tests as fully semihosted + **RISC-V RV32IMAC** applications on a QEMU **virt** emulated board +- `platform-qemu-riscv-rv64imafdc` - run the tests as fully semihosted + **RISC-V RV64IMAFDC** applications on a QEMU **virt** emulated board + +The tests are built and executed on: + +- GNU/Linux (Intel and Arm) +- macOS (Intel and Apple Silicon) +- Windows + +The build configurations use exactly the same source files on all platforms, +without changes. + +## Toolchains + +To improve source code portability, the builds are repeated with multiple +toolchains, even with multiple versions of the same toolchain. + +The following toolchains are used: + +- gcc 11, 12, 13 (native) +- clang 13, 14, 15, 16 (native) +- arm-none-eabi-gcc 11, 12, 13 (Cortex-M, AArch32) +- aarch64-none-elf-gcc 11, 12, 13 (AArch64) +- risc-none-elf-gcc 11, 12, 13 (RISC-V 32/64) + +## Tests details + +### sample-test + +The [sample-test.cpp](tests/src/sample-test.cpp) +file is a simple application to demonstrate how to call some of the +primitives available in the library to write a simple test. + +A typical run looks like: + +```console +test 1 + Start 1: sample-test + +1: Test command: /Users/ilg/MyProjects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/tests/build/native-cmake-sys-release/platform-bin/sample-test "one" "two" +1: Working Directory: /Users/ilg/MyProjects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/tests/build/native-cmake-sys-release/platform-bin +1: Test timeout computed to be: 10000000 +1: Built with clang Apple LLVM 15.0.0 (clang-1500.0.40.1), with exceptions. +1: +1: • Sample - test suite started +1: +1: ✓ Check various conditions - test case passed (7 checks) +1: ✓ Check various conditions with operators - test case passed (7 checks) +1: ✓ Check strings - test case passed (1 check) +1: ✓ Check strings with operators - test case passed (1 check) +1: ✓ Check compound conditions - test case passed (1 check) +1: ✓ Check compound conditions with operators - test case passed (1 check) +1: ✓ Check multiple function invocations - test case passed (2 checks) +1: ✓ Check args - test case passed (3 checks) +1: ✓ Check complex logic - test case passed (1 check) +1: ✓ Check if exceptions are thrown - test case passed (2 checks) +1: ✓ Check if exceptions are not thrown - test case passed (1 check) +1: ✓ Check containers - test case passed (2 checks) +1: ✓ Check containers with operators - test case passed (2 checks) +1: +1: ✓ Sample - test suite passed (31 checks in 13 test cases) +1: +1: • Separate - test suite started +1: +1: ✓ Check one - test case passed (1 check) +1: ✓ Check two - test case passed (1 check) +1: +1: ✓ Separate - test suite passed (2 checks in 2 test cases) +1: +1: • Explicit namespace - test suite started +1: +1: ✓ Check one - test case passed (1 check) +1: ✓ Check two - test case passed (1 check) +1: +1: ✓ Explicit namespace - test suite passed (2 checks in 2 test cases) +1: +1: • Args - test suite started +1: +1: ✓ args - test case passed (5 checks) +1: +1: ✓ Args - test suite passed (5 checks in 1 test case) +``` + +In addition to the default run, there are two more, one more verbose, and one +silent, which only returns the test result. + +### unit-test + +The [unit-test.cpp](tests/src/unit-test.cpp) +file is an exhaustive test trying to validate that all the library +functions work as expected. + +A typical run looks like: + +```console +test 5 + Start 5: unit-test + +5: Test command: /Users/ilg/MyProjects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/tests/build/native-cmake-sys-release/platform-bin/unit-test +5: Working Directory: /Users/ilg/MyProjects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/tests/build/native-cmake-sys-release/platform-bin +5: Test timeout computed to be: 10000000 +5: Built with clang Apple LLVM 15.0.0 (clang-1500.0.40.1), with exceptions. +5: +5: µTest++ unit tests; some checks are expected to fail. +5: +5: • Main - test suite started +5: +5: ✓ Initial counters - test case passed (0 checks) +5: ✓ expect(true) - test case passed (3 checks) +5: +5: • expect(false) - test case started +5: ✗ FAILED (micro-test-plus.h:102) +5: ✗ false FAILED (micro-test-plus.h:102) +5: ✗ expect(false) - test case FAILED (0 checks passed, 2 failed) +5: +5: ✓ Integer comparisons - test case passed (8 checks) +5: +5: • Failed integer comparisons - test case started +5: ✗ actual != 42 FAILED (micro-test-plus.h:102, 42 != 42) +5: ✗ actual+1 == 42 FAILED (micro-test-plus.h:102, 43 == 42) +5: ✗ actual-1 >= 42 FAILED (micro-test-plus.h:102, 41 >= 42) +5: ✗ actual > 42 FAILED (micro-test-plus.h:102, 42 > 42) +5: ✗ actual+1 <= 42 FAILED (micro-test-plus.h:102, 43 <= 42) +5: ✗ actual < 42 FAILED (micro-test-plus.h:102, 42 < 42) +5: ✗ FAILED (micro-test-plus.h:102, 42 != 42) +5: ✗ 42 != 42_i FAILED (micro-test-plus.h:102, 42 != 42) +5: ✗ Failed integer comparisons - test case FAILED (0 checks passed, 8 failed) +5: +5: ✓ Float comparisons - test case passed (27 checks) +5: +5: • Failed float comparisons - test case started +5: ✗ actual != 42.0 FAILED (micro-test-plus.h:102, 42.000000f != 42.000000f) +5: ✗ actual == 43.0 FAILED (micro-test-plus.h:102, 42.000000f == 43.000000f) +5: ✗ actual != 42 FAILED (micro-test-plus.h:102, 42.000000f != 42) +5: ✗ actual == 43 FAILED (micro-test-plus.h:102, 42.000000f == 43) +5: ✗ 42.101f != 42.101_f FAILED (micro-test-plus.h:102, 42.101002f != 42.101002f) +5: ✗ 42.101f epsilon 0.01f != 42.10_f FAILED (micro-test-plus.h:102, 42.101002f != 42.099998f) +5: ✗ 42.101f epsilon 0.1f == 42.10_f FAILED (micro-test-plus.h:102, 42.101002f == 42.099998f) +5: ✗ 42.1010001f epsilon 0.1f != 42.1_f FAILED (micro-test-plus.h:102, 42.101002f != 42.099998f) +5: ✗ 42.101f == 42.10_f FAILED (micro-test-plus.h:102, 42.101002f == 42.099998f) +5: ✗ 42.101f == 42.100_f FAILED (micro-test-plus.h:102, 42.101002f == 42.099998f) +5: ✗ 42.10f == 42.1_f FAILED (micro-test-plus.h:102, 42.099998f != 42.099998f) +5: ✗ 42.42f == 42.42_f FAILED (micro-test-plus.h:102, 42.419998f != 42.419998f) +5: ✗ 42.42 == 42.420_d FAILED (micro-test-plus.h:102, 42.420000 != 42.420000) +5: ✗ 42.0 == 42.0_d FAILED (micro-test-plus.h:102, 42.000000 != 42.000000) +5: ✗ 42. == 42._d FAILED (micro-test-plus.h:102, 42.000000 != 42.000000) +5: ✗ 42.42 == 42.42_ld FAILED (micro-test-plus.h:102, 42.420000l != 42.420000l) +5: ✗ 1234._f == 1234.f FAILED (micro-test-plus.h:102, 1234.000000f != 1234.000000f) +5: ✗ 1234.56_f == 1234.56f FAILED (micro-test-plus.h:102, 1234.560059f != 1234.560059f) +5: ✗ 12345678.9f == 12345678.9_f FAILED (micro-test-plus.h:102, 12345679.000000f != 12345679.000000f) +5: ✗ 111111.42f == 111111.42_f FAILED (micro-test-plus.h:102, 111111.421875f != 111111.421875f) +5: ✗ 1111111111.42 == 1111111111.42_d FAILED (micro-test-plus.h:102, 1111111111.420000 != 1111111111.420000) +5: ✗ Failed float comparisons - test case FAILED (0 checks passed, 21 failed) +5: +5: ✓ String comparisons - test case passed (6 checks) +5: +5: • Failed string comparisons - test case started +5: ✗ actual_sv != abc_sv FAILED (micro-test-plus.h:102, abc != abc) +5: ✗ actual_sv == abx_sv FAILED (micro-test-plus.h:102, abc == abx) +5: ✗ actual_sv >= abd_sv FAILED (micro-test-plus.h:102, abc >= abd) +5: ✗ actual_sv > abc_sv FAILED (micro-test-plus.h:102, abc > abc) +5: ✗ actual_sv <= abb_sv FAILED (micro-test-plus.h:102, abc <= abb) +5: ✗ actual_sv < abc_sv FAILED (micro-test-plus.h:102, abc < abc) +5: ✗ Failed string comparisons - test case FAILED (0 checks passed, 6 failed) +5: +5: ✓ Pointer comparisons - test case passed (9 checks) +5: +5: • Failed pointer comparisons - test case started +5: ✗ ptr1 != &one FAILED (micro-test-plus.h:102, 0x7ff7b286ce2c != 0x7ff7b286ce2c) +5: ✗ ptr1 != ptr2 FAILED (micro-test-plus.h:102, 0x7ff7b286ce2c != 0x7ff7b286ce2c) +5: ✗ ptr1 > ptr2 FAILED (micro-test-plus.h:102, 0x7ff7b286ce2c > 0x7ff7b286ce2c) +5: ✗ ptr1 < ptr2 FAILED (micro-test-plus.h:102, 0x7ff7b286ce2c < 0x7ff7b286ce2c) +5: ✗ ptr1 == a_non_nullptr FAILED (micro-test-plus.h:102, 0x7ff7b286ce2c == 0x7ff7b286ce90) +5: ✗ pfunc != afunc FAILED (micro-test-plus.h:102, 0x10d6d2e30 != 0x10d6d2e30) +5: ✗ pfunc == a_non_nullptr FAILED (micro-test-plus.h:102, 0x10d6d2e30 == 0x7ff7b286ce90) +5: ✗ pone >= ptwo FAILED (micro-test-plus.h:102, 0x7ff7b286ce38 >= 0x7ff7b286ce3c) +5: ✗ ptwo <= pone FAILED (micro-test-plus.h:102, 0x7ff7b286ce3c <= 0x7ff7b286ce38) +5: ✗ Failed pointer comparisons - test case FAILED (0 checks passed, 9 failed) +5: +5: ✓ Null pointer comparisons - test case passed (3 checks) +5: +5: • Failed null pointer comparisons - test case started +5: ✗ a_nullptr != nullptr FAILED (micro-test-plus.h:102, 0x0 != nullptr) +5: ✗ a_non_nullptr == nullptr FAILED (micro-test-plus.h:102, 0x7ff7b286ce90 == nullptr) +5: ✗ pfunc != nullptr FAILED (micro-test-plus.h:102, 0x0 != nullptr) +5: ✗ Failed null pointer comparisons - test case FAILED (0 checks passed, 3 failed) +5: +5: ✓ reflection::type_name() - test case passed (2 checks) +5: ✓ thrown exceptions - test case passed (2 checks) +5: +5: • Failed thrown exceptions - test case started +5: ✗ exception thrown FAILED (micro-test-plus.h:102, throws) +5: ✗ std::runtime_error thrown FAILED (micro-test-plus.h:102, throws) +5: ✗ Failed thrown exceptions - test case FAILED (0 checks passed, 2 failed) +5: +5: ✓ Not thrown exceptions - test case passed (1 check) +5: +5: • Failed not thrown exceptions - test case started +5: ✗ exception not thrown FAILED (micro-test-plus.h:102, nothrow) +5: ✗ Failed not thrown exceptions - test case FAILED (0 checks passed, 1 failed) +5: +5: • Logical operations - test case started +5: ✓ not (actual != 42) +5: ✗ not (actual == 42) FAILED (micro-test-plus.h:102, not 42 == 42) +5: ✓ (actual == 42) and (actual == 42.0) +5: ✗ (actual == 42) and (actual != 42.0) FAILED (micro-test-plus.h:102, (42 == 42 and 42.000000f != 42.000000)) +5: ✗ (actual != 42) and (actual == 42.0) FAILED (micro-test-plus.h:102, (42 != 42 and 42.000000f == 42.000000)) +5: ✗ (actual != 42) and (actual != 42.0) FAILED (micro-test-plus.h:102, (42 != 42 and 42.000000f != 42.000000)) +5: ✓ (42 == 42 and 42.000000f == 42.000000) +5: ✗ FAILED (micro-test-plus.h:102, (42 == 42 and 42.000000f != 42.000000)) +5: ✓ (actual == 42) or (actual == 42.0) +5: ✓ (actual == 42) or (actual != 42.0) +5: ✓ (actual != 42) or (actual == 42.0) +5: ✗ (actual != 42) or (actual != 42.0) FAILED (micro-test-plus.h:102, (42 != 42 or 42.000000f != 42.000000)) +5: ✗ Logical operations - test case FAILED (6 checks passed, 6 failed) +5: +5: • Operators - test case started +5: ✓ actual == 42 +5: ✗ actual+1 == 42 with scalar FAILED (micro-test-plus.h:102) +5: ✗ actual+1 == 42 with _i literal FAILED (micro-test-plus.h:102, 43 == 42) +5: ✗ actual+1 == 42 with _i() FAILED (micro-test-plus.h:102, 43 == 42) +5: ✓ actual+1 != 42 +5: ✓ actual-1 < 42 +5: ✓ actual <= 42 +5: ✓ actual+1 > 42 +5: ✓ actual >= 42 +5: ✓ not (actual != 42) +5: ✗ not (actual == 42) FAILED (micro-test-plus.h:102, not 42 == 42) +5: ✓ (actual == 42) and (actual == 42.0) +5: ✗ (actual == 42) and (actual != 42.0) FAILED (micro-test-plus.h:102, (42 == 42 and 42.000000f != 42.000000)) +5: ✗ (actual != 42) and (actual == 42.0) FAILED (micro-test-plus.h:102, (42 != 42 and 42.000000f == 42.000000)) +5: ✗ (actual != 42) and (actual != 42.0) FAILED (micro-test-plus.h:102, (42 != 42 and 42.000000f != 42.000000)) +5: ✓ (42 == 42 and 42.000000f == 42.000000) +5: ✗ FAILED (micro-test-plus.h:102, (42 == 42 and 42.000000f != 42.000000)) +5: ✓ (actual == 42) or (actual == 42.0) +5: ✓ (actual == 42) or (actual != 42.0) +5: ✓ (actual != 42) or (actual == 42.0) +5: ✗ (actual != 42) or (actual != 42.0) FAILED (micro-test-plus.h:102, (42 != 42 or 42.000000f != 42.000000f)) +5: ✗ Operators - test case FAILED (12 checks passed, 9 failed) +5: +5: ✓ Strings matches - test case passed (6 checks) +5: +5: • Failed strings matches - test case started +5: ✗ empty matches abc FAILED (micro-test-plus.h:102) +5: ✗ abc matches b?? FAILED (micro-test-plus.h:102) +5: ✗ abc matches a*d FAILED (micro-test-plus.h:102) +5: ✗ abc matches *C FAILED (micro-test-plus.h:102) +5: ✗ Failed strings matches - test case FAILED (0 checks passed, 4 failed) +5: +5: ✓ Splits - test case passed (4 checks) +5: +5: ✗ Main - test suite FAILED (92 checks passed, 71 failed, in 23 test cases) +5: +5: • Combinatorial integrals - test suite started +5: +5: ✓ Combinatorial integrals with signed long long - test case passed (20 checks) +5: ✓ Combinatorial integrals unsigned long long - test case passed (20 checks) +5: ✓ Combinatorial integrals with signed int - test case passed (20 checks) +5: ✓ Combinatorial integrals with unsigned int - test case passed (20 checks) +5: ✓ Combinatorial integrals with signed short - test case passed (20 checks) +5: ✓ Combinatorial integrals with unsigned short - test case passed (20 checks) +5: ✓ Combinatorial integrals with signed short - test case passed (20 checks) +5: ✓ Combinatorial integrals with unsigned short - test case passed (20 checks) +5: ✓ Combinatorial integrals with signed char - test case passed (20 checks) +5: ✓ Combinatorial integrals with unsigned char - test case passed (20 checks) +5: +5: ✓ Combinatorial integrals - test suite passed (200 checks in 10 test cases) +5: +5: • Failed combinatorial integrals - test suite started +5: +5: • Combinatorial integrals with signed long long - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42ll != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42ll != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42ll != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42ll != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42ll != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42ll != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42ll != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42ll != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42ll != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42ll != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42ll > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42ll < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43ll == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41ll >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43ll <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42ll > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42ll < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43ll == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41ll >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43ll <= 42u) +5: ✗ Combinatorial integrals with signed long long - test case FAILED (0 checks passed, 20 failed) +5: +5: • Combinatorial integrals unsigned long long - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42ull != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42ull != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42ull != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42ull != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42ull != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42ull != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42ull != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42ull != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42ull != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42ull != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42ull > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42ull < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43ull == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41ull >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43ull <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42ull > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42ull < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43ull == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41ull >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43ull <= 42u) +5: ✗ Combinatorial integrals unsigned long long - test case FAILED (0 checks passed, 20 failed) +5: +5: • Combinatorial integrals with signed int - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42 != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42 != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42 != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42 != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42 != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42 != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42 != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42 != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42 != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42 != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42 > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42 < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43 == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41 >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43 <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42 > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42 < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43 == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41 >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43 <= 42u) +5: ✗ Combinatorial integrals with signed int - test case FAILED (0 checks passed, 20 failed) +5: +5: • Combinatorial integrals with unsigned int - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42u != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42u != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42u != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42u != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42u != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42u != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42u != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42u != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42u != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42u != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42u > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42u < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43u == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41u >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43u <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42u > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42u < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43u == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41u >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43u <= 42u) +5: ✗ Combinatorial integrals with unsigned int - test case FAILED (0 checks passed, 20 failed) +5: +5: • Combinatorial integrals with signed short - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42 != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42 != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42 != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42 != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42 != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42 != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42 != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42 != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42 != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42 != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42 > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42 < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43 == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41 >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43 <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42 > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42 < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43 == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41 >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43 <= 42u) +5: ✗ Combinatorial integrals with signed short - test case FAILED (0 checks passed, 20 failed) +5: +5: • Combinatorial integrals with unsigned short - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42u != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42u != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42u != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42u != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42u != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42u != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42u != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42u != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42u != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42u != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42u > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42u < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43u == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41u >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43u <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42u > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42u < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43u == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41u >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43u <= 42u) +5: ✗ Combinatorial integrals with unsigned short - test case FAILED (0 checks passed, 20 failed) +5: +5: • Combinatorial integrals with signed short - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42s != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42s != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42s != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42s != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42s != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42s != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42s != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42s != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42s != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42s != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42s > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42s < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43s == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41s >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43s <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42s > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42s < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43s == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41s >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43s <= 42u) +5: ✗ Combinatorial integrals with signed short - test case FAILED (0 checks passed, 20 failed) +5: +5: • Combinatorial integrals with unsigned short - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42us != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42us != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42us != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42us != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42us != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42us != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42us != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42us != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42us != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42us != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42us > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42us < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43us == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41us >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43us <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42us > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42us < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43us == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41us >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43us <= 42u) +5: ✗ Combinatorial integrals with unsigned short - test case FAILED (0 checks passed, 20 failed) +5: +5: • Combinatorial integrals with signed char - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42c != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42c != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42c != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42c != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42c != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42c != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42c != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42c != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42c != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42c != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42c > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42c < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43c == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41c >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43c <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42c > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42c < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43c == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41c >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43c <= 42u) +5: ✗ Combinatorial integrals with signed char - test case FAILED (0 checks passed, 20 failed) +5: +5: • Combinatorial integrals with unsigned char - test case started +5: ✗ ne matches signed long long FAILED (micro-test-plus.h:102, 42uc != 42ll) +5: ✗ ne matches unsigned long long FAILED (micro-test-plus.h:102, 42uc != 42ull) +5: ✗ ne matches signed long FAILED (micro-test-plus.h:102, 42uc != 42l) +5: ✗ ne matches unsigned long FAILED (micro-test-plus.h:102, 42uc != 42ul) +5: ✗ ne matches signed int FAILED (micro-test-plus.h:102, 42uc != 42) +5: ✗ ne matches unsigned int FAILED (micro-test-plus.h:102, 42uc != 42u) +5: ✗ ne matches signed short FAILED (micro-test-plus.h:102, 42uc != 42s) +5: ✗ ne matches unsigned short FAILED (micro-test-plus.h:102, 42uc != 42us) +5: ✗ ne matches signed char FAILED (micro-test-plus.h:102, 42uc != 42c) +5: ✗ ne matches unsigned char FAILED (micro-test-plus.h:102, 42uc != 42uc) +5: ✗ gt matches signed int FAILED (micro-test-plus.h:102, 42uc > 42) +5: ✗ lt matches signed int FAILED (micro-test-plus.h:102, 42uc < 42) +5: ✗ eq matches signed int FAILED (micro-test-plus.h:102, 43uc == 42) +5: ✗ ge matches signed int FAILED (micro-test-plus.h:102, 41uc >= 42) +5: ✗ le matches signed int FAILED (micro-test-plus.h:102, 43uc <= 42) +5: ✗ gt matches unsigned int FAILED (micro-test-plus.h:102, 42uc > 42u) +5: ✗ lt matches unsigned int FAILED (micro-test-plus.h:102, 42uc < 42u) +5: ✗ eq matches unsigned int FAILED (micro-test-plus.h:102, 43uc == 42u) +5: ✗ ge matches unsigned int FAILED (micro-test-plus.h:102, 41uc >= 42u) +5: ✗ le matches unsigned int FAILED (micro-test-plus.h:102, 43uc <= 42u) +5: ✗ Combinatorial integrals with unsigned char - test case FAILED (0 checks passed, 20 failed) +5: +5: ✗ Failed combinatorial integrals - test suite FAILED (0 checks passed, 200 failed, in 10 test cases) +5: +5: • Combinatorial floats - test suite started +5: +5: ✓ Combinatorial floats - test case passed (11 checks) +5: ✓ Combinatorial doubles - test case passed (11 checks) +5: +5: ✓ Combinatorial floats - test suite passed (22 checks in 2 test cases) +5: +5: • Miscellaneous - test suite started +5: +5: ✓ Arrays - test case passed (4 checks) +5: +5: • Arrays failed - test case started +5: ✗ array{ 42 } ne array{ 42 } FAILED (micro-test-plus.h:102, {42} != {42}) +5: ✗ array{ 42 } eq array{ 43 } FAILED (micro-test-plus.h:102, {42} == {43}) +5: ✗ array{ 1, 2 } ne array{ 1, 2 } FAILED (micro-test-plus.h:102, {1, 2} != {1, 2}) +5: ✗ array{ 1, 2, 3 } eq array{ 1, 2, 4 } FAILED (micro-test-plus.h:102, {1, 2, 3} == {1, 2, 4}) +5: ✗ Arrays failed - test case FAILED (0 checks passed, 4 failed) +5: +5: ✓ Vectors - test case passed (6 checks) +5: +5: • Vectors failed - test case started +5: ✗ vector{ } != vector{ } FAILED (micro-test-plus.h:102, {} != {}) +5: ✗ FAILED (micro-test-plus.h:102, {} != {}) +5: ✗ vector{ 42 } != vector{ 42 } FAILED (micro-test-plus.h:102, {42} != {42}) +5: ✗ vector{ 42 } == vector{ 43 } FAILED (micro-test-plus.h:102, {42} == {43}) +5: ✗ vector{ 1, 2 } != vector{ 1, 2 } FAILED (micro-test-plus.h:102, {1, 2} != {1, 2}) +5: ✗ vector{ 1, 2, 3 } == vector{ 1, 2, 4 } FAILED (micro-test-plus.h:102, {1, 2, 3} == {1, 2, 4}) +5: ✗ Vectors failed - test case FAILED (0 checks passed, 6 failed) +5: +5: ✗ Miscellaneous - test suite FAILED (10 checks passed, 10 failed, in 4 test cases) +5: Overall, the µTest++ unit tests were successful! +5: +5/8 Test #5: unit-test ........................ Passed 0.01 sec +``` + +## Continuous Integration + +There is a GitHub Actions CI +[workflow](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/ci.yml) +that runs a selection of the +tests on every push; for details see +[ci.yml](../.github/workflows/ci.yml). + +## Extensive testing + +A second +[workflow](https://github.com/micro-os-plus/micro-test-plus-xpack/actions/workflows/test-all.yml) +can be triggered manually before releases, and runs all available tests +on all supported platforms; for details see +[test-all.yml](../.github/workflows/test-all.yml) + +@note +Running these tests on Apple Silicon macOS and Arm GNU/Linux +require the self-hosted runners for the `micro-os-plus` GitHub +organisation to be up and running. + +## Manual runs + +The full set can also be run manually with the following commands: + +```sh +npm install --global xpm@latest + +rm -rf ~/Work/micro-os-plus/micro-test-plus-xpack.git && \ +mkdir -p ~/Work/micro-os-plus && \ +git clone \ + --branch xpack-develop \ + https://github.com/micro-os-plus/micro-test-plus-xpack.git \ + ~/Work/micro-os-plus/micro-test-plus-xpack.git + +xpm run install-all -C ~/Work/micro-os-plus/micro-test-plus-xpack.git +xpm run test-all -C ~/Work/micro-os-plus/micro-test-plus-xpack.git +``` + +@note +On the first run, the install step might take quite some time, +since it has to download the toolchain archives, which are relatively +large, up to hundreds of MB.