summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 08:52:02 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 08:52:02 +0000
commit4c8c62c0872a5d7547d62654d07a27e159883e2d (patch)
tree4ce7d3315d932bcc9efbe068bbb9f19c8315e2d5
parentMerging upstream version 1.60.0. (diff)
downloadnghttp2-4c8c62c0872a5d7547d62654d07a27e159883e2d.tar.xz
nghttp2-4c8c62c0872a5d7547d62654d07a27e159883e2d.zip
Adding debian version 1.60.0-1.debian/1.60.0-1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--debian/changelog6
-rw-r--r--debian/control4
-rw-r--r--debian/libnghttp2-14.symbols14
-rw-r--r--debian/patches/0001-Make-fetch-ocsp-response-use-python3.patch18
-rw-r--r--debian/patches/0002-add-munit-explicitly.patch4100
-rw-r--r--debian/patches/series2
6 files changed, 4123 insertions, 21 deletions
diff --git a/debian/changelog b/debian/changelog
index 04038e8..2b2a512 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+nghttp2 (1.60.0-1) unstable; urgency=medium
+
+ * New upstream version 1.60.0
+
+ -- Tomasz Buchert <tomasz@debian.org> Fri, 08 Mar 2024 22:31:26 +0100
+
nghttp2 (1.59.0-1) unstable; urgency=medium
* New upstream version 1.59.0
diff --git a/debian/control b/debian/control
index dae16c5..605d639 100644
--- a/debian/control
+++ b/debian/control
@@ -13,7 +13,7 @@ Build-Depends: debhelper (>= 13),
libssl-dev,
libsystemd-dev,
libxml2-dev,
- pkg-config,
+ pkgconf,
zlib1g-dev
Build-Depends-Indep: python3-sphinx, python3-sphinx-rtd-theme
Standards-Version: 4.6.2
@@ -26,7 +26,7 @@ Package: libnghttp2-dev
Architecture: any
Multi-Arch: same
Section: libdevel
-Depends: libnghttp2-14 (= ${binary:Version}), pkg-config, ${misc:Depends}
+Depends: libnghttp2-14 (= ${binary:Version}), pkgconf, ${misc:Depends}
Suggests: libnghttp2-doc
Conflicts: libnghttp2-3-dev, libnghttp2-4-dev, libnghttp2-5-dev
Replaces: libnghttp2-3-dev, libnghttp2-4-dev, libnghttp2-5-dev
diff --git a/debian/libnghttp2-14.symbols b/debian/libnghttp2-14.symbols
index ac18b42..9f680af 100644
--- a/debian/libnghttp2-14.symbols
+++ b/debian/libnghttp2-14.symbols
@@ -13,7 +13,9 @@ libnghttp2.so.14 libnghttp2-14 #MINVER#
nghttp2_hd_deflate_get_max_dynamic_table_size@Base 1.4.0
nghttp2_hd_deflate_get_num_table_entries@Base 1.4.0
nghttp2_hd_deflate_get_table_entry@Base 1.4.0
+ nghttp2_hd_deflate_hd2@Base 1.60.0
nghttp2_hd_deflate_hd@Base 1.3.0
+ nghttp2_hd_deflate_hd_vec2@Base 1.60.0
nghttp2_hd_deflate_hd_vec@Base 1.14.0
nghttp2_hd_deflate_new2@Base 1.3.0
nghttp2_hd_deflate_new@Base 1.3.0
@@ -26,6 +28,7 @@ libnghttp2.so.14 libnghttp2-14 #MINVER#
nghttp2_hd_inflate_get_table_entry@Base 1.4.0
nghttp2_hd_inflate_hd@Base 1.3.0
nghttp2_hd_inflate_hd2@Base 1.11.0
+ nghttp2_hd_inflate_hd3@Base 1.60.0
nghttp2_hd_inflate_new2@Base 1.3.0
nghttp2_hd_inflate_new@Base 1.3.0
nghttp2_http2_strerror@Base 1.9.1
@@ -49,6 +52,7 @@ libnghttp2.so.14 libnghttp2-14 #MINVER#
nghttp2_option_set_server_fallback_rfc7540_priorities@Base 1.48.0
nghttp2_option_set_stream_reset_rate_limit@Base 1.57.0
nghttp2_option_set_user_recv_extension_type@Base 1.8.0
+ nghttp2_pack_settings_payload2@Base 1.60.0
nghttp2_pack_settings_payload@Base 1.3.0
nghttp2_priority_spec_check_default@Base 1.3.0
nghttp2_priority_spec_default_init@Base 1.3.0
@@ -62,6 +66,7 @@ libnghttp2.so.14 libnghttp2-14 #MINVER#
nghttp2_session_callbacks_del@Base 1.3.0
nghttp2_session_callbacks_new@Base 1.3.0
nghttp2_session_callbacks_set_before_frame_send_callback@Base 1.3.0
+ nghttp2_session_callbacks_set_data_source_read_length_callback2@Base 1.60.0
nghttp2_session_callbacks_set_data_source_read_length_callback@Base 1.3.0
nghttp2_session_callbacks_set_error_callback2@Base 1.28.0
nghttp2_session_callbacks_set_error_callback@Base 1.9.1
@@ -78,9 +83,13 @@ libnghttp2.so.14 libnghttp2-14 #MINVER#
nghttp2_session_callbacks_set_on_invalid_header_callback2@Base 1.14.0
nghttp2_session_callbacks_set_on_invalid_header_callback@Base 1.14.0
nghttp2_session_callbacks_set_on_stream_close_callback@Base 1.3.0
+ nghttp2_session_callbacks_set_pack_extension_callback2@Base 1.60.0
nghttp2_session_callbacks_set_pack_extension_callback@Base 1.8.0
+ nghttp2_session_callbacks_set_recv_callback2@Base 1.60.0
nghttp2_session_callbacks_set_recv_callback@Base 1.3.0
+ nghttp2_session_callbacks_set_select_padding_callback2@Base 1.60.0
nghttp2_session_callbacks_set_select_padding_callback@Base 1.3.0
+ nghttp2_session_callbacks_set_send_callback2@Base 1.60.0
nghttp2_session_callbacks_set_send_callback@Base 1.3.0
nghttp2_session_callbacks_set_send_data_callback@Base 1.3.0
nghttp2_session_callbacks_set_unpack_extension_callback@Base 1.8.0
@@ -117,7 +126,9 @@ libnghttp2.so.14 libnghttp2-14 #MINVER#
nghttp2_session_get_stream_remote_close@Base 1.3.0
nghttp2_session_get_stream_remote_window_size@Base 1.3.0
nghttp2_session_get_stream_user_data@Base 1.3.0
+ nghttp2_session_mem_recv2@Base 1.60.0
nghttp2_session_mem_recv@Base 1.3.0
+ nghttp2_session_mem_send2@Base 1.60.0
nghttp2_session_mem_send@Base 1.3.0
nghttp2_session_recv@Base 1.3.0
nghttp2_session_resume_data@Base 1.3.0
@@ -146,6 +157,7 @@ libnghttp2.so.14 libnghttp2-14 #MINVER#
nghttp2_stream_get_weight@Base 1.3.0
nghttp2_strerror@Base 1.3.0
nghttp2_submit_altsvc@Base 1.10.0
+ nghttp2_submit_data2@Base 1.60.0
nghttp2_submit_data@Base 1.3.0
nghttp2_submit_extension@Base 1.8.0
nghttp2_submit_goaway@Base 1.3.0
@@ -155,7 +167,9 @@ libnghttp2.so.14 libnghttp2-14 #MINVER#
nghttp2_submit_priority@Base 1.3.0
nghttp2_submit_priority_update@Base 1.48.0
nghttp2_submit_push_promise@Base 1.3.0
+ nghttp2_submit_request2@Base 1.60.0
nghttp2_submit_request@Base 1.3.0
+ nghttp2_submit_response2@Base 1.60.0
nghttp2_submit_response@Base 1.3.0
nghttp2_submit_rst_stream@Base 1.3.0
nghttp2_submit_settings@Base 1.3.0
diff --git a/debian/patches/0001-Make-fetch-ocsp-response-use-python3.patch b/debian/patches/0001-Make-fetch-ocsp-response-use-python3.patch
deleted file mode 100644
index 319ed21..0000000
--- a/debian/patches/0001-Make-fetch-ocsp-response-use-python3.patch
+++ /dev/null
@@ -1,18 +0,0 @@
-From: Tomasz Buchert <tomasz@debian.org>
-Date: Tue, 2 Jan 2018 11:09:26 +0100
-Subject: Make fetch-ocsp-response use python3
-
----
- script/fetch-ocsp-response | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/script/fetch-ocsp-response b/script/fetch-ocsp-response
-index 0ff7461..185116b 100755
---- a/script/fetch-ocsp-response
-+++ b/script/fetch-ocsp-response
-@@ -1,4 +1,4 @@
--#!/usr/bin/env python
-+#!/usr/bin/env python3
- # -*- coding: utf-8 -*-
-
- # nghttp2 - HTTP/2 C Library
diff --git a/debian/patches/0002-add-munit-explicitly.patch b/debian/patches/0002-add-munit-explicitly.patch
new file mode 100644
index 0000000..effb221
--- /dev/null
+++ b/debian/patches/0002-add-munit-explicitly.patch
@@ -0,0 +1,4100 @@
+From: Tomasz Buchert <tomasz@debian.org>
+Date: Fri, 29 Mar 2024 13:04:39 +0100
+Subject: add munit explicitly
+
+---
+ tests/munit/.appveyor.yml | 34 +
+ tests/munit/.clang-format | 215 ++++
+ tests/munit/.dir-locals.el | 2 +
+ tests/munit/.gitignore | 8 +
+ tests/munit/.travis.yml | 149 +++
+ tests/munit/COPYING | 21 +
+ tests/munit/README.md | 54 +
+ tests/munit/example.c | 351 +++++++
+ tests/munit/meson.build | 37 +
+ tests/munit/munit.c | 2457 ++++++++++++++++++++++++++++++++++++++++++++
+ tests/munit/munit.h | 575 +++++++++++
+ tests/munit/munitxx.h | 94 ++
+ 12 files changed, 3997 insertions(+)
+ create mode 100644 tests/munit/.appveyor.yml
+ create mode 100644 tests/munit/.clang-format
+ create mode 100644 tests/munit/.dir-locals.el
+ create mode 100644 tests/munit/.gitignore
+ create mode 100644 tests/munit/.travis.yml
+ create mode 100644 tests/munit/COPYING
+ create mode 100644 tests/munit/README.md
+ create mode 100644 tests/munit/example.c
+ create mode 100644 tests/munit/meson.build
+ create mode 100644 tests/munit/munit.c
+ create mode 100644 tests/munit/munit.h
+ create mode 100644 tests/munit/munitxx.h
+
+diff --git a/tests/munit/.appveyor.yml b/tests/munit/.appveyor.yml
+new file mode 100644
+index 0000000..5572092
+--- /dev/null
++++ b/tests/munit/.appveyor.yml
+@@ -0,0 +1,34 @@
++version: "{build}"
++
++environment:
++ matrix:
++ - ARCHITECTURE: x64
++ MSVC_VER: 14
++ - ARCHITECTURE: x86
++ MSVC_VER: 14
++ - ARCHITECTURE: x64
++ MSVC_VER: 12
++ - ARCHITECTURE: x86
++ MSVC_VER: 12
++ - ARCHITECTURE: x86
++ MSVC_VER: 11
++ - ARCHITECTURE: x86
++ MSVC_VER: 10
++ - ARCHITECTURE: x86
++ MSVC_VER: 9
++
++branches:
++ except:
++ - master
++ - /^(wip\/)?(travis|osx|mingw|ipp)(\-.+)?$/
++
++configuration: Debug
++
++install:
++
++before_build:
++ - call "C:\Program Files (x86)\Microsoft Visual Studio %MSVC_VER%.0\VC\vcvarsall.bat" %ARCHITECTURE%
++
++build_script: cl.exe /W4 /WX /Feexample munit.c example.c
++
++test_script: example.exe --color always
+diff --git a/tests/munit/.clang-format b/tests/munit/.clang-format
+new file mode 100644
+index 0000000..4aa455a
+--- /dev/null
++++ b/tests/munit/.clang-format
+@@ -0,0 +1,215 @@
++---
++Language: Cpp
++AccessModifierOffset: -2
++AlignAfterOpenBracket: Align
++AlignArrayOfStructures: None
++AlignConsecutiveAssignments:
++ Enabled: false
++ AcrossEmptyLines: false
++ AcrossComments: false
++ AlignCompound: false
++ PadOperators: true
++AlignConsecutiveBitFields:
++ Enabled: false
++ AcrossEmptyLines: false
++ AcrossComments: false
++ AlignCompound: false
++ PadOperators: true
++AlignConsecutiveDeclarations:
++ Enabled: false
++ AcrossEmptyLines: false
++ AcrossComments: false
++ AlignCompound: false
++ PadOperators: true
++AlignConsecutiveMacros:
++ Enabled: false
++ AcrossEmptyLines: false
++ AcrossComments: false
++ AlignCompound: false
++ PadOperators: true
++AlignEscapedNewlines: Right
++AlignOperands: Align
++AlignTrailingComments: true
++AllowAllArgumentsOnNextLine: true
++AllowAllParametersOfDeclarationOnNextLine: true
++AllowShortEnumsOnASingleLine: true
++AllowShortBlocksOnASingleLine: Never
++AllowShortCaseLabelsOnASingleLine: false
++AllowShortFunctionsOnASingleLine: All
++AllowShortLambdasOnASingleLine: All
++AllowShortIfStatementsOnASingleLine: Never
++AllowShortLoopsOnASingleLine: false
++AlwaysBreakAfterDefinitionReturnType: None
++AlwaysBreakAfterReturnType: None
++AlwaysBreakBeforeMultilineStrings: false
++AlwaysBreakTemplateDeclarations: MultiLine
++AttributeMacros:
++ - __capability
++BinPackArguments: true
++BinPackParameters: true
++BraceWrapping:
++ AfterCaseLabel: false
++ AfterClass: false
++ AfterControlStatement: Never
++ AfterEnum: false
++ AfterFunction: false
++ AfterNamespace: false
++ AfterObjCDeclaration: false
++ AfterStruct: false
++ AfterUnion: false
++ AfterExternBlock: false
++ BeforeCatch: false
++ BeforeElse: false
++ BeforeLambdaBody: false
++ BeforeWhile: false
++ IndentBraces: false
++ SplitEmptyFunction: true
++ SplitEmptyRecord: true
++ SplitEmptyNamespace: true
++BreakBeforeBinaryOperators: None
++BreakBeforeConceptDeclarations: Always
++BreakBeforeBraces: Attach
++BreakBeforeInheritanceComma: false
++BreakInheritanceList: BeforeColon
++BreakBeforeTernaryOperators: true
++BreakConstructorInitializersBeforeComma: false
++BreakConstructorInitializers: BeforeColon
++BreakAfterJavaFieldAnnotations: false
++BreakStringLiterals: true
++ColumnLimit: 80
++CommentPragmas: '^ IWYU pragma:'
++QualifierAlignment: Leave
++CompactNamespaces: false
++ConstructorInitializerIndentWidth: 4
++ContinuationIndentWidth: 4
++Cpp11BracedListStyle: true
++DeriveLineEnding: true
++DerivePointerAlignment: false
++DisableFormat: false
++EmptyLineAfterAccessModifier: Never
++EmptyLineBeforeAccessModifier: LogicalBlock
++ExperimentalAutoDetectBinPacking: false
++PackConstructorInitializers: NextLine
++BasedOnStyle: ''
++ConstructorInitializerAllOnOneLineOrOnePerLine: false
++AllowAllConstructorInitializersOnNextLine: true
++FixNamespaceComments: true
++ForEachMacros:
++ - foreach
++ - Q_FOREACH
++ - BOOST_FOREACH
++IfMacros:
++ - KJ_IF_MAYBE
++IncludeBlocks: Preserve
++IncludeCategories:
++ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
++ Priority: 2
++ SortPriority: 0
++ CaseSensitive: false
++ - Regex: '^(<|"(gtest|isl|json)/)'
++ Priority: 3
++ SortPriority: 0
++ CaseSensitive: false
++ - Regex: '.*'
++ Priority: 1
++ SortPriority: 0
++ CaseSensitive: false
++IncludeIsMainRegex: '$'
++IncludeIsMainSourceRegex: ''
++IndentAccessModifiers: false
++IndentCaseLabels: false
++IndentCaseBlocks: false
++IndentGotoLabels: true
++IndentPPDirectives: AfterHash
++IndentExternBlock: AfterExternBlock
++IndentRequiresClause: false
++IndentWidth: 2
++IndentWrappedFunctionNames: false
++InsertBraces: false
++InsertTrailingCommas: None
++JavaScriptQuotes: Leave
++JavaScriptWrapImports: true
++KeepEmptyLinesAtTheStartOfBlocks: true
++LambdaBodyIndentation: Signature
++MacroBlockBegin: ''
++MacroBlockEnd: ''
++MaxEmptyLinesToKeep: 1
++NamespaceIndentation: None
++ObjCBinPackProtocolList: Auto
++ObjCBlockIndentWidth: 2
++ObjCBreakBeforeNestedBlockParam: true
++ObjCSpaceAfterProperty: false
++ObjCSpaceBeforeProtocolList: true
++PenaltyBreakAssignment: 2
++PenaltyBreakBeforeFirstCallParameter: 19
++PenaltyBreakComment: 300
++PenaltyBreakFirstLessLess: 120
++PenaltyBreakOpenParenthesis: 0
++PenaltyBreakString: 1000
++PenaltyBreakTemplateDeclaration: 10
++PenaltyExcessCharacter: 1000000
++PenaltyReturnTypeOnItsOwnLine: 60
++PenaltyIndentedWhitespace: 0
++PointerAlignment: Right
++PPIndentWidth: -1
++ReferenceAlignment: Pointer
++ReflowComments: true
++RemoveBracesLLVM: false
++RequiresClausePosition: OwnLine
++SeparateDefinitionBlocks: Leave
++ShortNamespaceLines: 1
++SortIncludes: Never
++SortJavaStaticImport: Before
++SortUsingDeclarations: true
++SpaceAfterCStyleCast: false
++SpaceAfterLogicalNot: false
++SpaceAfterTemplateKeyword: true
++SpaceBeforeAssignmentOperators: true
++SpaceBeforeCaseColon: false
++SpaceBeforeCpp11BracedList: false
++SpaceBeforeCtorInitializerColon: true
++SpaceBeforeInheritanceColon: true
++SpaceBeforeParens: ControlStatements
++SpaceBeforeParensOptions:
++ AfterControlStatements: true
++ AfterForeachMacros: true
++ AfterFunctionDefinitionName: false
++ AfterFunctionDeclarationName: false
++ AfterIfMacros: true
++ AfterOverloadedOperator: false
++ AfterRequiresInClause: false
++ AfterRequiresInExpression: false
++ BeforeNonEmptyParentheses: false
++SpaceAroundPointerQualifiers: Default
++SpaceBeforeRangeBasedForLoopColon: true
++SpaceInEmptyBlock: false
++SpaceInEmptyParentheses: false
++SpacesBeforeTrailingComments: 1
++SpacesInAngles: Never
++SpacesInConditionalStatement: false
++SpacesInContainerLiterals: true
++SpacesInCStyleCastParentheses: false
++SpacesInLineCommentPrefix:
++ Minimum: 1
++ Maximum: -1
++SpacesInParentheses: false
++SpacesInSquareBrackets: false
++SpaceBeforeSquareBrackets: false
++BitFieldColonSpacing: Both
++Standard: Latest
++StatementAttributeLikeMacros:
++ - Q_EMIT
++StatementMacros:
++ - Q_UNUSED
++ - QT_REQUIRE_VERSION
++TabWidth: 8
++UseCRLF: false
++UseTab: Never
++WhitespaceSensitiveMacros:
++ - STRINGIZE
++ - PP_STRINGIZE
++ - BOOST_PP_STRINGIZE
++ - NS_SWIFT_NAME
++ - CF_SWIFT_NAME
++...
++
+diff --git a/tests/munit/.dir-locals.el b/tests/munit/.dir-locals.el
+new file mode 100644
+index 0000000..4cc5d84
+--- /dev/null
++++ b/tests/munit/.dir-locals.el
+@@ -0,0 +1,2 @@
++((nil . ((indent-tabs-mode . nil)
++ (c-basic-offset . 2))))
+diff --git a/tests/munit/.gitignore b/tests/munit/.gitignore
+new file mode 100644
+index 0000000..6ad0b44
+--- /dev/null
++++ b/tests/munit/.gitignore
+@@ -0,0 +1,8 @@
++*~
++.#*
++/*.o
++/example
++/*.exe
++/*.dll
++.dirstamp
++.deps
+diff --git a/tests/munit/.travis.yml b/tests/munit/.travis.yml
+new file mode 100644
+index 0000000..6f221a8
+--- /dev/null
++++ b/tests/munit/.travis.yml
+@@ -0,0 +1,149 @@
++language: c
++sudo: false
++dist: trusty
++branches:
++ except:
++ - /^(wip\/)?(appveyor|msvc|mingw|windows)(\-.+)?$/
++ # https://github.com/travis-ci/travis-ci/issues/6632
++ # - /^master$/
++matrix:
++ include:
++ ###
++ ## Linux builds using various versions of GCC.
++ ###
++ - env: C_COMPILER=gcc-6
++ addons:
++ apt:
++ sources:
++ - ubuntu-toolchain-r-test
++ packages:
++ - gcc-6
++ - g++-6
++ - env: C_COMPILER=gcc-5
++ addons:
++ apt:
++ sources:
++ - ubuntu-toolchain-r-test
++ packages:
++ - gcc-5
++ - g++-5
++ # - env: C_COMPILER=gcc-4.9
++ # addons:
++ # apt:
++ # sources:
++ # - ubuntu-toolchain-r-test
++ # packages:
++ # - gcc-4.9
++ # - g++-4.9
++ - env: C_COMPILER=gcc-4.8
++ addons:
++ apt:
++ sources:
++ - ubuntu-toolchain-r-test
++ packages:
++ - gcc-4.8
++ - g++-4.8
++ # - env: C_COMPILER=gcc-4.7
++ # addons:
++ # apt:
++ # sources:
++ # - ubuntu-toolchain-r-test
++ # packages:
++ # - gcc-4.7
++ # - g++-4.7
++ - env: C_COMPILER=gcc-4.6
++ addons:
++ apt:
++ sources:
++ - ubuntu-toolchain-r-test
++ packages:
++ - gcc-4.6
++ - g++-4.6
++ # - os: linux
++ # env: C_COMPILER=gcc-4.5
++ # addons:
++ # apt:
++ # sources:
++ # - ubuntu-toolchain-r-test
++ # packages:
++ # - gcc-4.5
++ # - g++-4.5
++ - env: C_COMPILER=gcc-4.4
++ addons:
++ apt:
++ sources:
++ - ubuntu-toolchain-r-test
++ packages:
++ - gcc-4.4
++ - g++-4.4
++
++ ###
++ ## clang on Linux
++ ###
++ - env: C_COMPILER=clang-3.9
++ addons:
++ apt:
++ sources:
++ - llvm-toolchain-precise-3.9
++ - ubuntu-toolchain-r-test
++ packages:
++ - clang-3.9
++ # - env: C_COMPILER=clang-3.8
++ # addons:
++ # apt:
++ # sources:
++ # - llvm-toolchain-precise-3.8
++ # - ubuntu-toolchain-r-test
++ # packages:
++ # - clang-3.8
++ - env: C_COMPILER=clang-3.7
++ addons:
++ apt:
++ sources:
++ - llvm-toolchain-precise-3.7
++ - ubuntu-toolchain-r-test
++ packages:
++ - clang-3.7
++ # - env: C_COMPILER=clang-3.6
++ # addons:
++ # apt:
++ # sources:
++ # - llvm-toolchain-precise-3.6
++ # - ubuntu-toolchain-r-test
++ # packages:
++ # - clang-3.6
++ - env: C_COMPILER=clang-3.5
++ addons:
++ apt:
++ sources:
++ - llvm-toolchain-precise-3.5
++ - ubuntu-toolchain-r-test
++ packages:
++ - clang-3.5
++
++ ###
++ ## PGI
++ ###
++ - env: C_COMPILER=pgcc OPENMP=y
++
++ ###
++ ## OS X
++ ###
++ - os: osx
++
++ ###
++ ## Meson
++ ###
++ - env: BUILD_SYSTEM=meson
++
++before_install:
++- if [ -n "${C_COMPILER}" ]; then export CC="${C_COMPILER}"; fi
++- if [ "${C_COMPILER}" = "pgcc" ]; then wget -q -O /dev/stdout 'https://raw.githubusercontent.com/nemequ/pgi-travis/master/install-pgi.sh' | /bin/sh; fi
++- if [ "${BUILD_SYSTEM}" = "meson" ]; then wget -O /tmp/ninja-linux.zip $(curl -s https://api.github.com/repos/ninja-build/ninja/releases/latest | grep -oP 'https://github.com/ninja-build/ninja/releases/download/v[0-9\.]+/ninja-linux.zip') && unzip -q /tmp/ninja-linux.zip -d ~/bin && pyenv local 3.6 && pip3 install meson; fi
++
++script:
++ - if [ "${BUILD_SYSTEM}" = "meson" ]; then meson build && ninja -Cbuild; else make CC="${CC}" AGGRESSIVE_WARNINGS=y EXTENSION="${EXTENSION}" OPENMP="${OPENMP}" ASAN="${ASAN}" UBSAN="${UBSAN}"; fi
++ - if [ "${BUILD_SYSTEM}" = "meson" ]; then ninja -Cbuild test; else make test; fi
++
++notifications:
++ email: false
+diff --git a/tests/munit/COPYING b/tests/munit/COPYING
+new file mode 100644
+index 0000000..2991ac7
+--- /dev/null
++++ b/tests/munit/COPYING
+@@ -0,0 +1,21 @@
++µnit Testing Framework
++Copyright (c) 2013-2016 Evan Nemerson <evan@nemerson.com>
++
++Permission is hereby granted, free of charge, to any person obtaining
++a copy of this software and associated documentation files (the
++"Software"), to deal in the Software without restriction, including
++without limitation the rights to use, copy, modify, merge, publish,
++distribute, sublicense, and/or sell copies of the Software, and to
++permit persons to whom the Software is furnished to do so, subject to
++the following conditions:
++
++The above copyright notice and this permission notice shall be
++included in all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
++LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+diff --git a/tests/munit/README.md b/tests/munit/README.md
+new file mode 100644
+index 0000000..9f861f2
+--- /dev/null
++++ b/tests/munit/README.md
+@@ -0,0 +1,54 @@
++# µnit
++
++µnit is a small but full-featured unit testing framework for C. It has
++no dependencies (beyond libc), is permissively licensed (MIT), and is
++easy to include into any project.
++
++For more information, see
++[the µnit web site](https://nemequ.github.io/munit).
++
++[![Build status](https://travis-ci.org/nemequ/munit.svg?branch=master)](https://travis-ci.org/nemequ/munit)
++[![Windows build status](https://ci.appveyor.com/api/projects/status/db515g5ifcwjohq7/branch/master?svg=true)](https://ci.appveyor.com/project/quixdb/munit/branch/master)
++
++## Features
++
++Features µnit currently includes include:
++
++ * Handy assertion macros which make for nice error messages.
++ * Reproducible cross-platform random number generation, including
++ support for supplying a seed via CLI.
++ * Timing of both wall-clock and CPU time.
++ * Parameterized tests.
++ * Nested test suites.
++ * Flexible CLI.
++ * Forking
++ ([except on Windows](https://github.com/nemequ/munit/issues/2)).
++ * Hiding output of successful tests.
++
++Features µnit does not currently include, but some day may include
++(a.k.a., if you file a PR…), include:
++
++ * [TAP](http://testanything.org/) support; feel free to discuss in
++ [issue #1](https://github.com/nemequ/munit/issues/1)
++
++### Include into your project with meson
++
++In your `subprojects` folder put a `munit.wrap` file containing:
++
++```
++[wrap-git]
++directory=munit
++url=https://github.com/nemequ/munit/
++revision=head
++```
++
++Then you can use a subproject fallback when you include munit as a
++dependency to your project: `dependency('munit', fallback: ['munit', 'munit_dep'])`
++
++## Documentation
++
++See [the µnit web site](https://nemequ.github.io/munit).
++
++Additionally, there is a heavily-commented
++[example.c](https://github.com/nemequ/munit/blob/master/example.c) in
++the repository.
+diff --git a/tests/munit/example.c b/tests/munit/example.c
+new file mode 100644
+index 0000000..d238d09
+--- /dev/null
++++ b/tests/munit/example.c
+@@ -0,0 +1,351 @@
++/* Example file for using µnit.
++ *
++ * µnit is MIT-licensed, but for this file and this file alone:
++ *
++ * To the extent possible under law, the author(s) of this file have
++ * waived all copyright and related or neighboring rights to this
++ * work. See <https://creativecommons.org/publicdomain/zero/1.0/> for
++ * details.
++ *********************************************************************/
++
++#include "munit.h"
++
++/* This is just to disable an MSVC warning about conditional
++ * expressions being constant, which you shouldn't have to do for your
++ * code. It's only here because we want to be able to do silly things
++ * like assert that 0 != 1 for our demo. */
++#if defined(_MSC_VER)
++#pragma warning(disable: 4127)
++#endif
++
++/* Tests are functions that return void, and take a single void*
++ * parameter. We'll get to what that parameter is later. */
++static MunitResult
++test_compare(const MunitParameter params[], void* data) {
++ /* We'll use these later */
++ const unsigned char val_uchar = 'b';
++ const short val_short = 1729;
++ double pi = 3.141592654;
++ char* stewardesses = "stewardesses";
++ char* most_fun_word_to_type;
++
++ /* These are just to silence compiler warnings about the parameters
++ * being unused. */
++ (void) params;
++ (void) data;
++
++ /* Let's start with the basics. */
++ munit_assert(0 != 1);
++
++ /* There is also the more verbose, though slightly more descriptive
++ munit_assert_true/false: */
++ munit_assert_false(0);
++
++ /* You can also call munit_error and munit_errorf yourself. We
++ * won't do it is used to indicate a failure, but here is what it
++ * would look like: */
++ /* munit_error("FAIL"); */
++ /* munit_errorf("Goodbye, cruel %s", "world"); */
++
++ /* There are macros for comparing lots of types. */
++ munit_assert_char('a', ==, 'a');
++
++ /* Sure, you could just assert('a' == 'a'), but if you did that, a
++ * failed assertion would just say something like "assertion failed:
++ * val_uchar == 'b'". µnit will tell you the actual values, so a
++ * failure here would result in something like "assertion failed:
++ * val_uchar == 'b' ('X' == 'b')." */
++ munit_assert_uchar(val_uchar, ==, 'b');
++
++ /* Obviously we can handle values larger than 'char' and 'uchar'.
++ * There are versions for char, short, int, long, long long,
++ * int8/16/32/64_t, as well as the unsigned versions of them all. */
++ munit_assert_short(42, <, val_short);
++
++ /* There is also support for size_t.
++ *
++ * The longest word in English without repeating any letters is
++ * "uncopyrightables", which has uncopyrightable (and
++ * dermatoglyphics, which is the study of fingerprints) beat by a
++ * character */
++ munit_assert_size(strlen("uncopyrightables"), >, strlen("dermatoglyphics"));
++
++ /* Of course there is also support for doubles and floats. */
++ munit_assert_double(pi, ==, 3.141592654);
++
++ /* If you want to compare two doubles for equality, you might want
++ * to consider using munit_assert_double_equal. It compares two
++ * doubles for equality within a precison of 1.0 x 10^-(precision).
++ * Note that precision (the third argument to the macro) needs to be
++ * fully evaluated to an integer by the preprocessor so µnit doesn't
++ * have to depend pow, which is often in libm not libc. */
++ munit_assert_double_equal(3.141592654, 3.141592653589793, 9);
++
++ /* And if you want to check strings for equality (or inequality),
++ * there is munit_assert_string_equal/not_equal.
++ *
++ * "stewardesses" is the longest word you can type on a QWERTY
++ * keyboard with only one hand, which makes it loads of fun to type.
++ * If I'm going to have to type a string repeatedly, let's make it a
++ * good one! */
++ munit_assert_string_equal(stewardesses, "stewardesses");
++
++ /* A personal favorite macro which is fantastic if you're working
++ * with binary data, is the one which naïvely checks two blobs of
++ * memory for equality. If this fails it will tell you the offset
++ * of the first differing byte. */
++ munit_assert_memory_equal(7, stewardesses, "steward");
++
++ /* You can also make sure that two blobs differ *somewhere*: */
++ munit_assert_memory_not_equal(8, stewardesses, "steward");
++
++ /* There are equal/not_equal macros for pointers, too: */
++ most_fun_word_to_type = stewardesses;
++ munit_assert_ptr_equal(most_fun_word_to_type, stewardesses);
++
++ /* And null/not_null */
++ munit_assert_null(NULL);
++ munit_assert_not_null(most_fun_word_to_type);
++
++ /* Lets verify that the data parameter is what we expected. We'll
++ * see where this comes from in a bit.
++ *
++ * Note that the casting isn't usually required; if you give this
++ * function a real pointer (instead of a number like 0xdeadbeef) it
++ * would work as expected. */
++ munit_assert_ptr_equal(data, (void*)(uintptr_t)0xdeadbeef);
++
++ return MUNIT_OK;
++}
++
++static MunitResult
++test_rand(const MunitParameter params[], void* user_data) {
++ int random_int;
++ double random_dbl;
++ munit_uint8_t data[5];
++
++ (void) params;
++ (void) user_data;
++
++ /* One thing missing from a lot of unit testing frameworks is a
++ * random number generator. You can't just use srand/rand because
++ * the implementation varies across different platforms, and it's
++ * important to be able to look at the seed used in a failing test
++ * to see if you can reproduce it. Some randomness is a fantastic
++ * thing to have in your tests, I don't know why more people don't
++ * do it...
++ *
++ * µnit's PRNG is re-seeded with the same value for each iteration
++ * of each test. The seed is retrieved from the MUNIT_SEED
++ * envirnment variable or, if none is provided, one will be
++ * (pseudo-)randomly generated. */
++
++ /* If you need an integer in a given range */
++ random_int = munit_rand_int_range(128, 4096);
++ munit_assert_int(random_int, >=, 128);
++ munit_assert_int(random_int, <=, 4096);
++
++ /* Or maybe you want a double, between 0 and 1: */
++ random_dbl = munit_rand_double();
++ munit_assert_double(random_dbl, >=, 0.0);
++ munit_assert_double(random_dbl, <=, 1.0);
++
++ /* Of course, you want to be able to reproduce bugs discovered
++ * during testing, so every time the tests are run they print the
++ * random seed used. When you want to reproduce a result, just put
++ * that random seed in the MUNIT_SEED environment variable; it even
++ * works on different platforms.
++ *
++ * If you want this to pass, use 0xdeadbeef as the random seed and
++ * uncomment the next line of code. Note that the PRNG is not
++ * re-seeded between iterations of the same test, so this will only
++ * work on the first iteration. */
++ /* munit_assert_uint32(munit_rand_uint32(), ==, 1306447409); */
++
++ /* You can also get blobs of random memory: */
++ munit_rand_memory(sizeof(data), data);
++
++ return MUNIT_OK;
++}
++
++/* This test case shows how to accept parameters. We'll see how to
++ * specify them soon.
++ *
++ * By default, every possible variation of a parameterized test is
++ * run, but you can specify parameters manually if you want to only
++ * run specific test(s), or you can pass the --single argument to the
++ * CLI to have the harness simply choose one variation at random
++ * instead of running them all. */
++static MunitResult
++test_parameters(const MunitParameter params[], void* user_data) {
++ const char* foo;
++ const char* bar;
++
++ (void) user_data;
++
++ /* The "foo" parameter is specified as one of the following values:
++ * "one", "two", or "three". */
++ foo = munit_parameters_get(params, "foo");
++ /* Similarly, "bar" is one of "four", "five", or "six". */
++ bar = munit_parameters_get(params, "bar");
++ /* "baz" is a bit more complicated. We don't actually specify a
++ * list of valid values, so by default NULL is passed. However, the
++ * CLI will accept any value. This is a good way to have a value
++ * that is usually selected randomly by the test, but can be
++ * overridden on the command line if desired. */
++ /* const char* baz = munit_parameters_get(params, "baz"); */
++
++ /* Notice that we're returning MUNIT_FAIL instead of writing an
++ * error message. Error messages are generally preferable, since
++ * they make it easier to diagnose the issue, but this is an
++ * option.
++ *
++ * Possible values are:
++ * - MUNIT_OK: Sucess
++ * - MUNIT_FAIL: Failure
++ * - MUNIT_SKIP: The test was skipped; usually this happens when a
++ * particular feature isn't in use. For example, if you're
++ * writing a test which uses a Wayland-only feature, but your
++ * application is running on X11.
++ * - MUNIT_ERROR: The test failed, but not because of anything you
++ * wanted to test. For example, maybe your test downloads a
++ * remote resource and tries to parse it, but the network was
++ * down.
++ */
++
++ if (strcmp(foo, "one") != 0 &&
++ strcmp(foo, "two") != 0 &&
++ strcmp(foo, "three") != 0)
++ return MUNIT_FAIL;
++
++ if (strcmp(bar, "red") != 0 &&
++ strcmp(bar, "green") != 0 &&
++ strcmp(bar, "blue") != 0)
++ return MUNIT_FAIL;
++
++ return MUNIT_OK;
++}
++
++/* The setup function, if you provide one, for a test will be run
++ * before the test, and the return value will be passed as the sole
++ * parameter to the test function. */
++static void*
++test_compare_setup(const MunitParameter params[], void* user_data) {
++ (void) params;
++
++ munit_assert_string_equal(user_data, "µnit");
++ return (void*) (uintptr_t) 0xdeadbeef;
++}
++
++/* To clean up after a test, you can use a tear down function. The
++ * fixture argument is the value returned by the setup function
++ * above. */
++static void
++test_compare_tear_down(void* fixture) {
++ munit_assert_ptr_equal(fixture, (void*)(uintptr_t)0xdeadbeef);
++}
++
++static char* foo_params[] = {
++ (char*) "one", (char*) "two", (char*) "three", NULL
++};
++
++static char* bar_params[] = {
++ (char*) "red", (char*) "green", (char*) "blue", NULL
++};
++
++static MunitParameterEnum test_params[] = {
++ { (char*) "foo", foo_params },
++ { (char*) "bar", bar_params },
++ { (char*) "baz", NULL },
++ { NULL, NULL },
++};
++
++/* Creating a test suite is pretty simple. First, you'll need an
++ * array of tests: */
++static MunitTest test_suite_tests[] = {
++ {
++ /* The name is just a unique human-readable way to identify the
++ * test. You can use it to run a specific test if you want, but
++ * usually it's mostly decorative. */
++ (char*) "/example/compare",
++ /* You probably won't be surprised to learn that the tests are
++ * functions. */
++ test_compare,
++ /* If you want, you can supply a function to set up a fixture. If
++ * you supply NULL, the user_data parameter from munit_suite_main
++ * will be used directly. If, however, you provide a callback
++ * here the user_data parameter will be passed to this callback,
++ * and the return value from this callback will be passed to the
++ * test function.
++ *
++ * For our example we don't really need a fixture, but lets
++ * provide one anyways. */
++ test_compare_setup,
++ /* If you passed a callback for the fixture setup function, you
++ * may want to pass a corresponding callback here to reverse the
++ * operation. */
++ test_compare_tear_down,
++ /* Finally, there is a bitmask for options you can pass here. You
++ * can provide either MUNIT_TEST_OPTION_NONE or 0 here to use the
++ * defaults. */
++ MUNIT_TEST_OPTION_NONE,
++ NULL
++ },
++ /* Usually this is written in a much more compact format; all these
++ * comments kind of ruin that, though. Here is how you'll usually
++ * see entries written: */
++ { (char*) "/example/rand", test_rand, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
++ /* To tell the test runner when the array is over, just add a NULL
++ * entry at the end. */
++ { (char*) "/example/parameters", test_parameters, NULL, NULL, MUNIT_TEST_OPTION_NONE, test_params },
++ { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
++};
++
++/* If you wanted to have your test suite run other test suites you
++ * could declare an array of them. Of course each sub-suite can
++ * contain more suites, etc. */
++/* static const MunitSuite other_suites[] = { */
++/* { "/second", test_suite_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE }, */
++/* { NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE } */
++/* }; */
++
++/* Now we'll actually declare the test suite. You could do this in
++ * the main function, or on the heap, or whatever you want. */
++static const MunitSuite test_suite = {
++ /* This string will be prepended to all test names in this suite;
++ * for example, "/example/rand" will become "/µnit/example/rand".
++ * Note that, while it doesn't really matter for the top-level
++ * suite, NULL signal the end of an array of tests; you should use
++ * an empty string ("") instead. */
++ (char*) "",
++ /* The first parameter is the array of test suites. */
++ test_suite_tests,
++ /* In addition to containing test cases, suites can contain other
++ * test suites. This isn't necessary in this example, but it can be
++ * a great help to projects with lots of tests by making it easier
++ * to spread the tests across many files. This is where you would
++ * put "other_suites" (which is commented out above). */
++ NULL,
++ /* An interesting feature of µnit is that it supports automatically
++ * running multiple iterations of the tests. This is usually only
++ * interesting if you make use of the PRNG to randomize your tests
++ * cases a bit, or if you are doing performance testing and want to
++ * average multiple runs. 0 is an alias for 1. */
++ 1,
++ /* Just like MUNIT_TEST_OPTION_NONE, you can provide
++ * MUNIT_SUITE_OPTION_NONE or 0 to use the default settings. */
++ MUNIT_SUITE_OPTION_NONE
++};
++
++/* This is only necessary for EXIT_SUCCESS and EXIT_FAILURE, which you
++ * *should* be using but probably aren't (no, zero and non-zero don't
++ * always mean success and failure). I guess my point is that nothing
++ * about µnit requires it. */
++#include <stdlib.h>
++
++int main(int argc, char* argv[MUNIT_ARRAY_PARAM(argc + 1)]) {
++ /* Finally, we'll actually run our test suite! That second argument
++ * is the user_data parameter which will be passed either to the
++ * test or (if provided) the fixture setup function. */
++ return munit_suite_main(&test_suite, (void*) "µnit", argc, argv);
++}
+diff --git a/tests/munit/meson.build b/tests/munit/meson.build
+new file mode 100644
+index 0000000..c15b405
+--- /dev/null
++++ b/tests/munit/meson.build
+@@ -0,0 +1,37 @@
++project('munit', 'c')
++
++conf_data = configuration_data()
++conf_data.set('version', '0.2.0')
++
++add_project_arguments('-std=c99', language : 'c')
++
++cc = meson.get_compiler('c')
++
++root_include = include_directories('.')
++
++munit = library('munit',
++ ['munit.c'],
++ install: meson.is_subproject())
++
++if meson.is_subproject()
++ munit_dep = declare_dependency(
++ include_directories : root_include,
++ link_with : munit)
++else
++ # standalone install
++ install_headers('munit.h')
++
++ pkg = import('pkgconfig')
++ pkg.generate(name: 'munit',
++ description: 'µnit Testing Library for C',
++ version: conf_data.get('version'),
++ libraries: munit)
++
++ # compile the demo project
++ munit_example_src = files('example.c')
++ munit_example = executable('munit_example', munit_example_src,
++ include_directories: root_include,
++ link_with: munit)
++
++ test('munit example test', munit_example)
++endif
+diff --git a/tests/munit/munit.c b/tests/munit/munit.c
+new file mode 100644
+index 0000000..d6a0ec6
+--- /dev/null
++++ b/tests/munit/munit.c
+@@ -0,0 +1,2457 @@
++/* Copyright (c) 2013-2018 Evan Nemerson <evan@nemerson.com>
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++/*** Configuration ***/
++
++/* This is just where the output from the test goes. It's really just
++ * meant to let you choose stdout or stderr, but if anyone really want
++ * to direct it to a file let me know, it would be fairly easy to
++ * support. */
++#if !defined(MUNIT_OUTPUT_FILE)
++# define MUNIT_OUTPUT_FILE stdout
++#endif
++
++/* This is a bit more useful; it tells µnit how to format the seconds in
++ * timed tests. If your tests run for longer you might want to reduce
++ * it, and if your computer is really fast and your tests are tiny you
++ * can increase it. */
++#if !defined(MUNIT_TEST_TIME_FORMAT)
++# define MUNIT_TEST_TIME_FORMAT "0.8f"
++#endif
++
++/* If you have long test names you might want to consider bumping
++ * this. The result information takes 43 characters. */
++#if !defined(MUNIT_TEST_NAME_LEN)
++# define MUNIT_TEST_NAME_LEN 37
++#endif
++
++/* If you don't like the timing information, you can disable it by
++ * defining MUNIT_DISABLE_TIMING. */
++#if !defined(MUNIT_DISABLE_TIMING)
++# define MUNIT_ENABLE_TIMING
++#endif
++
++/*** End configuration ***/
++
++#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE < 200809L)
++# undef _POSIX_C_SOURCE
++#endif
++#if !defined(_POSIX_C_SOURCE)
++# define _POSIX_C_SOURCE 200809L
++#endif
++
++/* Solaris freaks out if you try to use a POSIX or SUS standard without
++ * the "right" C standard. */
++#if defined(_XOPEN_SOURCE)
++# undef _XOPEN_SOURCE
++#endif
++
++#if defined(__STDC_VERSION__)
++# if __STDC_VERSION__ >= 201112L
++# define _XOPEN_SOURCE 700
++# elif __STDC_VERSION__ >= 199901L
++# define _XOPEN_SOURCE 600
++# endif
++#endif
++
++/* Because, according to Microsoft, POSIX is deprecated. You've got
++ * to appreciate the chutzpah. */
++#if defined(_MSC_VER) && !defined(_CRT_NONSTDC_NO_DEPRECATE)
++# define _CRT_NONSTDC_NO_DEPRECATE
++#endif
++
++#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
++# include <stdbool.h>
++#elif defined(_WIN32)
++/* https://msdn.microsoft.com/en-us/library/tf4dy80a.aspx */
++#endif
++
++#include <limits.h>
++#include <time.h>
++#include <errno.h>
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <stdarg.h>
++#include <setjmp.h>
++
++#if !defined(MUNIT_NO_NL_LANGINFO) && !defined(_WIN32)
++# define MUNIT_NL_LANGINFO
++# include <locale.h>
++# include <langinfo.h>
++# include <strings.h>
++#endif
++
++#if !defined(_WIN32)
++# include <unistd.h>
++# include <sys/types.h>
++# include <sys/wait.h>
++#else
++# include <windows.h>
++# include <io.h>
++# include <fcntl.h>
++# if !defined(STDERR_FILENO)
++# define STDERR_FILENO _fileno(stderr)
++# endif
++#endif
++
++#include "munit.h"
++
++#define MUNIT_STRINGIFY(x) #x
++#define MUNIT_XSTRINGIFY(x) MUNIT_STRINGIFY(x)
++
++#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || \
++ defined(__IBMCPP__)
++# define MUNIT_THREAD_LOCAL __thread
++#elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) || \
++ defined(_Thread_local)
++# define MUNIT_THREAD_LOCAL _Thread_local
++#elif defined(_WIN32)
++# define MUNIT_THREAD_LOCAL __declspec(thread)
++#endif
++
++/* MSVC 12.0 will emit a warning at /W4 for code like 'do { ... }
++ * while (0)', or 'do { ... } while (1)'. I'm pretty sure nobody
++ * at Microsoft compiles with /W4. */
++#if defined(_MSC_VER) && (_MSC_VER <= 1800)
++# pragma warning(disable : 4127)
++#endif
++
++#if defined(_WIN32) || defined(__EMSCRIPTEN__)
++# define MUNIT_NO_FORK
++#endif
++
++#if defined(__EMSCRIPTEN__)
++# define MUNIT_NO_BUFFER
++#endif
++
++/*** Logging ***/
++
++static MunitLogLevel munit_log_level_visible = MUNIT_LOG_INFO;
++static MunitLogLevel munit_log_level_fatal = MUNIT_LOG_ERROR;
++
++#if defined(MUNIT_THREAD_LOCAL)
++static MUNIT_THREAD_LOCAL munit_bool munit_error_jmp_buf_valid = 0;
++static MUNIT_THREAD_LOCAL jmp_buf munit_error_jmp_buf;
++#endif
++
++/* At certain warning levels, mingw will trigger warnings about
++ * suggesting the format attribute, which we've explicity *not* set
++ * because it will then choke on our attempts to use the MS-specific
++ * I64 modifier for size_t (which we have to use since MSVC doesn't
++ * support the C99 z modifier). */
++
++#if defined(__MINGW32__) || defined(__MINGW64__)
++# pragma GCC diagnostic push
++# pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
++#endif
++
++MUNIT_PRINTF(5, 0)
++static void munit_logf_exv(MunitLogLevel level, FILE *fp, const char *filename,
++ int line, const char *format, va_list ap) {
++ if (level < munit_log_level_visible)
++ return;
++
++ switch (level) {
++ case MUNIT_LOG_DEBUG:
++ fputs("Debug", fp);
++ break;
++ case MUNIT_LOG_INFO:
++ fputs("Info", fp);
++ break;
++ case MUNIT_LOG_WARNING:
++ fputs("Warning", fp);
++ break;
++ case MUNIT_LOG_ERROR:
++ fputs("Error", fp);
++ break;
++ default:
++ munit_logf_ex(MUNIT_LOG_ERROR, filename, line, "Invalid log level (%d)",
++ level);
++ return;
++ }
++
++ fputs(": ", fp);
++ if (filename != NULL)
++ fprintf(fp, "%s:%d: ", filename, line);
++ vfprintf(fp, format, ap);
++ fputc('\n', fp);
++}
++
++MUNIT_PRINTF(3, 4)
++static void munit_logf_internal(MunitLogLevel level, FILE *fp,
++ const char *format, ...) {
++ va_list ap;
++
++ va_start(ap, format);
++ munit_logf_exv(level, fp, NULL, 0, format, ap);
++ va_end(ap);
++}
++
++static void munit_log_internal(MunitLogLevel level, FILE *fp,
++ const char *message) {
++ munit_logf_internal(level, fp, "%s", message);
++}
++
++void munit_logf_ex(MunitLogLevel level, const char *filename, int line,
++ const char *format, ...) {
++ va_list ap;
++
++ va_start(ap, format);
++ munit_logf_exv(level, stderr, filename, line, format, ap);
++ va_end(ap);
++
++ if (level >= munit_log_level_fatal) {
++#if defined(MUNIT_THREAD_LOCAL)
++ if (munit_error_jmp_buf_valid)
++ longjmp(munit_error_jmp_buf, 1);
++#endif
++ abort();
++ }
++}
++
++void munit_errorf_ex(const char *filename, int line, const char *format, ...) {
++ va_list ap;
++
++ va_start(ap, format);
++ munit_logf_exv(MUNIT_LOG_ERROR, stderr, filename, line, format, ap);
++ va_end(ap);
++
++#if defined(MUNIT_THREAD_LOCAL)
++ if (munit_error_jmp_buf_valid)
++ longjmp(munit_error_jmp_buf, 1);
++#endif
++ abort();
++}
++
++#if defined(__MINGW32__) || defined(__MINGW64__)
++# pragma GCC diagnostic pop
++#endif
++
++#if !defined(MUNIT_STRERROR_LEN)
++# define MUNIT_STRERROR_LEN 80
++#endif
++
++static void munit_log_errno(MunitLogLevel level, FILE *fp, const char *msg) {
++#if defined(MUNIT_NO_STRERROR_R) || \
++ (defined(__MINGW32__) && !defined(MINGW_HAS_SECURE_API))
++ munit_logf_internal(level, fp, "%s: %s (%d)", msg, strerror(errno), errno);
++#else
++ char munit_error_str[MUNIT_STRERROR_LEN];
++ munit_error_str[0] = '\0';
++
++# if !defined(_WIN32)
++ strerror_r(errno, munit_error_str, MUNIT_STRERROR_LEN);
++# else
++ strerror_s(munit_error_str, MUNIT_STRERROR_LEN, errno);
++# endif
++
++ munit_logf_internal(level, fp, "%s: %s (%d)", msg, munit_error_str, errno);
++#endif
++}
++
++/*** Memory allocation ***/
++
++void *munit_malloc_ex(const char *filename, int line, size_t size) {
++ void *ptr;
++
++ if (size == 0)
++ return NULL;
++
++ ptr = calloc(1, size);
++ if (MUNIT_UNLIKELY(ptr == NULL)) {
++ munit_logf_ex(MUNIT_LOG_ERROR, filename, line,
++ "Failed to allocate %" MUNIT_SIZE_MODIFIER "u bytes.", size);
++ }
++
++ return ptr;
++}
++
++/*** Timer code ***/
++
++#if defined(MUNIT_ENABLE_TIMING)
++
++# define psnip_uint64_t munit_uint64_t
++# define psnip_uint32_t munit_uint32_t
++
++/* Code copied from portable-snippets
++ * <https://github.com/nemequ/portable-snippets/>. If you need to
++ * change something, please do it there so we can keep the code in
++ * sync. */
++
++/* Clocks (v1)
++ * Portable Snippets - https://gitub.com/nemequ/portable-snippets
++ * Created by Evan Nemerson <evan@nemerson.com>
++ *
++ * To the extent possible under law, the authors have waived all
++ * copyright and related or neighboring rights to this code. For
++ * details, see the Creative Commons Zero 1.0 Universal license at
++ * https://creativecommons.org/publicdomain/zero/1.0/
++ */
++
++# if !defined(PSNIP_CLOCK_H)
++# define PSNIP_CLOCK_H
++
++# if !defined(psnip_uint64_t)
++# include "../exact-int/exact-int.h"
++# endif
++
++# if !defined(PSNIP_CLOCK_STATIC_INLINE)
++# if defined(__GNUC__)
++# define PSNIP_CLOCK__COMPILER_ATTRIBUTES __attribute__((__unused__))
++# else
++# define PSNIP_CLOCK__COMPILER_ATTRIBUTES
++# endif
++
++# define PSNIP_CLOCK__FUNCTION PSNIP_CLOCK__COMPILER_ATTRIBUTES static
++# endif
++
++enum PsnipClockType {
++ /* This clock provides the current time, in units since 1970-01-01
++ * 00:00:00 UTC not including leap seconds. In other words, UNIX
++ * time. Keep in mind that this clock doesn't account for leap
++ * seconds, and can go backwards (think NTP adjustments). */
++ PSNIP_CLOCK_TYPE_WALL = 1,
++ /* The CPU time is a clock which increases only when the current
++ * process is active (i.e., it doesn't increment while blocking on
++ * I/O). */
++ PSNIP_CLOCK_TYPE_CPU = 2,
++ /* Monotonic time is always running (unlike CPU time), but it only
++ ever moves forward unless you reboot the system. Things like NTP
++ adjustments have no effect on this clock. */
++ PSNIP_CLOCK_TYPE_MONOTONIC = 3
++};
++
++struct PsnipClockTimespec {
++ psnip_uint64_t seconds;
++ psnip_uint64_t nanoseconds;
++};
++
++/* Methods we support: */
++
++# define PSNIP_CLOCK_METHOD_CLOCK_GETTIME 1
++# define PSNIP_CLOCK_METHOD_TIME 2
++# define PSNIP_CLOCK_METHOD_GETTIMEOFDAY 3
++# define PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER 4
++# define PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME 5
++# define PSNIP_CLOCK_METHOD_CLOCK 6
++# define PSNIP_CLOCK_METHOD_GETPROCESSTIMES 7
++# define PSNIP_CLOCK_METHOD_GETRUSAGE 8
++# define PSNIP_CLOCK_METHOD_GETSYSTEMTIMEPRECISEASFILETIME 9
++# define PSNIP_CLOCK_METHOD_GETTICKCOUNT64 10
++
++# include <assert.h>
++
++# if defined(HEDLEY_UNREACHABLE)
++# define PSNIP_CLOCK_UNREACHABLE() HEDLEY_UNREACHABLE()
++# else
++# define PSNIP_CLOCK_UNREACHABLE() assert(0)
++# endif
++
++/* Choose an implementation */
++
++/* #undef PSNIP_CLOCK_WALL_METHOD */
++/* #undef PSNIP_CLOCK_CPU_METHOD */
++/* #undef PSNIP_CLOCK_MONOTONIC_METHOD */
++
++/* We want to be able to detect the libc implementation, so we include
++ <limits.h> (<features.h> isn't available everywhere). */
++
++# if defined(__unix__) || defined(__unix) || defined(__linux__)
++# include <limits.h>
++# include <unistd.h>
++# endif
++
++# if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
++/* These are known to work without librt. If you know of others
++ * please let us know so we can add them. */
++# if (defined(__GLIBC__) && \
++ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \
++ (defined(__FreeBSD__))
++# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME
++# elif !defined(PSNIP_CLOCK_NO_LIBRT)
++# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME
++# endif
++# endif
++
++# if defined(_WIN32)
++# if !defined(PSNIP_CLOCK_CPU_METHOD)
++# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_GETPROCESSTIMES
++# endif
++# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
++# define PSNIP_CLOCK_MONOTONIC_METHOD \
++ PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
++# endif
++# endif
++
++# if defined(__MACH__) && !defined(__gnu_hurd__)
++# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
++# define PSNIP_CLOCK_MONOTONIC_METHOD \
++ PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
++# endif
++# endif
++
++# if defined(PSNIP_CLOCK_HAVE_CLOCK_GETTIME)
++# include <time.h>
++# if !defined(PSNIP_CLOCK_WALL_METHOD)
++# if defined(CLOCK_REALTIME_PRECISE)
++# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME_PRECISE
++# elif !defined(__sun)
++# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME
++# endif
++# endif
++# if !defined(PSNIP_CLOCK_CPU_METHOD)
++# if defined(_POSIX_CPUTIME) || defined(CLOCK_PROCESS_CPUTIME_ID)
++# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_PROCESS_CPUTIME_ID
++# elif defined(CLOCK_VIRTUAL)
++# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_VIRTUAL
++# endif
++# endif
++# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
++# if defined(CLOCK_MONOTONIC_RAW)
++# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC
++# elif defined(CLOCK_MONOTONIC_PRECISE)
++# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC_PRECISE
++# elif defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC)
++# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC
++# endif
++# endif
++# endif
++
++# if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L)
++# if !defined(PSNIP_CLOCK_WALL_METHOD)
++# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_GETTIMEOFDAY
++# endif
++# endif
++
++# if !defined(PSNIP_CLOCK_WALL_METHOD)
++# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_TIME
++# endif
++
++# if !defined(PSNIP_CLOCK_CPU_METHOD)
++# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK
++# endif
++
++/* Primarily here for testing. */
++# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ defined(PSNIP_CLOCK_REQUIRE_MONOTONIC)
++# error No monotonic clock found.
++# endif
++
++/* Implementations */
++
++# if (defined(PSNIP_CLOCK_CPU_METHOD) && \
++ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
++ (defined(PSNIP_CLOCK_WALL_METHOD) && \
++ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
++ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ (PSNIP_CLOCK_MONOTONIC_METHOD == \
++ PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
++ (defined(PSNIP_CLOCK_CPU_METHOD) && \
++ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
++ (defined(PSNIP_CLOCK_WALL_METHOD) && \
++ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
++ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
++ (defined(PSNIP_CLOCK_CPU_METHOD) && \
++ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \
++ (defined(PSNIP_CLOCK_WALL_METHOD) && \
++ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \
++ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME))
++# include <time.h>
++# endif
++
++# if (defined(PSNIP_CLOCK_CPU_METHOD) && \
++ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \
++ (defined(PSNIP_CLOCK_WALL_METHOD) && \
++ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \
++ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY))
++# include <sys/time.h>
++# endif
++
++# if (defined(PSNIP_CLOCK_CPU_METHOD) && \
++ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
++ (defined(PSNIP_CLOCK_WALL_METHOD) && \
++ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
++ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ (PSNIP_CLOCK_MONOTONIC_METHOD == \
++ PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
++ (defined(PSNIP_CLOCK_CPU_METHOD) && \
++ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \
++ (defined(PSNIP_CLOCK_WALL_METHOD) && \
++ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \
++ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64))
++# include <windows.h>
++# endif
++
++# if (defined(PSNIP_CLOCK_CPU_METHOD) && \
++ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \
++ (defined(PSNIP_CLOCK_WALL_METHOD) && \
++ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \
++ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE))
++# include <sys/time.h>
++# include <sys/resource.h>
++# endif
++
++# if (defined(PSNIP_CLOCK_CPU_METHOD) && \
++ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \
++ (defined(PSNIP_CLOCK_WALL_METHOD) && \
++ (PSNIP_CLOCK_WALL_METHOD == \
++ PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \
++ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ (PSNIP_CLOCK_MONOTONIC_METHOD == \
++ PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME))
++# include <CoreServices/CoreServices.h>
++# include <mach/mach.h>
++# include <mach/mach_time.h>
++# endif
++
++/*** Implementations ***/
++
++# define PSNIP_CLOCK_NSEC_PER_SEC ((psnip_uint32_t)(1000000000ULL))
++
++# if (defined(PSNIP_CLOCK_CPU_METHOD) && \
++ (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
++ (defined(PSNIP_CLOCK_WALL_METHOD) && \
++ (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
++ (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME))
++PSNIP_CLOCK__FUNCTION psnip_uint32_t
++psnip_clock__clock_getres(clockid_t clk_id) {
++ struct timespec res;
++ int r;
++
++ r = clock_getres(clk_id, &res);
++ if (r != 0)
++ return 0;
++
++ return (psnip_uint32_t)(PSNIP_CLOCK_NSEC_PER_SEC /
++ (psnip_uint64_t)res.tv_nsec);
++}
++
++PSNIP_CLOCK__FUNCTION int
++psnip_clock__clock_gettime(clockid_t clk_id, struct PsnipClockTimespec *res) {
++ struct timespec ts;
++
++ if (clock_gettime(clk_id, &ts) != 0)
++ return -10;
++
++ res->seconds = (psnip_uint64_t)(ts.tv_sec);
++ res->nanoseconds = (psnip_uint64_t)(ts.tv_nsec);
++
++ return 0;
++}
++# endif
++
++PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_wall_get_precision(void) {
++# if !defined(PSNIP_CLOCK_WALL_METHOD)
++ return 0;
++# elif defined(PSNIP_CLOCK_WALL_METHOD) && \
++ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++ return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL);
++# elif defined(PSNIP_CLOCK_WALL_METHOD) && \
++ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
++ return 1000000;
++# elif defined(PSNIP_CLOCK_WALL_METHOD) && \
++ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
++ return 1;
++# else
++ return 0;
++# endif
++}
++
++PSNIP_CLOCK__FUNCTION int
++psnip_clock_wall_get_time(struct PsnipClockTimespec *res) {
++ (void)res;
++
++# if !defined(PSNIP_CLOCK_WALL_METHOD)
++ return -2;
++# elif defined(PSNIP_CLOCK_WALL_METHOD) && \
++ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++ return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res);
++# elif defined(PSNIP_CLOCK_WALL_METHOD) && \
++ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
++ res->seconds = time(NULL);
++ res->nanoseconds = 0;
++# elif defined(PSNIP_CLOCK_WALL_METHOD) && \
++ PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
++ struct timeval tv;
++
++ if (gettimeofday(&tv, NULL) != 0)
++ return -6;
++
++ res->seconds = (psnip_uint64_t)tv.tv_sec;
++ res->nanoseconds = (psnip_uint64_t)tv.tv_usec * 1000;
++# else
++ return -2;
++# endif
++
++ return 0;
++}
++
++PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_cpu_get_precision(void) {
++# if !defined(PSNIP_CLOCK_CPU_METHOD)
++ return 0;
++# elif defined(PSNIP_CLOCK_CPU_METHOD) && \
++ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++ return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU);
++# elif defined(PSNIP_CLOCK_CPU_METHOD) && \
++ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
++ return CLOCKS_PER_SEC;
++# elif defined(PSNIP_CLOCK_CPU_METHOD) && \
++ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
++ return PSNIP_CLOCK_NSEC_PER_SEC / 100;
++# else
++ return 0;
++# endif
++}
++
++PSNIP_CLOCK__FUNCTION int
++psnip_clock_cpu_get_time(struct PsnipClockTimespec *res) {
++# if !defined(PSNIP_CLOCK_CPU_METHOD)
++ (void)res;
++ return -2;
++# elif defined(PSNIP_CLOCK_CPU_METHOD) && \
++ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++ return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res);
++# elif defined(PSNIP_CLOCK_CPU_METHOD) && \
++ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
++ clock_t t = clock();
++ if (t == ((clock_t)-1))
++ return -5;
++ res->seconds = t / CLOCKS_PER_SEC;
++ res->nanoseconds =
++ (t % CLOCKS_PER_SEC) * (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC);
++# elif defined(PSNIP_CLOCK_CPU_METHOD) && \
++ PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
++ FILETIME CreationTime, ExitTime, KernelTime, UserTime;
++ LARGE_INTEGER date, adjust;
++
++ if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime,
++ &KernelTime, &UserTime))
++ return -7;
++
++ /* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */
++ date.HighPart = (LONG)UserTime.dwHighDateTime;
++ date.LowPart = UserTime.dwLowDateTime;
++ adjust.QuadPart = 11644473600000 * 10000;
++ date.QuadPart -= adjust.QuadPart;
++
++ res->seconds = (psnip_uint64_t)(date.QuadPart / 10000000);
++ res->nanoseconds = (psnip_uint64_t)(date.QuadPart % 10000000) *
++ (PSNIP_CLOCK_NSEC_PER_SEC / 100);
++# elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE
++ struct rusage usage;
++ if (getrusage(RUSAGE_SELF, &usage) != 0)
++ return -8;
++
++ res->seconds = usage.ru_utime.tv_sec;
++ res->nanoseconds = tv.tv_usec * 1000;
++# else
++ (void)res;
++ return -2;
++# endif
++
++ return 0;
++}
++
++PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_monotonic_get_precision(void) {
++# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
++ return 0;
++# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++ return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC);
++# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
++ static mach_timebase_info_data_t tbi = {
++ 0,
++ };
++ if (tbi.denom == 0)
++ mach_timebase_info(&tbi);
++ return (psnip_uint32_t)(tbi.numer / tbi.denom);
++# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
++ return 1000;
++# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ PSNIP_CLOCK_MONOTONIC_METHOD == \
++ PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
++ LARGE_INTEGER Frequency;
++ QueryPerformanceFrequency(&Frequency);
++ return (psnip_uint32_t)((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC)
++ ? PSNIP_CLOCK_NSEC_PER_SEC
++ : Frequency.QuadPart);
++# else
++ return 0;
++# endif
++}
++
++PSNIP_CLOCK__FUNCTION int
++psnip_clock_monotonic_get_time(struct PsnipClockTimespec *res) {
++# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
++ (void)res;
++ return -2;
++# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
++ return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res);
++# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
++ psnip_uint64_t nsec = mach_absolute_time();
++ static mach_timebase_info_data_t tbi = {
++ 0,
++ };
++ if (tbi.denom == 0)
++ mach_timebase_info(&tbi);
++ nsec *= ((psnip_uint64_t)tbi.numer) / ((psnip_uint64_t)tbi.denom);
++ res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC;
++ res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC;
++# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ PSNIP_CLOCK_MONOTONIC_METHOD == \
++ PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
++ LARGE_INTEGER t, f;
++ if (QueryPerformanceCounter(&t) == 0)
++ return -12;
++
++ QueryPerformanceFrequency(&f);
++ res->seconds = (psnip_uint64_t)(t.QuadPart / f.QuadPart);
++ res->nanoseconds = (psnip_uint64_t)(t.QuadPart % f.QuadPart);
++ if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC)
++ res->nanoseconds /= (psnip_uint64_t)f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC;
++ else
++ res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / (psnip_uint64_t)f.QuadPart;
++# elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \
++ PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
++ const ULONGLONG msec = GetTickCount64();
++ res->seconds = msec / 1000;
++ res->nanoseconds = sec % 1000;
++# else
++ return -2;
++# endif
++
++ return 0;
++}
++
++/* Returns the number of ticks per second for the specified clock.
++ * For example, a clock with millisecond precision would return 1000,
++ * and a clock with 1 second (such as the time() function) would
++ * return 1.
++ *
++ * If the requested clock isn't available, it will return 0.
++ * Hopefully this will be rare, but if it happens to you please let us
++ * know so we can work on finding a way to support your system.
++ *
++ * Note that different clocks on the same system often have a
++ * different precisions.
++ */
++PSNIP_CLOCK__FUNCTION psnip_uint32_t
++psnip_clock_get_precision(enum PsnipClockType clock_type) {
++ switch (clock_type) {
++ case PSNIP_CLOCK_TYPE_MONOTONIC:
++ return psnip_clock_monotonic_get_precision();
++ case PSNIP_CLOCK_TYPE_CPU:
++ return psnip_clock_cpu_get_precision();
++ case PSNIP_CLOCK_TYPE_WALL:
++ return psnip_clock_wall_get_precision();
++ }
++
++ PSNIP_CLOCK_UNREACHABLE();
++ return 0;
++}
++
++/* Set the provided timespec to the requested time. Returns 0 on
++ * success, or a negative value on failure. */
++PSNIP_CLOCK__FUNCTION int psnip_clock_get_time(enum PsnipClockType clock_type,
++ struct PsnipClockTimespec *res) {
++ assert(res != NULL);
++
++ switch (clock_type) {
++ case PSNIP_CLOCK_TYPE_MONOTONIC:
++ return psnip_clock_monotonic_get_time(res);
++ case PSNIP_CLOCK_TYPE_CPU:
++ return psnip_clock_cpu_get_time(res);
++ case PSNIP_CLOCK_TYPE_WALL:
++ return psnip_clock_wall_get_time(res);
++ }
++
++ return -1;
++}
++
++# endif /* !defined(PSNIP_CLOCK_H) */
++
++static psnip_uint64_t munit_clock_get_elapsed(struct PsnipClockTimespec *start,
++ struct PsnipClockTimespec *end) {
++ psnip_uint64_t r = (end->seconds - start->seconds) * PSNIP_CLOCK_NSEC_PER_SEC;
++ if (end->nanoseconds < start->nanoseconds) {
++ return r - (start->nanoseconds - end->nanoseconds);
++ }
++
++ return r + (end->nanoseconds - start->nanoseconds);
++}
++
++#else
++# include <time.h>
++#endif /* defined(MUNIT_ENABLE_TIMING) */
++
++/*** PRNG stuff ***/
++
++/* This is (unless I screwed up, which is entirely possible) the
++ * version of PCG with 32-bit state. It was chosen because it has a
++ * small enough state that we should reliably be able to use CAS
++ * instead of requiring a lock for thread-safety.
++ *
++ * If I did screw up, I probably will not bother changing it unless
++ * there is a significant bias. It's really not important this be
++ * particularly strong, as long as it is fairly random it's much more
++ * important that it be reproducible, so bug reports have a better
++ * chance of being reproducible. */
++
++#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \
++ !defined(__STDC_NO_ATOMICS__) && !defined(__EMSCRIPTEN__) && \
++ (!defined(__GNUC_MINOR__) || (__GNUC__ > 4) || \
++ (__GNUC__ == 4 && __GNUC_MINOR__ > 8))
++# define HAVE_STDATOMIC
++#elif defined(__clang__)
++# if __has_extension(c_atomic)
++# define HAVE_CLANG_ATOMICS
++# endif
++#endif
++
++/* Workaround for http://llvm.org/bugs/show_bug.cgi?id=26911 */
++#if defined(__clang__) && defined(_WIN32)
++# undef HAVE_STDATOMIC
++# if defined(__c2__)
++# undef HAVE_CLANG_ATOMICS
++# endif
++#endif
++
++#if defined(_OPENMP)
++# define ATOMIC_UINT32_T uint32_t
++# define ATOMIC_UINT32_INIT(x) (x)
++#elif defined(HAVE_STDATOMIC)
++# include <stdatomic.h>
++# define ATOMIC_UINT32_T _Atomic uint32_t
++# define ATOMIC_UINT32_INIT(x) ATOMIC_VAR_INIT(x)
++#elif defined(HAVE_CLANG_ATOMICS)
++# define ATOMIC_UINT32_T _Atomic uint32_t
++# define ATOMIC_UINT32_INIT(x) (x)
++#elif defined(_WIN32)
++# define ATOMIC_UINT32_T volatile LONG
++# define ATOMIC_UINT32_INIT(x) (x)
++#else
++# define ATOMIC_UINT32_T volatile uint32_t
++# define ATOMIC_UINT32_INIT(x) (x)
++#endif
++
++static ATOMIC_UINT32_T munit_rand_state = ATOMIC_UINT32_INIT(42);
++
++#if defined(_OPENMP)
++static inline void munit_atomic_store(ATOMIC_UINT32_T *dest,
++ ATOMIC_UINT32_T value) {
++# pragma omp critical(munit_atomics)
++ *dest = value;
++}
++
++static inline uint32_t munit_atomic_load(ATOMIC_UINT32_T *src) {
++ int ret;
++# pragma omp critical(munit_atomics)
++ ret = *src;
++ return ret;
++}
++
++static inline uint32_t munit_atomic_cas(ATOMIC_UINT32_T *dest,
++ ATOMIC_UINT32_T *expected,
++ ATOMIC_UINT32_T desired) {
++ munit_bool ret;
++
++# pragma omp critical(munit_atomics)
++ {
++ if (*dest == *expected) {
++ *dest = desired;
++ ret = 1;
++ } else {
++ ret = 0;
++ }
++ }
++
++ return ret;
++}
++#elif defined(HAVE_STDATOMIC)
++# define munit_atomic_store(dest, value) atomic_store(dest, value)
++# define munit_atomic_load(src) atomic_load(src)
++# define munit_atomic_cas(dest, expected, value) \
++ atomic_compare_exchange_weak(dest, expected, value)
++#elif defined(HAVE_CLANG_ATOMICS)
++# define munit_atomic_store(dest, value) \
++ __c11_atomic_store(dest, value, __ATOMIC_SEQ_CST)
++# define munit_atomic_load(src) __c11_atomic_load(src, __ATOMIC_SEQ_CST)
++# define munit_atomic_cas(dest, expected, value) \
++ __c11_atomic_compare_exchange_weak(dest, expected, value, \
++ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
++#elif defined(__GNUC__) && (__GNUC__ > 4) || \
++ (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
++# define munit_atomic_store(dest, value) \
++ __atomic_store_n(dest, value, __ATOMIC_SEQ_CST)
++# define munit_atomic_load(src) __atomic_load_n(src, __ATOMIC_SEQ_CST)
++# define munit_atomic_cas(dest, expected, value) \
++ __atomic_compare_exchange_n(dest, expected, value, 1, __ATOMIC_SEQ_CST, \
++ __ATOMIC_SEQ_CST)
++#elif defined(__GNUC__) && (__GNUC__ >= 4)
++# define munit_atomic_store(dest, value) \
++ do { \
++ *(dest) = (value); \
++ } while (0)
++# define munit_atomic_load(src) (*(src))
++# define munit_atomic_cas(dest, expected, value) \
++ __sync_bool_compare_and_swap(dest, *expected, value)
++#elif defined(_WIN32) /* Untested */
++# define munit_atomic_store(dest, value) \
++ do { \
++ *(dest) = (value); \
++ } while (0)
++# define munit_atomic_load(src) (*(src))
++# define munit_atomic_cas(dest, expected, value) \
++ InterlockedCompareExchange((dest), (value), *(expected))
++#else
++# warning No atomic implementation, PRNG will not be thread-safe
++# define munit_atomic_store(dest, value) \
++ do { \
++ *(dest) = (value); \
++ } while (0)
++# define munit_atomic_load(src) (*(src))
++static inline munit_bool munit_atomic_cas(ATOMIC_UINT32_T *dest,
++ ATOMIC_UINT32_T *expected,
++ ATOMIC_UINT32_T desired) {
++ if (*dest == *expected) {
++ *dest = desired;
++ return 1;
++ } else {
++ return 0;
++ }
++}
++#endif
++
++#define MUNIT_PRNG_MULTIPLIER (747796405U)
++#define MUNIT_PRNG_INCREMENT (1729U)
++
++static munit_uint32_t munit_rand_next_state(munit_uint32_t state) {
++ return state * MUNIT_PRNG_MULTIPLIER + MUNIT_PRNG_INCREMENT;
++}
++
++static munit_uint32_t munit_rand_from_state(munit_uint32_t state) {
++ munit_uint32_t res = ((state >> ((state >> 28) + 4)) ^ state) * (277803737U);
++ res ^= res >> 22;
++ return res;
++}
++
++void munit_rand_seed(munit_uint32_t seed) {
++ munit_uint32_t state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT);
++ munit_atomic_store(&munit_rand_state, state);
++}
++
++static munit_uint32_t munit_rand_generate_seed(void) {
++ munit_uint32_t seed, state;
++#if defined(MUNIT_ENABLE_TIMING)
++ struct PsnipClockTimespec wc = {
++ 0,
++ };
++
++ psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wc);
++ seed = (munit_uint32_t)wc.nanoseconds;
++#else
++ seed = (munit_uint32_t)time(NULL);
++#endif
++
++ state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT);
++ return munit_rand_from_state(state);
++}
++
++static munit_uint32_t munit_rand_state_uint32(munit_uint32_t *state) {
++ const munit_uint32_t old = *state;
++ *state = munit_rand_next_state(old);
++ return munit_rand_from_state(old);
++}
++
++munit_uint32_t munit_rand_uint32(void) {
++ munit_uint32_t old, state;
++
++ do {
++ old = munit_atomic_load(&munit_rand_state);
++ state = munit_rand_next_state(old);
++ } while (!munit_atomic_cas(&munit_rand_state, &old, state));
++
++ return munit_rand_from_state(old);
++}
++
++static void munit_rand_state_memory(munit_uint32_t *state, size_t size,
++ munit_uint8_t *data) {
++ size_t members_remaining = size / sizeof(munit_uint32_t);
++ size_t bytes_remaining = size % sizeof(munit_uint32_t);
++ munit_uint8_t *b = data;
++ munit_uint32_t rv;
++ while (members_remaining-- > 0) {
++ rv = munit_rand_state_uint32(state);
++ memcpy(b, &rv, sizeof(munit_uint32_t));
++ b += sizeof(munit_uint32_t);
++ }
++ if (bytes_remaining != 0) {
++ rv = munit_rand_state_uint32(state);
++ memcpy(b, &rv, bytes_remaining);
++ }
++}
++
++void munit_rand_memory(size_t size, munit_uint8_t *data) {
++ munit_uint32_t old, state;
++
++ do {
++ state = old = munit_atomic_load(&munit_rand_state);
++ munit_rand_state_memory(&state, size, data);
++ } while (!munit_atomic_cas(&munit_rand_state, &old, state));
++}
++
++static munit_uint32_t munit_rand_state_at_most(munit_uint32_t *state,
++ munit_uint32_t salt,
++ munit_uint32_t max) {
++ /* We want (UINT32_MAX + 1) % max, which in unsigned arithmetic is the same
++ * as (UINT32_MAX + 1 - max) % max = -max % max. We compute -max using not
++ * to avoid compiler warnings.
++ */
++ const munit_uint32_t min = (~max + 1U) % max;
++ munit_uint32_t x;
++
++ if (max == (~((munit_uint32_t)0U)))
++ return munit_rand_state_uint32(state) ^ salt;
++
++ max++;
++
++ do {
++ x = munit_rand_state_uint32(state) ^ salt;
++ } while (x < min);
++
++ return x % max;
++}
++
++static munit_uint32_t munit_rand_at_most(munit_uint32_t salt,
++ munit_uint32_t max) {
++ munit_uint32_t old, state;
++ munit_uint32_t retval;
++
++ do {
++ state = old = munit_atomic_load(&munit_rand_state);
++ retval = munit_rand_state_at_most(&state, salt, max);
++ } while (!munit_atomic_cas(&munit_rand_state, &old, state));
++
++ return retval;
++}
++
++int munit_rand_int_range(int min, int max) {
++ munit_uint64_t range = (munit_uint64_t)max - (munit_uint64_t)min;
++
++ if (min > max)
++ return munit_rand_int_range(max, min);
++
++ if (range > (~((munit_uint32_t)0U)))
++ range = (~((munit_uint32_t)0U));
++
++ return min + (int)munit_rand_at_most(0, (munit_uint32_t)range);
++}
++
++double munit_rand_double(void) {
++ munit_uint32_t old, state;
++ double retval = 0.0;
++
++ do {
++ state = old = munit_atomic_load(&munit_rand_state);
++
++ /* See http://mumble.net/~campbell/tmp/random_real.c for how to do
++ * this right. Patches welcome if you feel that this is too
++ * biased. */
++ retval = munit_rand_state_uint32(&state) / ((~((munit_uint32_t)0U)) + 1.0);
++ } while (!munit_atomic_cas(&munit_rand_state, &old, state));
++
++ return retval;
++}
++
++/*** Test suite handling ***/
++
++typedef struct {
++ unsigned int successful;
++ unsigned int skipped;
++ unsigned int failed;
++ unsigned int errored;
++#if defined(MUNIT_ENABLE_TIMING)
++ munit_uint64_t cpu_clock;
++ munit_uint64_t wall_clock;
++#endif
++} MunitReport;
++
++typedef struct {
++ const char *prefix;
++ const MunitSuite *suite;
++ const char **tests;
++ munit_uint32_t seed;
++ unsigned int iterations;
++ MunitParameter *parameters;
++ munit_bool single_parameter_mode;
++ void *user_data;
++ MunitReport report;
++ munit_bool colorize;
++ munit_bool fork;
++ munit_bool show_stderr;
++ munit_bool fatal_failures;
++} MunitTestRunner;
++
++const char *munit_parameters_get(const MunitParameter params[],
++ const char *key) {
++ const MunitParameter *param;
++
++ for (param = params; param != NULL && param->name != NULL; param++)
++ if (strcmp(param->name, key) == 0)
++ return param->value;
++ return NULL;
++}
++
++#if defined(MUNIT_ENABLE_TIMING)
++static void munit_print_time(FILE *fp, munit_uint64_t nanoseconds) {
++ fprintf(fp, "%" MUNIT_TEST_TIME_FORMAT,
++ ((double)nanoseconds) / ((double)PSNIP_CLOCK_NSEC_PER_SEC));
++}
++#endif
++
++/* Add a paramter to an array of parameters. */
++static MunitResult munit_parameters_add(size_t *params_size,
++ MunitParameter **params, char *name,
++ char *value) {
++ *params = realloc(*params, sizeof(MunitParameter) * (*params_size + 2));
++ if (*params == NULL)
++ return MUNIT_ERROR;
++
++ (*params)[*params_size].name = name;
++ (*params)[*params_size].value = value;
++ (*params_size)++;
++ (*params)[*params_size].name = NULL;
++ (*params)[*params_size].value = NULL;
++
++ return MUNIT_OK;
++}
++
++/* Concatenate two strings, but just return one of the components
++ * unaltered if the other is NULL or "". */
++static char *munit_maybe_concat(size_t *len, char *prefix, char *suffix) {
++ char *res;
++ size_t res_l;
++ const size_t prefix_l = prefix != NULL ? strlen(prefix) : 0;
++ const size_t suffix_l = suffix != NULL ? strlen(suffix) : 0;
++ if (prefix_l == 0 && suffix_l == 0) {
++ res = NULL;
++ res_l = 0;
++ } else if (prefix_l == 0 && suffix_l != 0) {
++ res = suffix;
++ res_l = suffix_l;
++ } else if (prefix_l != 0 && suffix_l == 0) {
++ res = prefix;
++ res_l = prefix_l;
++ } else {
++ res_l = prefix_l + suffix_l;
++ res = malloc(res_l + 1);
++ memcpy(res, prefix, prefix_l);
++ memcpy(res + prefix_l, suffix, suffix_l);
++ res[res_l] = 0;
++ }
++
++ if (len != NULL)
++ *len = res_l;
++
++ return res;
++}
++
++/* Possbily free a string returned by munit_maybe_concat. */
++static void munit_maybe_free_concat(char *s, const char *prefix,
++ const char *suffix) {
++ if (prefix != s && suffix != s)
++ free(s);
++}
++
++/* Cheap string hash function, just used to salt the PRNG. */
++static munit_uint32_t munit_str_hash(const char *name) {
++ const char *p;
++ munit_uint32_t h = 5381U;
++
++ for (p = name; *p != '\0'; p++)
++ h = (munit_uint32_t)(h << 5) + h + (munit_uint32_t)*p;
++
++ return h;
++}
++
++static void munit_splice(int from, int to) {
++ munit_uint8_t buf[1024];
++#if !defined(_WIN32)
++ ssize_t len;
++ ssize_t bytes_written;
++ ssize_t write_res;
++#else
++ int len;
++ int bytes_written;
++ int write_res;
++#endif
++ do {
++ len = read(from, buf, sizeof(buf));
++ if (len > 0) {
++ bytes_written = 0;
++ do {
++ write_res = write(to, buf + bytes_written,
++#if !defined(_WIN32)
++ (size_t)
++#else
++ (unsigned int)
++#endif
++ (len - bytes_written));
++ if (write_res < 0)
++ break;
++ bytes_written += write_res;
++ } while (bytes_written < len);
++ } else
++ break;
++ } while (1);
++}
++
++/* This is the part that should be handled in the child process */
++static MunitResult munit_test_runner_exec(MunitTestRunner *runner,
++ const MunitTest *test,
++ const MunitParameter params[],
++ MunitReport *report) {
++ unsigned int iterations = runner->iterations;
++ MunitResult result = MUNIT_FAIL;
++#if defined(MUNIT_ENABLE_TIMING)
++ struct PsnipClockTimespec wall_clock_begin =
++ {
++ 0,
++ },
++ wall_clock_end = {
++ 0,
++ };
++ struct PsnipClockTimespec cpu_clock_begin =
++ {
++ 0,
++ },
++ cpu_clock_end = {
++ 0,
++ };
++#endif
++ unsigned int i = 0;
++
++ if ((test->options & MUNIT_TEST_OPTION_SINGLE_ITERATION) ==
++ MUNIT_TEST_OPTION_SINGLE_ITERATION)
++ iterations = 1;
++ else if (iterations == 0)
++ iterations = runner->suite->iterations;
++
++ munit_rand_seed(runner->seed);
++
++ do {
++ void *data = (test->setup == NULL) ? runner->user_data
++ : test->setup(params, runner->user_data);
++
++#if defined(MUNIT_ENABLE_TIMING)
++ psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_begin);
++ psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_begin);
++#endif
++
++ result = test->test(params, data);
++
++#if defined(MUNIT_ENABLE_TIMING)
++ psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_end);
++ psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_end);
++#endif
++
++ if (test->tear_down != NULL)
++ test->tear_down(data);
++
++ if (MUNIT_LIKELY(result == MUNIT_OK)) {
++ report->successful++;
++#if defined(MUNIT_ENABLE_TIMING)
++ report->wall_clock +=
++ munit_clock_get_elapsed(&wall_clock_begin, &wall_clock_end);
++ report->cpu_clock +=
++ munit_clock_get_elapsed(&cpu_clock_begin, &cpu_clock_end);
++#endif
++ } else {
++ switch ((int)result) {
++ case MUNIT_SKIP:
++ report->skipped++;
++ break;
++ case MUNIT_FAIL:
++ report->failed++;
++ break;
++ case MUNIT_ERROR:
++ report->errored++;
++ break;
++ default:
++ break;
++ }
++ break;
++ }
++ } while (++i < iterations);
++
++ return result;
++}
++
++#if defined(MUNIT_EMOTICON)
++# define MUNIT_RESULT_STRING_OK ":)"
++# define MUNIT_RESULT_STRING_SKIP ":|"
++# define MUNIT_RESULT_STRING_FAIL ":("
++# define MUNIT_RESULT_STRING_ERROR ":o"
++# define MUNIT_RESULT_STRING_TODO ":/"
++#else
++# define MUNIT_RESULT_STRING_OK "OK "
++# define MUNIT_RESULT_STRING_SKIP "SKIP "
++# define MUNIT_RESULT_STRING_FAIL "FAIL "
++# define MUNIT_RESULT_STRING_ERROR "ERROR"
++# define MUNIT_RESULT_STRING_TODO "TODO "
++#endif
++
++static void munit_test_runner_print_color(const MunitTestRunner *runner,
++ const char *string, char color) {
++ if (runner->colorize)
++ fprintf(MUNIT_OUTPUT_FILE, "\x1b[3%cm%s\x1b[39m", color, string);
++ else
++ fputs(string, MUNIT_OUTPUT_FILE);
++}
++
++#if !defined(MUNIT_NO_BUFFER)
++static int munit_replace_stderr(FILE *stderr_buf) {
++ if (stderr_buf != NULL) {
++ const int orig_stderr = dup(STDERR_FILENO);
++
++ int errfd = fileno(stderr_buf);
++ if (MUNIT_UNLIKELY(errfd == -1)) {
++ exit(EXIT_FAILURE);
++ }
++
++ dup2(errfd, STDERR_FILENO);
++
++ return orig_stderr;
++ }
++
++ return -1;
++}
++
++static void munit_restore_stderr(int orig_stderr) {
++ if (orig_stderr != -1) {
++ dup2(orig_stderr, STDERR_FILENO);
++ close(orig_stderr);
++ }
++}
++#endif /* !defined(MUNIT_NO_BUFFER) */
++
++/* Run a test with the specified parameters. */
++static void
++munit_test_runner_run_test_with_params(MunitTestRunner *runner,
++ const MunitTest *test,
++ const MunitParameter params[]) {
++ MunitResult result = MUNIT_OK;
++ MunitReport report = {
++ 0,
++ 0,
++ 0,
++ 0,
++#if defined(MUNIT_ENABLE_TIMING)
++ 0,
++ 0
++#endif
++ };
++ unsigned int output_l;
++ munit_bool first;
++ const MunitParameter *param;
++ FILE *stderr_buf;
++#if !defined(MUNIT_NO_FORK)
++ int pipefd[2];
++ pid_t fork_pid;
++ ssize_t bytes_written = 0;
++ ssize_t write_res;
++ ssize_t bytes_read = 0;
++ ssize_t read_res;
++ int status = 0;
++ pid_t changed_pid;
++#endif
++
++ if (params != NULL) {
++ output_l = 2;
++ fputs(" ", MUNIT_OUTPUT_FILE);
++ first = 1;
++ for (param = params; param != NULL && param->name != NULL; param++) {
++ if (!first) {
++ fputs(", ", MUNIT_OUTPUT_FILE);
++ output_l += 2;
++ } else {
++ first = 0;
++ }
++
++ output_l += (unsigned int)fprintf(MUNIT_OUTPUT_FILE, "%s=%s", param->name,
++ param->value);
++ }
++ while (output_l++ < MUNIT_TEST_NAME_LEN) {
++ fputc(' ', MUNIT_OUTPUT_FILE);
++ }
++ }
++
++ fflush(MUNIT_OUTPUT_FILE);
++
++ stderr_buf = NULL;
++#if !defined(_WIN32) || defined(__MINGW32__)
++ stderr_buf = tmpfile();
++#else
++ tmpfile_s(&stderr_buf);
++#endif
++ if (stderr_buf == NULL) {
++ munit_log_errno(MUNIT_LOG_ERROR, stderr,
++ "unable to create buffer for stderr");
++ result = MUNIT_ERROR;
++ goto print_result;
++ }
++
++#if !defined(MUNIT_NO_FORK)
++ if (runner->fork) {
++ pipefd[0] = -1;
++ pipefd[1] = -1;
++ if (pipe(pipefd) != 0) {
++ munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to create pipe");
++ result = MUNIT_ERROR;
++ goto print_result;
++ }
++
++ fork_pid = fork();
++ if (fork_pid == 0) {
++ int orig_stderr;
++
++ close(pipefd[0]);
++
++ orig_stderr = munit_replace_stderr(stderr_buf);
++ munit_test_runner_exec(runner, test, params, &report);
++
++ /* Note that we don't restore stderr. This is so we can buffer
++ * things written to stderr later on (such as by
++ * asan/tsan/ubsan, valgrind, etc.) */
++ close(orig_stderr);
++
++ do {
++ write_res =
++ write(pipefd[1], ((munit_uint8_t *)(&report)) + bytes_written,
++ sizeof(report) - (size_t)bytes_written);
++ if (write_res < 0) {
++ if (stderr_buf != NULL) {
++ munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to write to pipe");
++ }
++ exit(EXIT_FAILURE);
++ }
++ bytes_written += write_res;
++ } while ((size_t)bytes_written < sizeof(report));
++
++ if (stderr_buf != NULL)
++ fclose(stderr_buf);
++ close(pipefd[1]);
++
++ exit(EXIT_SUCCESS);
++ } else if (fork_pid == -1) {
++ close(pipefd[0]);
++ close(pipefd[1]);
++ if (stderr_buf != NULL) {
++ munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to fork");
++ }
++ report.errored++;
++ result = MUNIT_ERROR;
++ } else {
++ close(pipefd[1]);
++ do {
++ read_res = read(pipefd[0], ((munit_uint8_t *)(&report)) + bytes_read,
++ sizeof(report) - (size_t)bytes_read);
++ if (read_res < 1)
++ break;
++ bytes_read += read_res;
++ } while (bytes_read < (ssize_t)sizeof(report));
++
++ changed_pid = waitpid(fork_pid, &status, 0);
++
++ if (MUNIT_LIKELY(changed_pid == fork_pid) &&
++ MUNIT_LIKELY(WIFEXITED(status))) {
++ if (bytes_read != sizeof(report)) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
++ "child exited unexpectedly with status %d",
++ WEXITSTATUS(status));
++ report.errored++;
++ } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
++ "child exited with status %d",
++ WEXITSTATUS(status));
++ report.errored++;
++ }
++ } else {
++ if (WIFSIGNALED(status)) {
++# if defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700)
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
++ "child killed by signal %d (%s)",
++ WTERMSIG(status), strsignal(WTERMSIG(status)));
++# else
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
++ "child killed by signal %d", WTERMSIG(status));
++# endif
++ } else if (WIFSTOPPED(status)) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf,
++ "child stopped by signal %d", WSTOPSIG(status));
++ }
++ report.errored++;
++ }
++
++ close(pipefd[0]);
++ waitpid(fork_pid, NULL, 0);
++ }
++ } else
++#endif
++ {
++#if !defined(MUNIT_NO_BUFFER)
++ const volatile int orig_stderr = munit_replace_stderr(stderr_buf);
++#endif
++
++#if defined(MUNIT_THREAD_LOCAL)
++ if (MUNIT_UNLIKELY(setjmp(munit_error_jmp_buf) != 0)) {
++ result = MUNIT_FAIL;
++ report.failed++;
++ } else {
++ munit_error_jmp_buf_valid = 1;
++ result = munit_test_runner_exec(runner, test, params, &report);
++ }
++#else
++ result = munit_test_runner_exec(runner, test, params, &report);
++#endif
++
++#if !defined(MUNIT_NO_BUFFER)
++ munit_restore_stderr(orig_stderr);
++#endif
++
++ /* Here just so that the label is used on Windows and we don't get
++ * a warning */
++ goto print_result;
++ }
++
++print_result:
++
++ fputs("[ ", MUNIT_OUTPUT_FILE);
++ if ((test->options & MUNIT_TEST_OPTION_TODO) == MUNIT_TEST_OPTION_TODO) {
++ if (report.failed != 0 || report.errored != 0 || report.skipped != 0) {
++ munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_TODO, '3');
++ result = MUNIT_OK;
++ } else {
++ munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1');
++ if (MUNIT_LIKELY(stderr_buf != NULL))
++ munit_log_internal(MUNIT_LOG_ERROR, stderr_buf,
++ "Test marked TODO, but was successful.");
++ runner->report.failed++;
++ result = MUNIT_ERROR;
++ }
++ } else if (report.failed > 0) {
++ munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_FAIL, '1');
++ runner->report.failed++;
++ result = MUNIT_FAIL;
++ } else if (report.errored > 0) {
++ munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1');
++ runner->report.errored++;
++ result = MUNIT_ERROR;
++ } else if (report.skipped > 0) {
++ munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_SKIP, '3');
++ runner->report.skipped++;
++ result = MUNIT_SKIP;
++ } else if (report.successful > 1) {
++ munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2');
++#if defined(MUNIT_ENABLE_TIMING)
++ fputs(" ] [ ", MUNIT_OUTPUT_FILE);
++ munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock / report.successful);
++ fputs(" / ", MUNIT_OUTPUT_FILE);
++ munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock / report.successful);
++ fprintf(MUNIT_OUTPUT_FILE,
++ " CPU ]\n %-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s Total: [ ",
++ "");
++ munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock);
++ fputs(" / ", MUNIT_OUTPUT_FILE);
++ munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock);
++ fputs(" CPU", MUNIT_OUTPUT_FILE);
++#endif
++ runner->report.successful++;
++ result = MUNIT_OK;
++ } else if (report.successful > 0) {
++ munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2');
++#if defined(MUNIT_ENABLE_TIMING)
++ fputs(" ] [ ", MUNIT_OUTPUT_FILE);
++ munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock);
++ fputs(" / ", MUNIT_OUTPUT_FILE);
++ munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock);
++ fputs(" CPU", MUNIT_OUTPUT_FILE);
++#endif
++ runner->report.successful++;
++ result = MUNIT_OK;
++ }
++ fputs(" ]\n", MUNIT_OUTPUT_FILE);
++
++ if (stderr_buf != NULL) {
++ if (result == MUNIT_FAIL || result == MUNIT_ERROR || runner->show_stderr) {
++ fflush(MUNIT_OUTPUT_FILE);
++
++ rewind(stderr_buf);
++ munit_splice(fileno(stderr_buf), STDERR_FILENO);
++
++ fflush(stderr);
++ }
++
++ fclose(stderr_buf);
++ }
++}
++
++static void munit_test_runner_run_test_wild(MunitTestRunner *runner,
++ const MunitTest *test,
++ const char *test_name,
++ MunitParameter *params,
++ MunitParameter *p) {
++ const MunitParameterEnum *pe;
++ char **values;
++ MunitParameter *next;
++
++ for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) {
++ if (p->name == pe->name)
++ break;
++ }
++
++ if (pe == NULL)
++ return;
++
++ for (values = pe->values; *values != NULL; values++) {
++ next = p + 1;
++ p->value = *values;
++ if (next->name == NULL) {
++ munit_test_runner_run_test_with_params(runner, test, params);
++ } else {
++ munit_test_runner_run_test_wild(runner, test, test_name, params, next);
++ }
++ if (runner->fatal_failures &&
++ (runner->report.failed != 0 || runner->report.errored != 0))
++ break;
++ }
++}
++
++/* Run a single test, with every combination of parameters
++ * requested. */
++static void munit_test_runner_run_test(MunitTestRunner *runner,
++ const MunitTest *test,
++ const char *prefix) {
++ char *test_name =
++ munit_maybe_concat(NULL, (char *)prefix, (char *)test->name);
++ /* The array of parameters to pass to
++ * munit_test_runner_run_test_with_params */
++ MunitParameter *params = NULL;
++ size_t params_l = 0;
++ /* Wildcard parameters are parameters which have possible values
++ * specified in the test, but no specific value was passed to the
++ * CLI. That means we want to run the test once for every
++ * possible combination of parameter values or, if --single was
++ * passed to the CLI, a single time with a random set of
++ * parameters. */
++ MunitParameter *wild_params = NULL;
++ size_t wild_params_l = 0;
++ const MunitParameterEnum *pe;
++ const MunitParameter *cli_p;
++ munit_bool filled;
++ unsigned int possible;
++ char **vals;
++ size_t first_wild;
++ const MunitParameter *wp;
++ int pidx;
++
++ munit_rand_seed(runner->seed);
++
++ fprintf(MUNIT_OUTPUT_FILE, "%-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s",
++ test_name);
++
++ if (test->parameters == NULL) {
++ /* No parameters. Simple, nice. */
++ munit_test_runner_run_test_with_params(runner, test, NULL);
++ } else {
++ fputc('\n', MUNIT_OUTPUT_FILE);
++
++ for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) {
++ /* Did we received a value for this parameter from the CLI? */
++ filled = 0;
++ for (cli_p = runner->parameters; cli_p != NULL && cli_p->name != NULL;
++ cli_p++) {
++ if (strcmp(cli_p->name, pe->name) == 0) {
++ if (MUNIT_UNLIKELY(munit_parameters_add(&params_l, &params, pe->name,
++ cli_p->value) != MUNIT_OK))
++ goto cleanup;
++ filled = 1;
++ break;
++ }
++ }
++ if (filled)
++ continue;
++
++ /* Nothing from CLI, is the enum NULL/empty? We're not a
++ * fuzzer… */
++ if (pe->values == NULL || pe->values[0] == NULL)
++ continue;
++
++ /* If --single was passed to the CLI, choose a value from the
++ * list of possibilities randomly. */
++ if (runner->single_parameter_mode) {
++ possible = 0;
++ for (vals = pe->values; *vals != NULL; vals++)
++ possible++;
++ /* We want the tests to be reproducible, even if you're only
++ * running a single test, but we don't want every test with
++ * the same number of parameters to choose the same parameter
++ * number, so use the test name as a primitive salt. */
++ pidx = (int)munit_rand_at_most(munit_str_hash(test_name), possible - 1);
++ if (MUNIT_UNLIKELY(munit_parameters_add(&params_l, &params, pe->name,
++ pe->values[pidx]) != MUNIT_OK))
++ goto cleanup;
++ } else {
++ /* We want to try every permutation. Put in a placeholder
++ * entry, we'll iterate through them later. */
++ if (MUNIT_UNLIKELY(munit_parameters_add(&wild_params_l, &wild_params,
++ pe->name, NULL) != MUNIT_OK))
++ goto cleanup;
++ }
++ }
++
++ if (wild_params_l != 0) {
++ first_wild = params_l;
++ for (wp = wild_params; wp != NULL && wp->name != NULL; wp++) {
++ for (pe = test->parameters;
++ pe != NULL && pe->name != NULL && pe->values != NULL; pe++) {
++ if (strcmp(wp->name, pe->name) == 0) {
++ if (MUNIT_UNLIKELY(munit_parameters_add(&params_l, &params,
++ pe->name,
++ pe->values[0]) != MUNIT_OK))
++ goto cleanup;
++ }
++ }
++ }
++
++ munit_test_runner_run_test_wild(runner, test, test_name, params,
++ params + first_wild);
++ } else {
++ munit_test_runner_run_test_with_params(runner, test, params);
++ }
++
++ cleanup:
++ free(params);
++ free(wild_params);
++ }
++
++ munit_maybe_free_concat(test_name, prefix, test->name);
++}
++
++/* Recurse through the suite and run all the tests. If a list of
++ * tests to run was provied on the command line, run only those
++ * tests. */
++static void munit_test_runner_run_suite(MunitTestRunner *runner,
++ const MunitSuite *suite,
++ const char *prefix) {
++ size_t pre_l;
++ char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix);
++ const MunitTest *test;
++ const char **test_name;
++ const MunitSuite *child_suite;
++
++ /* Run the tests. */
++ for (test = suite->tests; test != NULL && test->test != NULL; test++) {
++ if (runner->tests != NULL) { /* Specific tests were requested on the CLI */
++ for (test_name = runner->tests; test_name != NULL && *test_name != NULL;
++ test_name++) {
++ if ((pre_l == 0 || strncmp(pre, *test_name, pre_l) == 0) &&
++ strncmp(test->name, *test_name + pre_l,
++ strlen(*test_name + pre_l)) == 0) {
++ munit_test_runner_run_test(runner, test, pre);
++ if (runner->fatal_failures &&
++ (runner->report.failed != 0 || runner->report.errored != 0))
++ goto cleanup;
++ }
++ }
++ } else { /* Run all tests */
++ munit_test_runner_run_test(runner, test, pre);
++ }
++ }
++
++ if (runner->fatal_failures &&
++ (runner->report.failed != 0 || runner->report.errored != 0))
++ goto cleanup;
++
++ /* Run any child suites. */
++ for (child_suite = suite->suites;
++ child_suite != NULL && child_suite->prefix != NULL; child_suite++) {
++ munit_test_runner_run_suite(runner, child_suite, pre);
++ }
++
++cleanup:
++
++ munit_maybe_free_concat(pre, prefix, suite->prefix);
++}
++
++static void munit_test_runner_run(MunitTestRunner *runner) {
++ munit_test_runner_run_suite(runner, runner->suite, NULL);
++}
++
++static void munit_print_help(int argc, char *const *argv, void *user_data,
++ const MunitArgument arguments[]) {
++ const MunitArgument *arg;
++ (void)argc;
++
++ printf("USAGE: %s [OPTIONS...] [TEST...]\n\n", argv[0]);
++ puts(
++ " --seed SEED\n"
++ " Value used to seed the PRNG. Must be a 32-bit integer in "
++ "decimal\n"
++ " notation with no separators (commas, decimals, spaces, "
++ "etc.), or\n"
++ " hexidecimal prefixed by \"0x\".\n"
++ " --iterations N\n"
++ " Run each test N times. 0 means the default number.\n"
++ " --param name value\n"
++ " A parameter key/value pair which will be passed to any test "
++ "with\n"
++ " takes a parameter of that name. If not provided, the test "
++ "will be\n"
++ " run once for each possible parameter value.\n"
++ " --list Write a list of all available tests.\n"
++ " --list-params\n"
++ " Write a list of all available tests and their possible "
++ "parameters.\n"
++ " --single Run each parameterized test in a single configuration "
++ "instead of\n"
++ " every possible combination\n"
++ " --log-visible debug|info|warning|error\n"
++ " --log-fatal debug|info|warning|error\n"
++ " Set the level at which messages of different severities are "
++ "visible,\n"
++ " or cause the test to terminate.\n"
++#if !defined(MUNIT_NO_FORK)
++ " --no-fork Do not execute tests in a child process. If this option is "
++ "supplied\n"
++ " and a test crashes (including by failing an assertion), no "
++ "further\n"
++ " tests will be performed.\n"
++#endif
++ " --fatal-failures\n"
++ " Stop executing tests as soon as a failure is found.\n"
++ " --show-stderr\n"
++ " Show data written to stderr by the tests, even if the test "
++ "succeeds.\n"
++ " --color auto|always|never\n"
++ " Colorize (or don't) the output.\n"
++ /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890
++ */
++ " --help Print this help message and exit.\n");
++#if defined(MUNIT_NL_LANGINFO)
++ setlocale(LC_ALL, "");
++ fputs((strcasecmp("UTF-8", nl_langinfo(CODESET)) == 0) ? "µnit" : "munit",
++ stdout);
++#else
++ puts("munit");
++#endif
++ printf(" %d.%d.%d\n"
++ "Full documentation at: https://nemequ.github.io/munit/\n",
++ (MUNIT_CURRENT_VERSION >> 16) & 0xff,
++ (MUNIT_CURRENT_VERSION >> 8) & 0xff,
++ (MUNIT_CURRENT_VERSION >> 0) & 0xff);
++ for (arg = arguments; arg != NULL && arg->name != NULL; arg++)
++ arg->write_help(arg, user_data);
++}
++
++static const MunitArgument *
++munit_arguments_find(const MunitArgument arguments[], const char *name) {
++ const MunitArgument *arg;
++
++ for (arg = arguments; arg != NULL && arg->name != NULL; arg++)
++ if (strcmp(arg->name, name) == 0)
++ return arg;
++
++ return NULL;
++}
++
++static void munit_suite_list_tests(const MunitSuite *suite,
++ munit_bool show_params, const char *prefix) {
++ size_t pre_l;
++ char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix);
++ const MunitTest *test;
++ const MunitParameterEnum *params;
++ munit_bool first;
++ char **val;
++ const MunitSuite *child_suite;
++
++ for (test = suite->tests; test != NULL && test->name != NULL; test++) {
++ if (pre != NULL)
++ fputs(pre, stdout);
++ puts(test->name);
++
++ if (show_params) {
++ for (params = test->parameters; params != NULL && params->name != NULL;
++ params++) {
++ fprintf(stdout, " - %s: ", params->name);
++ if (params->values == NULL) {
++ puts("Any");
++ } else {
++ first = 1;
++ for (val = params->values; *val != NULL; val++) {
++ if (!first) {
++ fputs(", ", stdout);
++ } else {
++ first = 0;
++ }
++ fputs(*val, stdout);
++ }
++ putc('\n', stdout);
++ }
++ }
++ }
++ }
++
++ for (child_suite = suite->suites;
++ child_suite != NULL && child_suite->prefix != NULL; child_suite++) {
++ munit_suite_list_tests(child_suite, show_params, pre);
++ }
++
++ munit_maybe_free_concat(pre, prefix, suite->prefix);
++}
++
++static munit_bool munit_stream_supports_ansi(FILE *stream) {
++#if !defined(_WIN32)
++ return isatty(fileno(stream));
++#else
++
++# if !defined(__MINGW32__)
++ size_t ansicon_size = 0;
++# endif
++
++ if (isatty(fileno(stream))) {
++# if !defined(__MINGW32__)
++ getenv_s(&ansicon_size, NULL, 0, "ANSICON");
++ return ansicon_size != 0;
++# else
++ return getenv("ANSICON") != NULL;
++# endif
++ }
++ return 0;
++#endif
++}
++
++int munit_suite_main_custom(const MunitSuite *suite, void *user_data, int argc,
++ char *const *argv,
++ const MunitArgument arguments[]) {
++ int result = EXIT_FAILURE;
++ MunitTestRunner runner;
++ size_t parameters_size = 0;
++ size_t tests_size = 0;
++ int arg;
++
++ char *envptr;
++ unsigned long ts;
++ char *endptr;
++ unsigned long long iterations;
++ MunitLogLevel level;
++ const MunitArgument *argument;
++ const char **runner_tests;
++ unsigned int tests_run;
++ unsigned int tests_total;
++
++ runner.prefix = NULL;
++ runner.suite = NULL;
++ runner.tests = NULL;
++ runner.seed = 0;
++ runner.iterations = 0;
++ runner.parameters = NULL;
++ runner.single_parameter_mode = 0;
++ runner.user_data = NULL;
++
++ runner.report.successful = 0;
++ runner.report.skipped = 0;
++ runner.report.failed = 0;
++ runner.report.errored = 0;
++#if defined(MUNIT_ENABLE_TIMING)
++ runner.report.cpu_clock = 0;
++ runner.report.wall_clock = 0;
++#endif
++
++ runner.colorize = 0;
++#if !defined(_WIN32)
++ runner.fork = 1;
++#else
++ runner.fork = 0;
++#endif
++ runner.show_stderr = 0;
++ runner.fatal_failures = 0;
++ runner.suite = suite;
++ runner.user_data = user_data;
++ runner.seed = munit_rand_generate_seed();
++ runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE);
++
++ for (arg = 1; arg < argc; arg++) {
++ if (strncmp("--", argv[arg], 2) == 0) {
++ if (strcmp("seed", argv[arg] + 2) == 0) {
++ if (arg + 1 >= argc) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "%s requires an argument", argv[arg]);
++ goto cleanup;
++ }
++
++ envptr = argv[arg + 1];
++ ts = strtoul(argv[arg + 1], &envptr, 0);
++ if (*envptr != '\0' || ts > (~((munit_uint32_t)0U))) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "invalid value ('%s') passed to %s",
++ argv[arg + 1], argv[arg]);
++ goto cleanup;
++ }
++ runner.seed = (munit_uint32_t)ts;
++
++ arg++;
++ } else if (strcmp("iterations", argv[arg] + 2) == 0) {
++ if (arg + 1 >= argc) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "%s requires an argument", argv[arg]);
++ goto cleanup;
++ }
++
++ endptr = argv[arg + 1];
++ iterations = strtoul(argv[arg + 1], &endptr, 0);
++ if (*endptr != '\0' || iterations > UINT_MAX) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "invalid value ('%s') passed to %s",
++ argv[arg + 1], argv[arg]);
++ goto cleanup;
++ }
++
++ runner.iterations = (unsigned int)iterations;
++
++ arg++;
++ } else if (strcmp("param", argv[arg] + 2) == 0) {
++ if (arg + 2 >= argc) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "%s requires two arguments", argv[arg]);
++ goto cleanup;
++ }
++
++ runner.parameters = realloc(
++ runner.parameters, sizeof(MunitParameter) * (parameters_size + 2));
++ if (runner.parameters == NULL) {
++ munit_log_internal(MUNIT_LOG_ERROR, stderr,
++ "failed to allocate memory");
++ goto cleanup;
++ }
++ runner.parameters[parameters_size].name = (char *)argv[arg + 1];
++ runner.parameters[parameters_size].value = (char *)argv[arg + 2];
++ parameters_size++;
++ runner.parameters[parameters_size].name = NULL;
++ runner.parameters[parameters_size].value = NULL;
++ arg += 2;
++ } else if (strcmp("color", argv[arg] + 2) == 0) {
++ if (arg + 1 >= argc) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "%s requires an argument", argv[arg]);
++ goto cleanup;
++ }
++
++ if (strcmp(argv[arg + 1], "always") == 0)
++ runner.colorize = 1;
++ else if (strcmp(argv[arg + 1], "never") == 0)
++ runner.colorize = 0;
++ else if (strcmp(argv[arg + 1], "auto") == 0)
++ runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE);
++ else {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "invalid value ('%s') passed to %s",
++ argv[arg + 1], argv[arg]);
++ goto cleanup;
++ }
++
++ arg++;
++ } else if (strcmp("help", argv[arg] + 2) == 0) {
++ munit_print_help(argc, argv, user_data, arguments);
++ result = EXIT_SUCCESS;
++ goto cleanup;
++ } else if (strcmp("single", argv[arg] + 2) == 0) {
++ runner.single_parameter_mode = 1;
++ } else if (strcmp("show-stderr", argv[arg] + 2) == 0) {
++ runner.show_stderr = 1;
++#if !defined(_WIN32)
++ } else if (strcmp("no-fork", argv[arg] + 2) == 0) {
++ runner.fork = 0;
++#endif
++ } else if (strcmp("fatal-failures", argv[arg] + 2) == 0) {
++ runner.fatal_failures = 1;
++ } else if (strcmp("log-visible", argv[arg] + 2) == 0 ||
++ strcmp("log-fatal", argv[arg] + 2) == 0) {
++ if (arg + 1 >= argc) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "%s requires an argument", argv[arg]);
++ goto cleanup;
++ }
++
++ if (strcmp(argv[arg + 1], "debug") == 0)
++ level = MUNIT_LOG_DEBUG;
++ else if (strcmp(argv[arg + 1], "info") == 0)
++ level = MUNIT_LOG_INFO;
++ else if (strcmp(argv[arg + 1], "warning") == 0)
++ level = MUNIT_LOG_WARNING;
++ else if (strcmp(argv[arg + 1], "error") == 0)
++ level = MUNIT_LOG_ERROR;
++ else {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "invalid value ('%s') passed to %s",
++ argv[arg + 1], argv[arg]);
++ goto cleanup;
++ }
++
++ if (strcmp("log-visible", argv[arg] + 2) == 0)
++ munit_log_level_visible = level;
++ else
++ munit_log_level_fatal = level;
++
++ arg++;
++ } else if (strcmp("list", argv[arg] + 2) == 0) {
++ munit_suite_list_tests(suite, 0, NULL);
++ result = EXIT_SUCCESS;
++ goto cleanup;
++ } else if (strcmp("list-params", argv[arg] + 2) == 0) {
++ munit_suite_list_tests(suite, 1, NULL);
++ result = EXIT_SUCCESS;
++ goto cleanup;
++ } else {
++ argument = munit_arguments_find(arguments, argv[arg] + 2);
++ if (argument == NULL) {
++ munit_logf_internal(MUNIT_LOG_ERROR, stderr,
++ "unknown argument ('%s')", argv[arg]);
++ goto cleanup;
++ }
++
++ if (!argument->parse_argument(suite, user_data, &arg, argc, argv))
++ goto cleanup;
++ }
++ } else {
++ runner_tests =
++ realloc((void *)runner.tests, sizeof(char *) * (tests_size + 2));
++ if (runner_tests == NULL) {
++ munit_log_internal(MUNIT_LOG_ERROR, stderr,
++ "failed to allocate memory");
++ goto cleanup;
++ }
++ runner.tests = runner_tests;
++ runner.tests[tests_size++] = argv[arg];
++ runner.tests[tests_size] = NULL;
++ }
++ }
++
++ fflush(stderr);
++ fprintf(MUNIT_OUTPUT_FILE,
++ "Running test suite with seed 0x%08" PRIx32 "...\n", runner.seed);
++
++ munit_test_runner_run(&runner);
++
++ tests_run =
++ runner.report.successful + runner.report.failed + runner.report.errored;
++ tests_total = tests_run + runner.report.skipped;
++ if (tests_run == 0) {
++ fprintf(stderr, "No tests run, %d (100%%) skipped.\n",
++ runner.report.skipped);
++ } else {
++ fprintf(MUNIT_OUTPUT_FILE,
++ "%d of %d (%0.0f%%) tests successful, %d (%0.0f%%) test skipped.\n",
++ runner.report.successful, tests_run,
++ (((double)runner.report.successful) / ((double)tests_run)) * 100.0,
++ runner.report.skipped,
++ (((double)runner.report.skipped) / ((double)tests_total)) * 100.0);
++ }
++
++ if (runner.report.failed == 0 && runner.report.errored == 0) {
++ result = EXIT_SUCCESS;
++ }
++
++cleanup:
++ free(runner.parameters);
++ free((void *)runner.tests);
++
++ return result;
++}
++
++int munit_suite_main(const MunitSuite *suite, void *user_data, int argc,
++ char *const *argv) {
++ return munit_suite_main_custom(suite, user_data, argc, argv, NULL);
++}
++
++static uint8_t hexchars[] = "0123456789abcdef";
++
++static uint8_t *hexdump_addr(uint8_t *dest, size_t addr) {
++ size_t i;
++ uint8_t a;
++
++ for (i = 0; i < 4; ++i) {
++ a = (addr >> (3 - i) * 8) & 0xff;
++
++ *dest++ = hexchars[a >> 4];
++ *dest++ = hexchars[a & 0xf];
++ }
++
++ return dest;
++}
++
++static uint8_t *asciidump(uint8_t *dest, const uint8_t *data, size_t datalen) {
++ size_t i;
++
++ *dest++ = '|';
++
++ for (i = 0; i < datalen; ++i) {
++ if (0x20 <= data[i] && data[i] <= 0x7e) {
++ *dest++ = data[i];
++ } else {
++ *dest++ = '.';
++ }
++ }
++
++ *dest++ = '|';
++
++ return dest;
++}
++
++static uint8_t *hexdump8(uint8_t *dest, const uint8_t *data, size_t datalen) {
++ size_t i;
++
++ for (i = 0; i < datalen; ++i) {
++ *dest++ = hexchars[data[i] >> 4];
++ *dest++ = hexchars[data[i] & 0xf];
++ *dest++ = ' ';
++ }
++
++ for (; i < 8; ++i) {
++ *dest++ = ' ';
++ *dest++ = ' ';
++ *dest++ = ' ';
++ }
++
++ return dest;
++}
++
++static uint8_t *hexdump16(uint8_t *dest, const uint8_t *data, size_t datalen) {
++ dest = hexdump8(dest, data, datalen < 8 ? datalen : 8);
++ *dest++ = ' ';
++
++ if (datalen < 8) {
++ data = NULL;
++ datalen = 0;
++ } else {
++ data += 8;
++ datalen -= 8;
++ }
++
++ dest = hexdump8(dest, data, datalen);
++ *dest++ = ' ';
++
++ return dest;
++}
++
++static uint8_t *hexdump_line(uint8_t *dest, const uint8_t *data, size_t datalen,
++ size_t addr) {
++ dest = hexdump_addr(dest, addr);
++ *dest++ = ' ';
++ *dest++ = ' ';
++
++ dest = hexdump16(dest, data, datalen);
++
++ dest = asciidump(dest, data, datalen);
++
++ return dest;
++}
++
++int munit_hexdump(FILE *fp, const void *data, size_t datalen) {
++ size_t offset = 0, n, len;
++ uint8_t buf[128], *p;
++ const uint8_t *s;
++ int repeated = 0;
++
++ if (datalen == 0) {
++ return 0;
++ }
++
++ for (; offset < datalen; offset += 16) {
++ n = datalen - offset;
++ s = (const uint8_t *)data + offset;
++
++ if (n >= 16) {
++ n = 16;
++
++ if (offset > 0) {
++ if (memcmp(s - 16, s, 16) == 0) {
++ if (repeated) {
++ continue;
++ }
++
++ repeated = 1;
++
++ if (fwrite("*\n", 1, 2, fp) < 2) {
++ return -1;
++ }
++
++ continue;
++ }
++
++ repeated = 0;
++ }
++ }
++
++ p = hexdump_line(buf, s, n, offset);
++ *p++ = '\n';
++
++ len = (size_t)(p - buf);
++
++ if (fwrite(buf, 1, len, fp) < len) {
++ return -1;
++ }
++ }
++
++ p = hexdump_addr(buf, datalen);
++ *p++ = '\n';
++
++ len = (size_t)(p - buf);
++
++ if (fwrite(buf, 1, len, fp) < len) {
++ return -1;
++ }
++
++ return 0;
++}
++
++int munit_hexdump_diff(FILE *fp, const void *a, size_t alen, const void *b,
++ size_t blen) {
++ size_t offset = 0, k, i, len, ncomp, maxlen, adoff = 0;
++ uint8_t buf[128], *p;
++ const uint8_t mk[2] = {'-', '+'};
++ struct datasource {
++ const uint8_t *data;
++ size_t datalen;
++ const uint8_t *s;
++ size_t n;
++ } ds[] = {{a, alen, NULL, 0}, {b, blen, NULL, 0}}, *dp;
++
++ maxlen = alen < blen ? blen : alen;
++
++ for (; offset < maxlen; offset += 16) {
++ for (k = 0; k < 2; ++k) {
++ dp = &ds[k];
++
++ if (offset < dp->datalen) {
++ dp->s = (const uint8_t *)dp->data + offset;
++ dp->n = dp->datalen - offset;
++
++ if (dp->n > 16) {
++ dp->n = 16;
++ }
++ } else {
++ dp->s = NULL;
++ dp->n = 0;
++ }
++ }
++
++ if (ds[0].n == ds[1].n && memcmp(ds[0].s, ds[1].s, ds[0].n) == 0) {
++ continue;
++ }
++
++ for (k = 0; k < 2; ++k) {
++ dp = &ds[k];
++
++ if (!dp->n) {
++ continue;
++ }
++
++ p = buf;
++ *p++ = mk[k];
++ *p++ = mk[k];
++ *p++ = mk[k];
++ *p++ = mk[k];
++
++ p = hexdump_line(p, dp->s, dp->n, offset);
++ *p++ = '\n';
++
++ len = (size_t)(p - buf);
++
++ if (fwrite(buf, 1, len, fp) < len) {
++ return -1;
++ }
++ }
++
++ if (!ds[0].n || !ds[1].n) {
++ continue;
++ }
++
++ ncomp = ds[0].n < ds[1].n ? ds[0].n : ds[1].n;
++
++ p = buf + 4 + 10;
++
++ memset(buf, ' ', 4 + 78);
++
++ for (i = 0; i < ncomp; ++i) {
++ if (ds[0].s[i] == ds[1].s[i]) {
++ *p++ = ' ';
++ *p++ = ' ';
++ } else {
++ adoff = 4 + 10 + 51 + i;
++ *(buf + adoff) = '^';
++
++ *p++ = '^';
++ *p++ = '^';
++ }
++
++ *p++ = ' ';
++
++ if (i == 7) {
++ *p++ = ' ';
++ }
++ }
++
++ if (adoff) {
++ len = adoff + 1;
++ } else {
++ len = (size_t)(p - buf);
++ }
++
++ buf[len++] = '\n';
++
++ if (fwrite(buf, 1, len, fp) < len) {
++ return -1;
++ }
++ }
++
++ return 0;
++}
+diff --git a/tests/munit/munit.h b/tests/munit/munit.h
+new file mode 100644
+index 0000000..3bdcca0
+--- /dev/null
++++ b/tests/munit/munit.h
+@@ -0,0 +1,575 @@
++/* µnit Testing Framework
++ * Copyright (c) 2013-2017 Evan Nemerson <evan@nemerson.com>
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#ifndef MUNIT_H
++#define MUNIT_H
++
++#include <stdarg.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <stddef.h>
++
++#define MUNIT_VERSION(major, minor, revision) \
++ (((major) << 16) | ((minor) << 8) | (revision))
++
++#define MUNIT_CURRENT_VERSION MUNIT_VERSION(0, 4, 1)
++
++#if defined(_MSC_VER) && (_MSC_VER < 1600)
++# define munit_int8_t __int8
++# define munit_uint8_t unsigned __int8
++# define munit_int16_t __int16
++# define munit_uint16_t unsigned __int16
++# define munit_int32_t __int32
++# define munit_uint32_t unsigned __int32
++# define munit_int64_t __int64
++# define munit_uint64_t unsigned __int64
++#else
++# include <stdint.h>
++# define munit_int8_t int8_t
++# define munit_uint8_t uint8_t
++# define munit_int16_t int16_t
++# define munit_uint16_t uint16_t
++# define munit_int32_t int32_t
++# define munit_uint32_t uint32_t
++# define munit_int64_t int64_t
++# define munit_uint64_t uint64_t
++#endif
++
++#if defined(_MSC_VER) && (_MSC_VER < 1800)
++# if !defined(PRIi8)
++# define PRIi8 "i"
++# endif
++# if !defined(PRIi16)
++# define PRIi16 "i"
++# endif
++# if !defined(PRIi32)
++# define PRIi32 "i"
++# endif
++# if !defined(PRIi64)
++# define PRIi64 "I64i"
++# endif
++# if !defined(PRId8)
++# define PRId8 "d"
++# endif
++# if !defined(PRId16)
++# define PRId16 "d"
++# endif
++# if !defined(PRId32)
++# define PRId32 "d"
++# endif
++# if !defined(PRId64)
++# define PRId64 "I64d"
++# endif
++# if !defined(PRIx8)
++# define PRIx8 "x"
++# endif
++# if !defined(PRIx16)
++# define PRIx16 "x"
++# endif
++# if !defined(PRIx32)
++# define PRIx32 "x"
++# endif
++# if !defined(PRIx64)
++# define PRIx64 "I64x"
++# endif
++# if !defined(PRIu8)
++# define PRIu8 "u"
++# endif
++# if !defined(PRIu16)
++# define PRIu16 "u"
++# endif
++# if !defined(PRIu32)
++# define PRIu32 "u"
++# endif
++# if !defined(PRIu64)
++# define PRIu64 "I64u"
++# endif
++#else
++# include <inttypes.h>
++#endif
++
++#if !defined(munit_bool)
++# if defined(bool)
++# define munit_bool bool
++# elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
++# define munit_bool _Bool
++# else
++# define munit_bool int
++# endif
++#endif
++
++#if defined(__cplusplus)
++extern "C" {
++#endif
++
++#if defined(__GNUC__)
++# define MUNIT_LIKELY(expr) (__builtin_expect((expr), 1))
++# define MUNIT_UNLIKELY(expr) (__builtin_expect((expr), 0))
++# define MUNIT_UNUSED __attribute__((__unused__))
++#else
++# define MUNIT_LIKELY(expr) (expr)
++# define MUNIT_UNLIKELY(expr) (expr)
++# define MUNIT_UNUSED
++#endif
++
++#if !defined(_WIN32)
++# define MUNIT_SIZE_MODIFIER "z"
++# define MUNIT_CHAR_MODIFIER "hh"
++# define MUNIT_SHORT_MODIFIER "h"
++#else
++# if defined(_M_X64) || defined(__amd64__)
++# define MUNIT_SIZE_MODIFIER "I64"
++# else
++# define MUNIT_SIZE_MODIFIER ""
++# endif
++# define MUNIT_CHAR_MODIFIER ""
++# define MUNIT_SHORT_MODIFIER ""
++#endif
++
++#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
++# define MUNIT_NO_RETURN _Noreturn
++#elif defined(__GNUC__)
++# define MUNIT_NO_RETURN __attribute__((__noreturn__))
++#elif defined(_MSC_VER)
++# define MUNIT_NO_RETURN __declspec(noreturn)
++#else
++# define MUNIT_NO_RETURN
++#endif
++
++#if defined(_MSC_VER) && (_MSC_VER >= 1500)
++# define MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ __pragma(warning(push)) __pragma(warning(disable : 4127))
++# define MUNIT_POP_DISABLE_MSVC_C4127_ __pragma(warning(pop))
++#else
++# define MUNIT_PUSH_DISABLE_MSVC_C4127_
++# define MUNIT_POP_DISABLE_MSVC_C4127_
++#endif
++
++typedef enum {
++ MUNIT_LOG_DEBUG,
++ MUNIT_LOG_INFO,
++ MUNIT_LOG_WARNING,
++ MUNIT_LOG_ERROR
++} MunitLogLevel;
++
++#if defined(__GNUC__) && !defined(__MINGW32__)
++# define MUNIT_PRINTF(string_index, first_to_check) \
++ __attribute__((format(printf, string_index, first_to_check)))
++#else
++# define MUNIT_PRINTF(string_index, first_to_check)
++#endif
++
++MUNIT_PRINTF(4, 5)
++void munit_logf_ex(MunitLogLevel level, const char *filename, int line,
++ const char *format, ...);
++
++#define munit_logf(level, format, ...) \
++ munit_logf_ex(level, __FILE__, __LINE__, format, __VA_ARGS__)
++
++#define munit_log(level, msg) munit_logf(level, "%s", msg)
++
++MUNIT_NO_RETURN
++MUNIT_PRINTF(3, 4)
++void munit_errorf_ex(const char *filename, int line, const char *format, ...);
++
++#define munit_errorf(format, ...) \
++ munit_errorf_ex(__FILE__, __LINE__, format, __VA_ARGS__)
++
++#define munit_error(msg) munit_errorf("%s", msg)
++
++#define munit_assert(expr) \
++ do { \
++ if (!MUNIT_LIKELY(expr)) { \
++ munit_error("assertion failed: " #expr); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#define munit_assert_true(expr) \
++ do { \
++ if (!MUNIT_LIKELY(expr)) { \
++ munit_error("assertion failed: " #expr " is not true"); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#define munit_assert_false(expr) \
++ do { \
++ if (!MUNIT_LIKELY(!(expr))) { \
++ munit_error("assertion failed: " #expr " is not false"); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#define munit_assert_type_full(prefix, suffix, T, fmt, a, op, b) \
++ do { \
++ T munit_tmp_a_ = (a); \
++ T munit_tmp_b_ = (b); \
++ if (!(munit_tmp_a_ op munit_tmp_b_)) { \
++ munit_errorf("assertion failed: %s %s %s (" prefix "%" fmt suffix \
++ " %s " prefix "%" fmt suffix ")", \
++ #a, #op, #b, munit_tmp_a_, #op, munit_tmp_b_); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#define munit_assert_type(T, fmt, a, op, b) \
++ munit_assert_type_full("", "", T, fmt, a, op, b)
++
++#define munit_assert_char(a, op, b) \
++ munit_assert_type_full("'\\x", "'", char, "02" MUNIT_CHAR_MODIFIER "x", a, \
++ op, b)
++#define munit_assert_uchar(a, op, b) \
++ munit_assert_type_full("'\\x", "'", unsigned char, \
++ "02" MUNIT_CHAR_MODIFIER "x", a, op, b)
++#define munit_assert_short(a, op, b) \
++ munit_assert_type(short, MUNIT_SHORT_MODIFIER "d", a, op, b)
++#define munit_assert_ushort(a, op, b) \
++ munit_assert_type(unsigned short, MUNIT_SHORT_MODIFIER "u", a, op, b)
++#define munit_assert_int(a, op, b) munit_assert_type(int, "d", a, op, b)
++#define munit_assert_uint(a, op, b) \
++ munit_assert_type(unsigned int, "u", a, op, b)
++#define munit_assert_long(a, op, b) munit_assert_type(long int, "ld", a, op, b)
++#define munit_assert_ulong(a, op, b) \
++ munit_assert_type(unsigned long int, "lu", a, op, b)
++#define munit_assert_llong(a, op, b) \
++ munit_assert_type(long long int, "lld", a, op, b)
++#define munit_assert_ullong(a, op, b) \
++ munit_assert_type(unsigned long long int, "llu", a, op, b)
++
++#define munit_assert_size(a, op, b) \
++ munit_assert_type(size_t, MUNIT_SIZE_MODIFIER "u", a, op, b)
++#define munit_assert_ssize(a, op, b) \
++ munit_assert_type(ssize_t, MUNIT_SIZE_MODIFIER "d", a, op, b)
++
++#define munit_assert_float(a, op, b) munit_assert_type(float, "f", a, op, b)
++#define munit_assert_double(a, op, b) munit_assert_type(double, "g", a, op, b)
++#define munit_assert_ptr(a, op, b) \
++ munit_assert_type(const void *, "p", a, op, b)
++
++#define munit_assert_int8(a, op, b) \
++ munit_assert_type(munit_int8_t, PRIi8, a, op, b)
++#define munit_assert_uint8(a, op, b) \
++ munit_assert_type(munit_uint8_t, PRIu8, a, op, b)
++#define munit_assert_int16(a, op, b) \
++ munit_assert_type(munit_int16_t, PRIi16, a, op, b)
++#define munit_assert_uint16(a, op, b) \
++ munit_assert_type(munit_uint16_t, PRIu16, a, op, b)
++#define munit_assert_int32(a, op, b) \
++ munit_assert_type(munit_int32_t, PRIi32, a, op, b)
++#define munit_assert_uint32(a, op, b) \
++ munit_assert_type(munit_uint32_t, PRIu32, a, op, b)
++#define munit_assert_int64(a, op, b) \
++ munit_assert_type(munit_int64_t, PRIi64, a, op, b)
++#define munit_assert_uint64(a, op, b) \
++ munit_assert_type(munit_uint64_t, PRIu64, a, op, b)
++
++#define munit_assert_ptrdiff(a, op, b) \
++ munit_assert_type(ptrdiff_t, "td", a, op, b)
++
++#define munit_assert_enum(T, a, op, b) munit_assert_type(T, "d", a, op, b)
++
++#define munit_assert_double_equal(a, b, precision) \
++ do { \
++ const double munit_tmp_a_ = (a); \
++ const double munit_tmp_b_ = (b); \
++ const double munit_tmp_diff_ = ((munit_tmp_a_ - munit_tmp_b_) < 0) \
++ ? -(munit_tmp_a_ - munit_tmp_b_) \
++ : (munit_tmp_a_ - munit_tmp_b_); \
++ if (MUNIT_UNLIKELY(munit_tmp_diff_ > 1e-##precision)) { \
++ munit_errorf("assertion failed: %s == %s (%0." #precision \
++ "g == %0." #precision "g)", \
++ #a, #b, munit_tmp_a_, munit_tmp_b_); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#include <string.h>
++#define munit_assert_string_equal(a, b) \
++ do { \
++ const char *munit_tmp_a_ = a; \
++ const char *munit_tmp_b_ = b; \
++ if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) != 0)) { \
++ munit_hexdump_diff(stderr, munit_tmp_a_, strlen(munit_tmp_a_), \
++ munit_tmp_b_, strlen(munit_tmp_b_)); \
++ munit_errorf("assertion failed: string %s == %s (\"%s\" == \"%s\")", #a, \
++ #b, munit_tmp_a_, munit_tmp_b_); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#define munit_assert_string_not_equal(a, b) \
++ do { \
++ const char *munit_tmp_a_ = a; \
++ const char *munit_tmp_b_ = b; \
++ if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) == 0)) { \
++ munit_errorf("assertion failed: string %s != %s (\"%s\" == \"%s\")", #a, \
++ #b, munit_tmp_a_, munit_tmp_b_); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#define munit_assert_memory_equal(size, a, b) \
++ do { \
++ const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \
++ const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \
++ const size_t munit_tmp_size_ = (size); \
++ if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) != \
++ 0) { \
++ size_t munit_tmp_pos_; \
++ for (munit_tmp_pos_ = 0; munit_tmp_pos_ < munit_tmp_size_; \
++ munit_tmp_pos_++) { \
++ if (munit_tmp_a_[munit_tmp_pos_] != munit_tmp_b_[munit_tmp_pos_]) { \
++ munit_hexdump_diff(stderr, munit_tmp_a_, size, munit_tmp_b_, size); \
++ munit_errorf("assertion failed: memory %s == %s, at offset " \
++ "%" MUNIT_SIZE_MODIFIER "u", \
++ #a, #b, munit_tmp_pos_); \
++ break; \
++ } \
++ } \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#define munit_assert_memn_equal(a, a_size, b, b_size) \
++ do { \
++ const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \
++ const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \
++ const size_t munit_tmp_a_size_ = (a_size); \
++ const size_t munit_tmp_b_size_ = (b_size); \
++ if (MUNIT_UNLIKELY(munit_tmp_a_size_ != munit_tmp_b_size_) || \
++ MUNIT_UNLIKELY(munit_tmp_a_size_ && memcmp(munit_tmp_a_, munit_tmp_b_, \
++ munit_tmp_a_size_)) != 0) { \
++ munit_hexdump_diff(stderr, munit_tmp_a_, munit_tmp_a_size_, \
++ munit_tmp_b_, munit_tmp_b_size_); \
++ munit_errorf("assertion failed: memory %s == %s", #a, #b); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#define munit_assert_memory_not_equal(size, a, b) \
++ do { \
++ const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \
++ const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \
++ const size_t munit_tmp_size_ = (size); \
++ if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) == \
++ 0) { \
++ munit_errorf("assertion failed: memory %s != %s (%zu bytes)", #a, #b, \
++ munit_tmp_size_); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#define munit_assert_ptr_equal(a, b) munit_assert_ptr(a, ==, b)
++#define munit_assert_ptr_not_equal(a, b) munit_assert_ptr(a, !=, b)
++#define munit_assert_null(ptr) munit_assert_ptr(ptr, ==, NULL)
++#define munit_assert_not_null(ptr) munit_assert_ptr(ptr, !=, NULL)
++#define munit_assert_ptr_null(ptr) munit_assert_ptr(ptr, ==, NULL)
++#define munit_assert_ptr_not_null(ptr) munit_assert_ptr(ptr, !=, NULL)
++
++/*** Memory allocation ***/
++
++void *munit_malloc_ex(const char *filename, int line, size_t size);
++
++#define munit_malloc(size) munit_malloc_ex(__FILE__, __LINE__, (size))
++
++#define munit_new(type) ((type *)munit_malloc(sizeof(type)))
++
++#define munit_calloc(nmemb, size) munit_malloc((nmemb) * (size))
++
++#define munit_newa(type, nmemb) ((type *)munit_calloc((nmemb), sizeof(type)))
++
++/*** Random number generation ***/
++
++void munit_rand_seed(munit_uint32_t seed);
++munit_uint32_t munit_rand_uint32(void);
++int munit_rand_int_range(int min, int max);
++double munit_rand_double(void);
++void munit_rand_memory(size_t size, munit_uint8_t *buffer);
++
++/*** Tests and Suites ***/
++
++typedef enum {
++ /* Test successful */
++ MUNIT_OK,
++ /* Test failed */
++ MUNIT_FAIL,
++ /* Test was skipped */
++ MUNIT_SKIP,
++ /* Test failed due to circumstances not intended to be tested
++ * (things like network errors, invalid parameter value, failure to
++ * allocate memory in the test harness, etc.). */
++ MUNIT_ERROR
++} MunitResult;
++
++typedef struct {
++ char *name;
++ char **values;
++} MunitParameterEnum;
++
++typedef struct {
++ char *name;
++ char *value;
++} MunitParameter;
++
++const char *munit_parameters_get(const MunitParameter params[],
++ const char *key);
++
++typedef enum {
++ MUNIT_TEST_OPTION_NONE = 0,
++ MUNIT_TEST_OPTION_SINGLE_ITERATION = 1 << 0,
++ MUNIT_TEST_OPTION_TODO = 1 << 1
++} MunitTestOptions;
++
++typedef MunitResult (*MunitTestFunc)(const MunitParameter params[],
++ void *user_data_or_fixture);
++typedef void *(*MunitTestSetup)(const MunitParameter params[], void *user_data);
++typedef void (*MunitTestTearDown)(void *fixture);
++
++typedef struct {
++ const char *name;
++ MunitTestFunc test;
++ MunitTestSetup setup;
++ MunitTestTearDown tear_down;
++ MunitTestOptions options;
++ MunitParameterEnum *parameters;
++} MunitTest;
++
++typedef enum { MUNIT_SUITE_OPTION_NONE = 0 } MunitSuiteOptions;
++
++typedef struct MunitSuite_ MunitSuite;
++
++struct MunitSuite_ {
++ const char *prefix;
++ const MunitTest *tests;
++ const MunitSuite *suites;
++ unsigned int iterations;
++ MunitSuiteOptions options;
++};
++
++int munit_suite_main(const MunitSuite *suite, void *user_data, int argc,
++ char *const *argv);
++
++/* Note: I'm not very happy with this API; it's likely to change if I
++ * figure out something better. Suggestions welcome. */
++
++typedef struct MunitArgument_ MunitArgument;
++
++struct MunitArgument_ {
++ char *name;
++ munit_bool (*parse_argument)(const MunitSuite *suite, void *user_data,
++ int *arg, int argc, char *const *argv);
++ void (*write_help)(const MunitArgument *argument, void *user_data);
++};
++
++int munit_suite_main_custom(const MunitSuite *suite, void *user_data, int argc,
++ char *const *argv, const MunitArgument arguments[]);
++
++#if defined(MUNIT_ENABLE_ASSERT_ALIASES)
++
++# define assert_true(expr) munit_assert_true(expr)
++# define assert_false(expr) munit_assert_false(expr)
++# define assert_char(a, op, b) munit_assert_char(a, op, b)
++# define assert_uchar(a, op, b) munit_assert_uchar(a, op, b)
++# define assert_short(a, op, b) munit_assert_short(a, op, b)
++# define assert_ushort(a, op, b) munit_assert_ushort(a, op, b)
++# define assert_int(a, op, b) munit_assert_int(a, op, b)
++# define assert_uint(a, op, b) munit_assert_uint(a, op, b)
++# define assert_long(a, op, b) munit_assert_long(a, op, b)
++# define assert_ulong(a, op, b) munit_assert_ulong(a, op, b)
++# define assert_llong(a, op, b) munit_assert_llong(a, op, b)
++# define assert_ullong(a, op, b) munit_assert_ullong(a, op, b)
++# define assert_size(a, op, b) munit_assert_size(a, op, b)
++# define assert_ssize(a, op, b) munit_assert_ssize(a, op, b)
++# define assert_float(a, op, b) munit_assert_float(a, op, b)
++# define assert_double(a, op, b) munit_assert_double(a, op, b)
++# define assert_ptr(a, op, b) munit_assert_ptr(a, op, b)
++
++# define assert_int8(a, op, b) munit_assert_int8(a, op, b)
++# define assert_uint8(a, op, b) munit_assert_uint8(a, op, b)
++# define assert_int16(a, op, b) munit_assert_int16(a, op, b)
++# define assert_uint16(a, op, b) munit_assert_uint16(a, op, b)
++# define assert_int32(a, op, b) munit_assert_int32(a, op, b)
++# define assert_uint32(a, op, b) munit_assert_uint32(a, op, b)
++# define assert_int64(a, op, b) munit_assert_int64(a, op, b)
++# define assert_uint64(a, op, b) munit_assert_uint64(a, op, b)
++
++# define assert_ptrdiff(a, op, b) munit_assert_ptrdiff(a, op, b)
++
++# define assert_enum(T, a, op, b) munit_assert_enum(T, a, op, b)
++
++# define assert_double_equal(a, b, precision) \
++ munit_assert_double_equal(a, b, precision)
++# define assert_string_equal(a, b) munit_assert_string_equal(a, b)
++# define assert_string_not_equal(a, b) munit_assert_string_not_equal(a, b)
++# define assert_memory_equal(size, a, b) munit_assert_memory_equal(size, a, b)
++# define assert_memn_equal(a, a_size, b, b_size) \
++ munit_assert_memn_equal(a, a_size, b, b_size)
++# define assert_memory_not_equal(size, a, b) \
++ munit_assert_memory_not_equal(size, a, b)
++# define assert_ptr_equal(a, b) munit_assert_ptr_equal(a, b)
++# define assert_ptr_not_equal(a, b) munit_assert_ptr_not_equal(a, b)
++# define assert_ptr_null(ptr) munit_assert_null_equal(ptr)
++# define assert_ptr_not_null(ptr) munit_assert_not_null(ptr)
++
++# define assert_null(ptr) munit_assert_null(ptr)
++# define assert_not_null(ptr) munit_assert_not_null(ptr)
++
++#endif /* defined(MUNIT_ENABLE_ASSERT_ALIASES) */
++
++#define munit_void_test_decl(func) \
++ void func(void); \
++ \
++ static inline MunitResult wrap_##func(const MunitParameter params[], \
++ void *fixture) { \
++ (void)params; \
++ (void)fixture; \
++ \
++ func(); \
++ return MUNIT_OK; \
++ }
++
++#define munit_void_test(func) \
++ { "/" #func, wrap_##func, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
++
++#define munit_test_end() \
++ { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
++
++int munit_hexdump(FILE *fp, const void *data, size_t datalen);
++
++int munit_hexdump_diff(FILE *fp, const void *a, size_t alen, const void *b,
++ size_t blen);
++
++#if defined(__cplusplus)
++}
++#endif
++
++#endif /* !defined(MUNIT_H) */
++
++#if defined(MUNIT_ENABLE_ASSERT_ALIASES)
++#if defined(assert)
++# undef assert
++#endif
++#define assert(expr) munit_assert(expr)
++#endif
+diff --git a/tests/munit/munitxx.h b/tests/munit/munitxx.h
+new file mode 100644
+index 0000000..5e50921
+--- /dev/null
++++ b/tests/munit/munitxx.h
+@@ -0,0 +1,94 @@
++/* µnit Testing Framework
++ * Copyright (c) 2013-2017 Evan Nemerson <evan@nemerson.com>
++ * Copyright (c) 2023 munit contributors
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#ifndef MUNITXX_H
++#define MUNITXX_H
++
++#include <munit.h>
++
++#include <type_traits>
++#include <string>
++#if __cplusplus >= 201703L
++# include <string_view>
++#endif // __cplusplus >= 201703L
++
++#define munit_assert_stdstring_equal(a, b) \
++ do { \
++ const std::string munit_tmp_a_ = a; \
++ const std::string munit_tmp_b_ = b; \
++ if (MUNIT_UNLIKELY(a != b)) { \
++ munit_hexdump_diff(stderr, munit_tmp_a_.c_str(), munit_tmp_a_.size(), \
++ munit_tmp_b_.c_str(), munit_tmp_b_.size()); \
++ munit_errorf("assertion failed: string %s == %s (\"%s\" == \"%s\")", #a, \
++ #b, munit_tmp_a_.c_str(), munit_tmp_b_.c_str()); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#if __cplusplus >= 201703L
++# define munit_assert_stdsv_equal(a, b) \
++ do { \
++ const std::string_view munit_tmp_a_ = a; \
++ const std::string_view munit_tmp_b_ = b; \
++ if (MUNIT_UNLIKELY(a != b)) { \
++ munit_hexdump_diff(stderr, munit_tmp_a_.data(), munit_tmp_a_.size(), \
++ munit_tmp_b_.data(), munit_tmp_b_.size()); \
++ munit_errorf( \
++ "assertion failed: string %s == %s (\"%.*s\" == \"%.*s\")", #a, \
++ #b, (int)munit_tmp_a_.size(), munit_tmp_a_.data(), \
++ (int)munit_tmp_b_.size(), munit_tmp_b_.data()); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++#endif // __cplusplus >= 201703L
++
++#define munit_assert_enum_class(a, op, b) \
++ do { \
++ auto munit_tmp_a_ = (a); \
++ auto munit_tmp_b_ = (b); \
++ if (!(munit_tmp_a_ op munit_tmp_b_)) { \
++ auto munit_tmp_a_str_ = std::to_string( \
++ static_cast<std::underlying_type_t<decltype(munit_tmp_a_)>>( \
++ munit_tmp_a_)); \
++ auto munit_tmp_b_str_ = std::to_string( \
++ static_cast<std::underlying_type_t<decltype(munit_tmp_b_)>>( \
++ munit_tmp_b_)); \
++ munit_errorf("assertion failed: %s %s %s (%s %s %s)", #a, #op, #b, \
++ munit_tmp_a_str_.c_str(), #op, munit_tmp_b_str_.c_str()); \
++ } \
++ MUNIT_PUSH_DISABLE_MSVC_C4127_ \
++ } while (0) MUNIT_POP_DISABLE_MSVC_C4127_
++
++#if defined(MUNIT_ENABLE_ASSERT_ALIASES)
++
++# define assert_stdstring_equal(a, b) munit_assert_stdstring_equal(a, b)
++# if __cplusplus >= 201703L
++# define assert_stdsv_equal(a, b) munit_assert_stdsv_equal(a, b)
++# endif // __cplusplus >= 201703L
++# define assert_enum_class(a, op, b) munit_assert_enum_class(a, op, b)
++
++#endif /* defined(MUNIT_ENABLE_ASSERT_ALIASES) */
++
++#endif // MUNITXX_H
diff --git a/debian/patches/series b/debian/patches/series
index 8670f6c..079921e 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1,2 @@
-0001-Make-fetch-ocsp-response-use-python3.patch
0002-Workaround-for-963648.patch
+0002-add-munit-explicitly.patch