Introduce clar testing framework
Subsystems (2)
cover Introduce clar testing framework
Hi,
there's been some discussion around extending our unit testing framework to avoid duplication when declaring test functions. Right now, a testing function has to be declared and then wired up via the test's main function, which can be a bit annoying. In the thread, René proposes an alternative that gets rid of this duplication by using macros. And while that does solve the issue, there were some concerns about things being too much "magic" while at the same time not being flexible enough.
Part of the discussion revolved around whether we maybe want to have a proper unit testing framework in our codebase instead of reinventing the wheel. As I quite liked the "clar" [2] testing framework from back when I was still developing libgit2 I proposed it as a possible alternative. This patch series wires up the clar framework as a proof of concept and converts the strvec test suite to use it.
The magic to avoid the above code duplication is quite self-contained in a "generate.py" file. This script extracts function declarations from all unit test suites and then writes those into a "clar.suite" header file. All that one needs to do is thus to declare a function with a specific name "test_<suite>__<name>" and then everything else gets wired up automatically.
Whether this is better than the solution proposed by René is probably a matter of taste. While I'm not a huge fan of the macro-based solution, I don't want to veto it either (not that I'd have that power anyway). So please, you should rather read this as a proof of concept to see how alternatives could look like such that we have a better picture of where we want to end up.
Some random thoughts:
- The mandated Python dependency is suboptimal in my opinion. Rewriting the script in e.g. Perl should be easy enough though, it's no rocket science.
- I prefer that the proposed solution results in a single binary as compared to one binary per test system.
- The clar gives us the ability to pick which tests to run via command line parameters, which I personally like more than picking the specific binary to run.
- The clar replaces some test assertions that we already have. They feel a bit more mature, but overall there aren't all that many assertions available. If we wanted to pick it up, then we'd likely have to add some more wrappers.
- The clar uses longjmp instead of manually having to return from functions in case there was an assertion failure. This is easier to work with in my opinion.
Also, note that I only tested this on my Linux machine. I have no clue whether this works as-is on Windows, but I do know that libgit2 tests run on Linux, macOS and Windows. So it should work in theory, it's just a matter of polishing this series.
I'm happy to hear your thoughts on this, even if it ultimately ends up being shot down.
Patrick
[1]: <[email protected]> [2]: https://github.com/clar-test/clar
Patrick Steinhardt (3):
t: import the clar unit testing framework
Makefile: wire up the clar unit testing framework t/unit-tests: convert strvec tests to use clar
.gitignore | 1 +
Makefile | 36 +-
t/Makefile | 1 +
t/unit-tests/.gitignore | 3 +
t/unit-tests/clar/.github/workflows/ci.yml | 23 +
t/unit-tests/clar/COPYING | 15 +
t/unit-tests/clar/README.md | 329 ++++++++
t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++
t/unit-tests/clar/clar.h | 173 +++++
t/unit-tests/clar/clar/fixtures.h | 50 ++
t/unit-tests/clar/clar/fs.h | 522 +++++++++++++
t/unit-tests/clar/clar/print.h | 211 ++++++
t/unit-tests/clar/clar/sandbox.h | 154 ++++
t/unit-tests/clar/clar/summary.h | 143 ++++
t/unit-tests/clar/generate.py | 267 +++++++
t/unit-tests/clar/test/.gitignore | 5 +
t/unit-tests/clar/test/Makefile | 39 +
t/unit-tests/clar/test/clar_test.h | 16 +
t/unit-tests/clar/test/main.c | 40 +
t/unit-tests/clar/test/main.c.sample | 27 +
t/unit-tests/clar/test/resources/test/file | 1 +
t/unit-tests/clar/test/sample.c | 84 ++
t/unit-tests/{t-strvec.c => strvec.c} | 124 ++-
t/unit-tests/unit-test.c | 16 +
t/unit-tests/unit-test.h | 3 +
25 files changed, 3041 insertions(+), 84 deletions(-)
create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml
create mode 100644 t/unit-tests/clar/COPYING
create mode 100644 t/unit-tests/clar/README.md
create mode 100644 t/unit-tests/clar/clar.c
create mode 100644 t/unit-tests/clar/clar.h
create mode 100644 t/unit-tests/clar/clar/fixtures.h
create mode 100644 t/unit-tests/clar/clar/fs.h
create mode 100644 t/unit-tests/clar/clar/print.h
create mode 100644 t/unit-tests/clar/clar/sandbox.h
create mode 100644 t/unit-tests/clar/clar/summary.h
create mode 100755 t/unit-tests/clar/generate.py
create mode 100644 t/unit-tests/clar/test/.gitignore
create mode 100644 t/unit-tests/clar/test/Makefile
create mode 100644 t/unit-tests/clar/test/clar_test.h
create mode 100644 t/unit-tests/clar/test/main.c
create mode 100644 t/unit-tests/clar/test/main.c.sample
create mode 100644 t/unit-tests/clar/test/resources/test/file
create mode 100644 t/unit-tests/clar/test/sample.c
rename t/unit-tests/{t-strvec.c => strvec.c} (54%)
create mode 100644 t/unit-tests/unit-test.c
create mode 100644 t/unit-tests/unit-test.hSignature · 1 line
2.46.0.dirty
RFC PATCH 1/3 t: import the clar unit testing framework
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
--- t/unit-tests/clar/.github/workflows/ci.yml | 23 + t/unit-tests/clar/COPYING | 15 + t/unit-tests/clar/README.md | 329 ++++++++ t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++ t/unit-tests/clar/clar.h | 173 +++++ t/unit-tests/clar/clar/fixtures.h | 50 ++ t/unit-tests/clar/clar/fs.h | 522 +++++++++++++ t/unit-tests/clar/clar/print.h | 211 ++++++ t/unit-tests/clar/clar/sandbox.h | 154 ++++ t/unit-tests/clar/clar/summary.h | 143 ++++ t/unit-tests/clar/generate.py | 267 +++++++ t/unit-tests/clar/test/.gitignore | 5 + t/unit-tests/clar/test/Makefile | 39 + t/unit-tests/clar/test/clar_test.h | 16 + t/unit-tests/clar/test/main.c | 40 + t/unit-tests/clar/test/main.c.sample | 27 + t/unit-tests/clar/test/resources/test/file | 1 + t/unit-tests/clar/test/sample.c | 84 ++ 18 files changed, 2941 insertions(+) create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml create mode 100644 t/unit-tests/clar/COPYING create mode 100644 t/unit-tests/clar/README.md create mode 100644 t/unit-tests/clar/clar.c create mode 100644 t/unit-tests/clar/clar.h create mode 100644 t/unit-tests/clar/clar/fixtures.h create mode 100644 t/unit-tests/clar/clar/fs.h create mode 100644 t/unit-tests/clar/clar/print.h create mode 100644 t/unit-tests/clar/clar/sandbox.h create mode 100644 t/unit-tests/clar/clar/summary.h create mode 100755 t/unit-tests/clar/generate.py create mode 100644 t/unit-tests/clar/test/.gitignore create mode 100644 t/unit-tests/clar/test/Makefile create mode 100644 t/unit-tests/clar/test/clar_test.h create mode 100644 t/unit-tests/clar/test/main.c create mode 100644 t/unit-tests/clar/test/main.c.sample create mode 100644 t/unit-tests/clar/test/resources/test/file create mode 100644 t/unit-tests/clar/test/sample.c
t/unit-tests/clar/.github/workflows/ci.yml new file +23 -0
@@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + strategy: + matrix: + os: [ ubuntu-latest, macos-latest ] + + runs-on: ${{ matrix.os }} + + steps: + - name: Check out + uses: actions/checkout@v2 + - name: Build + run: | + cd test + make
t/unit-tests/clar/COPYING new file +15 -0
@@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2011-2015 Vicent Marti + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
t/unit-tests/clar/README.md new file +329 -0
329 lines not on this page · raw message
t/unit-tests/clar/clar.c new file +842 -0
842 lines not on this page · raw message
t/unit-tests/clar/clar.h new file +173 -0
173 lines not on this page · raw message
t/unit-tests/clar/clar/fixtures.h new file +50 -0
50 lines not on this page · raw message
t/unit-tests/clar/clar/fs.h new file +522 -0
522 lines not on this page · raw message
t/unit-tests/clar/clar/print.h new file +211 -0
211 lines not on this page · raw message
t/unit-tests/clar/clar/sandbox.h new file +154 -0
154 lines not on this page · raw message
t/unit-tests/clar/clar/summary.h new file +143 -0
143 lines not on this page · raw message
t/unit-tests/clar/generate.py new file +267 -0
267 lines not on this page · raw message
t/unit-tests/clar/test/.gitignore new file +5 -0
@@ -0,0 +1,5 @@ +clar.suite +.clarcache +clar_test +*.o +
t/unit-tests/clar/test/Makefile new file +39 -0
@@ -0,0 +1,39 @@ +# +# Copyright (c) Vicent Marti. All rights reserved. +# +# This file is part of clar, distributed under the ISC license. +# For full terms see the included COPYING file. +# + +# +# Set up the path to the clar sources and to the fixtures directory +# +# The fixture path needs to be an absolute path so it can be used +# even after we have chdir'ed into the test directory while testing. +# +CURRENT_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +TEST_DIRECTORY := $(abspath $(dir $(CURRENT_MAKEFILE))) +CLAR_PATH := $(dir $(TEST_DIRECTORY)) +CLAR_FIXTURE_PATH := $(TEST_DIRECTORY)/resources/ + +CFLAGS=-g -I.. -I. -Wall -DCLAR_FIXTURE_PATH=\"$(CLAR_FIXTURE_PATH)\" + +.PHONY: clean + +# list the objects that go into our test +objects = main.o sample.o + +# build the test executable itself +clar_test: $(objects) clar_test.h clar.suite $(CLAR_PATH)clar.c + $(CC) $(CFLAGS) -o $@ "$(CLAR_PATH)clar.c" $(objects) + +# test object files depend on clar macros +$(objects) : $(CLAR_PATH)clar.h + +# build the clar.suite file of test metadata +clar.suite: + python "$(CLAR_PATH)generate.py" . + +# remove all generated files +clean: + $(RM) -rf *.o clar.suite .clarcache clar_test clar_test.dSYM
t/unit-tests/clar/test/clar_test.h new file +16 -0
@@ -0,0 +1,16 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ +#ifndef __CLAR_TEST__ +#define __CLAR_TEST__ + +/* Import the standard clar helper functions */ +#include "clar.h" + +/* Your custom shared includes / defines here */ +extern int global_test_counter; + +#endif
t/unit-tests/clar/test/main.c new file +40 -0
@@ -0,0 +1,40 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ + +#include "clar_test.h" + +/* +* Sample main() for clar tests. +* +* You should write your own main routine for clar tests that does specific +* setup and teardown as necessary for your application. The only required +* line is the call to `clar_test(argc, argv)`, which will execute the test +* suite. If you want to check the return value of the test application, +* your main() should return the same value returned by clar_test(). +*/ + +int global_test_counter =0 ; + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ +int ret; + +/* Your custom initialization here */ + global_test_counter =0 ; + +/* Run the test suite */ + ret =clar_test (argc, argv); + +/* Your custom cleanup here */ +cl_assert_equal_i (8 , global_test_counter); + +return ret; +}
t/unit-tests/clar/test/main.c.sample new file +27 -0
@@ -0,0 +1,27 @@ +/* + * Copyright (c) Vicent Marti. All rights reserved. + * + * This file is part of clar, distributed under the ISC license. + * For full terms see the included COPYING file. + */ + +#include "clar_test.h" + +/* + * Minimal main() for clar tests. + * + * Modify this with any application specific setup or teardown that you need. + * The only required line is the call to `clar_test(argc, argv)`, which will + * execute the test suite. If you want to check the return value of the test + * application, main() should return the same value returned by clar_test(). + */ + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + /* Run the test suite */ + return clar_test(argc, argv); +}
t/unit-tests/clar/test/resources/test/file new file +1 -0
@@ -0,0 +1 @@ +File
t/unit-tests/clar/test/sample.c new file +84 -0
84 lines not on this page · raw message
Signature · 1 line
2.46.0.dirty
Re: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 3 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Rather than forking our own copy of clar, could we just add it as a submodule instead?
Re: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 7 lines · Josh Steadmon
Quoted 3 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Rather than forking our own copy of clar, could we just add it as a submodule instead?
I doubt that forking was Patrick's intention to begin with. Convenience is.
It certainly is a possibility to bind it as a submodule, but it would be easier for everybody to futz with and experiment, at least while it is in its RFC phase.
Thanks.
Re: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 7 lines · Junio C Hamano
Quoted 5 lines · Josh Steadmon
Quoted 3 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Quoted 5 lines
Quoted 2 lines
Rather than forking our own copy of clar, could we just add it as a submodule instead?
I doubt that forking was Patrick's intention to begin with. Convenience is.
Indeed. If we decide to adopt it I'd like to carry any required changes upstream.
Quoted 3 lines
It certainly is a possibility to bind it as a submodule, but it would be easier for everybody to futz with and experiment, at least while it is in its RFC phase.
I'm not too fond of using submodules, but I'm also not strictly against using them. I'll do whatever works best for folks here and adapt accordingly.
Patrick
RE: [RFC PATCH 1/3] t: import the clar unit testing framework
In reply to Josh SteadmonQuoted 6 lines · Josh Steadmon
Quoted 3 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Rather than forking our own copy of clar, could we just add it as a
submodule
Quoted 1 line
instead?
What are the requirements to build/use this?
Re: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 11 lines · On Wed, Jul 31, 2024 at 04:04:29PM -0400, [email protected] wrote:
Quoted 6 lines · Josh Steadmon
Quoted 3 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Rather than forking our own copy of clar, could we just add it as a
submodule
Quoted 1 line
instead?
What are the requirements to build/use this?
In its current form, Python is a dependency due to "generate.py". I mentioned elsewhere though that I'd be happy to port it to our language of choice.
Other than that it should only require a C89 compiler.
Patrick
RE: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 7 lines · Patrick Steinhardt
Quoted 6 lines · On Wed, Jul 31, 2024 at 04:04:29PM -0400, [email protected] wrote:
Quoted 5 lines · Josh Steadmon
Quoted 3 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
Rather than forking our own copy of clar, could we just add it as a
submodule
Quoted 1 line
instead?
What are the requirements to build/use this?
In its current form, Python is a dependency due to "
generate.py". I
mentioned
Quoted 3 lines
elsewhere though that I'd be happy to port it to our language of choice.
Other than that it should only require a C89 compiler.
Unfortunately, it needs gcc, but that is just because of its Makefile using cc -Wall (not portable).
RE: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 14 lines · On Thursday, August 1, 2024 8:15 AM, I wrote:
Quoted 13 lines · Patrick Steinhardt
Quoted 11 lines · On Wed, Jul 31, 2024 at 04:04:29PM -0400, [email protected] wrote:
Quoted 6 lines · Josh Steadmon
Quoted 3 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Rather than forking our own copy of clar, could we just add it as a
submodule
Quoted 1 line
instead?
What are the requirements to build/use this?
Quoted 7 lines
Quoted 1 line
In its current form, Python is a dependency due to "
generate.py". Imentioned
Quoted 3 lines
elsewhere though that I'd be happy to port it to our language of choice.
Other than that it should only require a C89 compiler.
Unfortunately, it needs gcc, but that is just because of its
Makefileusing
cc -Wall
Quoted 1 line
(not portable).
It looks like clar needs CC=c99 when running generate.py, which is fine. But there is no current way to
suppress -Wall without hand-modifying clar/test/Makefile. The options like that should either be in
CFLAGS?=... instead of CFLAGS=... or we could introduce CFLAGS_DBG?= and put -Wall in there - it is
only for reporting warnings which is done different in the c99 I have. Ideally, the CFLAGS should come
from ./git/config.mak.uname/
Re: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 25 lines · On Thu, Aug 01, 2024 at 08:54:36AM -0400, [email protected] wrote:
Quoted 14 lines · On Thursday, August 1, 2024 8:15 AM, I wrote:
Quoted 13 lines · Patrick Steinhardt
Quoted 11 lines · On Wed, Jul 31, 2024 at 04:04:29PM -0400, [email protected] wrote:
Quoted 6 lines · Josh Steadmon
Quoted 3 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Rather than forking our own copy of clar, could we just add it as a
submodule
Quoted 1 line
instead?
What are the requirements to build/use this?
Quoted 7 lines
Quoted 1 line
In its current form, Python is a dependency due to "
generate.py". Imentioned
Quoted 3 lines
elsewhere though that I'd be happy to port it to our language of choice.
Other than that it should only require a C89 compiler.
Unfortunately, it needs gcc, but that is just because of its
Makefileusingcc -Wall
Quoted 1 line
(not portable).
Quoted 9 lines
It looks like clar needs CC=c99 when running
generate.py, which is fine. But there is no current way to
suppress -Wall without hand-modifying clar/test/Makefile. The options like that should either be in
CFLAGS?=... instead of CFLAGS=... or we could introduce CFLAGS_DBG?= and put -Wall in there - it is
only for reporting warnings which is done different in the c99 I have. Ideally, the CFLAGS should come
from ./git/config.mak.uname/
As mentioned in another mail, we do not use its Makefile at all. Did you check whether the version I have proposed here works when running make test?
Patrick
RE: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 12 lines · Patrick Steinhardt
Quoted 11 lines · On Thu, Aug 01, 2024 at 08:54:36AM -0400, [email protected] wrote:
Quoted 10 lines · On Thursday, August 1, 2024 8:15 AM, I wrote:
Quoted 9 lines · Patrick Steinhardt
Quoted 8 lines · On Wed, Jul 31, 2024 at 04:04:29PM -0400, [email protected] wrote:
Quoted 7 lines · Josh Steadmon
Quoted 3 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Rather than forking our own copy of clar, could we just add it as a
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
submodule
Quoted 1 line
instead?
What are the requirements to build/use this?
In its current form, Python is a dependency due to "
generate.py". Imentioned
Quoted 1 line
elsewhere though that I'd be happy to port it to our language of
choice.
Quoted 17 lines
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Other than that it should only require a C89 compiler.
Unfortunately, it needs gcc, but that is just because of its
Makefileusingcc -Wall
Quoted 1 line
(not portable).
Quoted 8 lines
It looks like clar needs CC=c99 when running
generate.py, which is fine. But there is no current way to suppress -Wall without hand-modifying clar/test/Makefile. The options like that should either be in CFLAGS?=... instead of CFLAGS=... or we could introduce CFLAGS_DBG?= and put -Wall in there - it is only for reporting warnings which is done different in the c99 I have. Ideally, the CFLAGS should come
from ./git/config.mak.uname/
Quoted 1 line
As mentioned in another mail, we do not use its
Makefileat all. Did you
check
Quoted 1 line
whether the version I have proposed here works when running
make test?
That is the commit I have been trying to use. make test in clar or git?
Re: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 6 lines · On Thu, Aug 01, 2024 at 09:47:38AM -0400, [email protected] wrote:
Quoted 1 line · Patrick Steinhardt
As mentioned in another mail, we do not use its
Makefileat all. Did youcheck
Quoted 1 line
whether the version I have proposed here works when running
make test?That is the commit I have been trying to use. make test in clar or git?
In Git itself. make test builds and runs our unit tests, and that now also includes unit tests based on clar with this patch series. The clar Makefile exists only because I did a 1:1 import of the upstream dependency. We could just as well remove it altogether, including other bits that we don't end up using.
Patrick
RE: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 10 lines · Patrick Steinhardt
Quoted 7 lines · On Thu, Aug 01, 2024 at 09:47:38AM -0400, [email protected] wrote:
Quoted 2 lines · Patrick Steinhardt
As mentioned in another mail, we do not use its
Makefileat all. Did youcheck
Quoted 1 line
whether the version I have proposed here works when running
make test?That is the commit I have been trying to use. make test in clar or git?
In Git itself.
make testbuilds and runs our unit tests, and that now
also includes
Quoted 1 line
unit tests based on clar with this patch series. The clar
Makefileexists
only because I
Quoted 1 line
did a 1:1 import of the upstream dependency. We could just as well remove
it
Quoted 1 line
altogether, including other bits that we don't end up using.
I see. Well, the 2.46.0 test passes. I ran 'seen' 5 days ago and 'next' is going now. Would that catch it?
Re: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 12 lines · On Thu, Aug 01, 2024 at 09:53:52AM -0400, [email protected] wrote:
Quoted 10 lines · Patrick Steinhardt
Quoted 7 lines · On Thu, Aug 01, 2024 at 09:47:38AM -0400, [email protected] wrote:
Quoted 2 lines · Patrick Steinhardt
As mentioned in another mail, we do not use its
Makefileat all. Did youcheck
Quoted 1 line
whether the version I have proposed here works when running
make test?That is the commit I have been trying to use. make test in clar or git?
In Git itself.
make testbuilds and runs our unit tests, and that nowalso includes
Quoted 8 lines
Quoted 1 line
unit tests based on clar with this patch series. The clar
Makefileexistsonly because I
Quoted 1 line
did a 1:1 import of the upstream dependency. We could just as well remove
it
Quoted 1 line
altogether, including other bits that we don't end up using.
I see. Well, the 2.46.0 test passes. I ran 'seen' 5 days ago and 'next' is going now. Would that catch it?
No, as the patches in this thread are only up for discussion right now and have not been merged to any of the branches. You'd have to apply them on top of v2.46.0 first :)
Patrick
RE: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 8 lines · Patrick Steinhardt
Quoted 7 lines · On Thu, Aug 01, 2024 at 09:53:52AM -0400, [email protected] wrote:
Quoted 6 lines · Patrick Steinhardt
Quoted 5 lines · On Thu, Aug 01, 2024 at 09:47:38AM -0400, [email protected] wrote:
Quoted 2 lines · Patrick Steinhardt
As mentioned in another mail, we do not use its
Makefileat all. Did youcheck
Quoted 1 line
whether the version I have proposed here works when running `make
test`?
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
That is the commit I have been trying to use. make test in clar or
git?
Quoted 9 lines
Quoted 9 lines
Quoted 3 lines
In Git itself.
make testbuilds and runs our unit tests, and that nowalso includes
Quoted 2 lines
unit tests based on clar with this patch series. The clar
Makefileexistsonly because I
Quoted 2 lines
did a 1:1 import of the upstream dependency. We could just as well remove
Quoted 7 lines
Quoted 5 lines
it
Quoted 1 line
altogether, including other bits that we don't end up using.
I see. Well, the 2.46.0 test passes. I ran 'seen' 5 days ago and 'next' is going now. Would that catch it?
No, as the patches in this thread are only up for discussion right now and
have not
Quoted 1 line
been merged to any of the branches. You'd have to apply them on top of
v2.46.0
Quoted 1 line
first :)
Do you happen to have a public fork?
Regards,
Randall
Re: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 28 lines · On Thu, Aug 01, 2024 at 10:04:37AM -0400, [email protected] wrote:
Quoted 8 lines · Patrick Steinhardt
Quoted 7 lines · On Thu, Aug 01, 2024 at 09:53:52AM -0400, [email protected] wrote:
Quoted 6 lines · Patrick Steinhardt
Quoted 5 lines · On Thu, Aug 01, 2024 at 09:47:38AM -0400, [email protected] wrote:
Quoted 2 lines · Patrick Steinhardt
As mentioned in another mail, we do not use its
Makefileat all. Did youcheck
Quoted 1 line
whether the version I have proposed here works when running `make
test`?
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
That is the commit I have been trying to use. make test in clar or
git?
Quoted 15 lines
Quoted 14 lines
Quoted 3 lines
In Git itself.
make testbuilds and runs our unit tests, and that nowalso includes
Quoted 2 lines
unit tests based on clar with this patch series. The clar
Makefileexistsonly because I
Quoted 2 lines
did a 1:1 import of the upstream dependency. We could just as well remove
it
Quoted 1 line
altogether, including other bits that we don't end up using.
I see. Well, the 2.46.0 test passes. I ran 'seen' 5 days ago and 'next' is going now. Would that catch it?
Quoted 7 lines
Quoted 1 line
No, as the patches in this thread are only up for discussion right now and
have not
Quoted 1 line
been merged to any of the branches. You'd have to apply them on top of
v2.46.0
Quoted 1 line
first :)
Do you happen to have a public fork?
You can pull the branch pks-clar-unit-tests from
https://gitlab.com/gitlab-org/git.
Patrick
RE: [RFC PATCH 1/3] t: import the clar unit testing framework
Quoted 35 lines · Patrick Steinhardt
Quoted 4 lines · On Thu, Aug 01, 2024 at 10:04:37AM -0400, [email protected] wrote:
Quoted 3 lines · Patrick Steinhardt
Quoted 2 lines · On Thu, Aug 01, 2024 at 09:53:52AM -0400, [email protected] wrote:
Quoted 1 line · Patrick Steinhardt
On Thu, Aug 01, 2024 at 09:47:38AM -0400, [email protected]
Quoted 29 lines · wrote:
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines · Patrick Steinhardt
Quoted 2 lines
As mentioned in another mail, we do not use its
Makefileat all. Did youcheck
Quoted 2 lines
whether the version I have proposed here works when running `make
test`?
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
That is the commit I have been trying to use. make test in clar or
git?
Quoted 9 lines
Quoted 9 lines
Quoted 3 lines
In Git itself.
make testbuilds and runs our unit tests, and that nowalso includes
Quoted 2 lines
unit tests based on clar with this patch series. The clar
Makefileexistsonly because I
Quoted 2 lines
did a 1:1 import of the upstream dependency. We could just as well remove
Quoted 8 lines
Quoted 5 lines
it
Quoted 1 line
altogether, including other bits that we don't end up using.
I see. Well, the 2.46.0 test passes. I ran 'seen' 5 days ago and 'next' is going now. Would that catch it?
No, as the patches in this thread are only up for discussion right now and
have not
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
been merged to any of the branches. You'd have to apply them on top of
v2.46.0
Quoted 1 line
first :)
Do you happen to have a public fork?
You can pull the branch pks-clar-unit-tests from
https://gitlab.com/gitlab-org/git.
Two issues so far:
CC t/unit-tests/clar/clar.o
assert(!"Unexpected commandline argument!");
^
"/home/randall/git-clar/t/unit-tests/clar/clar.c", line 547: warning(317):
controlling expression is constant
if (mkdtemp(_clar_path) == NULL)
^"/home/randall/git-clar/t/unit-tests/clar/clar/sandbox.h", line 132:
error(114):
identifier "mkdtemp" is undefined
mkdtemp is not portable. assert was not #included. Thoughts?
RE: [RFC PATCH 1/3] t: import the clar unit testing framework
In reply to Patrick SteinhardtQuoted 35 lines · Patrick Steinhardt
Quoted 4 lines · On Thu, Aug 01, 2024 at 10:04:37AM -0400, [email protected] wrote:
Quoted 3 lines · Patrick Steinhardt
Quoted 2 lines · On Thu, Aug 01, 2024 at 09:53:52AM -0400, [email protected] wrote:
Quoted 1 line · Patrick Steinhardt
On Thu, Aug 01, 2024 at 09:47:38AM -0400, [email protected]
Quoted 29 lines · wrote:
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines · Patrick Steinhardt
Quoted 2 lines
As mentioned in another mail, we do not use its
Makefileat all. Did youcheck
Quoted 2 lines
whether the version I have proposed here works when running `make
test`?
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
That is the commit I have been trying to use. make test in clar or
git?
Quoted 9 lines
Quoted 9 lines
Quoted 3 lines
In Git itself.
make testbuilds and runs our unit tests, and that nowalso includes
Quoted 2 lines
unit tests based on clar with this patch series. The clar
Makefileexistsonly because I
Quoted 2 lines
did a 1:1 import of the upstream dependency. We could just as well remove
Quoted 8 lines
Quoted 5 lines
it
Quoted 1 line
altogether, including other bits that we don't end up using.
I see. Well, the 2.46.0 test passes. I ran 'seen' 5 days ago and 'next' is going now. Would that catch it?
No, as the patches in this thread are only up for discussion right now and
have not
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
been merged to any of the branches. You'd have to apply them on top of
v2.46.0
Quoted 1 line
first :)
Do you happen to have a public fork?
You can pull the branch pks-clar-unit-tests from
https://gitlab.com/gitlab-org/git.
Fix #1:
(quoted diff) +0 -0
b/t/unit-tests/clar/clar/sandbox.h
index 0ba1479620..81a40f26b8 100644
t/unit-tests/clar/clar/sandbox.h +1 -1
@@ -116,7 +116,7 @@ static int build_sandbox_path(void)strncpy (_clar_path + len, path_tail,sizeof (_clar_path) - len); -#if defined(__MINGW32__) +#if defined(__MINGW32__) || defined(__TANDEM)if (_mktemp (_clar_path) ==NULL )return -1 ;
That gets past the clar build problem. Probably more to come as I encounter issues. The assert issue was just a warning about a constant expression that will always fail.
RE: [RFC PATCH 1/3] t: import the clar unit testing framework (better one)
In reply to Patrick SteinhardtQuoted 35 lines · Patrick Steinhardt
Quoted 4 lines · On Thu, Aug 01, 2024 at 10:04:37AM -0400, [email protected] wrote:
Quoted 3 lines · Patrick Steinhardt
Quoted 2 lines · On Thu, Aug 01, 2024 at 09:53:52AM -0400, [email protected] wrote:
Quoted 1 line · Patrick Steinhardt
On Thu, Aug 01, 2024 at 09:47:38AM -0400, [email protected]
Quoted 29 lines · wrote:
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines · Patrick Steinhardt
Quoted 2 lines
As mentioned in another mail, we do not use its
Makefileat all. Did youcheck
Quoted 2 lines
whether the version I have proposed here works when running `make
test`?
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
That is the commit I have been trying to use. make test in clar or
git?
Quoted 9 lines
Quoted 9 lines
Quoted 3 lines
In Git itself.
make testbuilds and runs our unit tests, and that nowalso includes
Quoted 2 lines
unit tests based on clar with this patch series. The clar
Makefileexistsonly because I
Quoted 2 lines
did a 1:1 import of the upstream dependency. We could just as well remove
Quoted 8 lines
Quoted 5 lines
it
Quoted 1 line
altogether, including other bits that we don't end up using.
I see. Well, the 2.46.0 test passes. I ran 'seen' 5 days ago and 'next' is going now. Would that catch it?
No, as the patches in this thread are only up for discussion right now and
have not
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
been merged to any of the branches. You'd have to apply them on top of
v2.46.0
Quoted 1 line
first :)
Do you happen to have a public fork?
You can pull the branch pks-clar-unit-tests from
https://gitlab.com/gitlab-org/git.
This fix is better (and actually works unlike the previous):
(quoted diff) +0 -0
b/t/unit-tests/clar/clar/sandbox.h
index 0ba1479620..1b14dbfd76 100644
t/unit-tests/clar/clar/sandbox.h +6 -0
@@ -120,6 +120,12 @@ static int build_sandbox_path(void)if (_mktemp (_clar_path) ==NULL )return -1 ; +if (mkdir (_clar_path,0700 ) !=0 ) +return -1 ; +#elif defined(__TANDEM) +if (mktemp (_clar_path) ==NULL ) +return -1 ; +if (mkdir (_clar_path,0700 ) !=0 )return -1 ;#elif defined(_WIN32)
This is based on the above repo/branch. Basically a separate block for __TANDEM for temp directories. Build is done. I'm running the full test now and may report more or success.
Regards,
Randall
Re: [RFC PATCH 1/3] t: import the clar unit testing framework (better one)
Am 01.08.24 um 19:43 schrieb [email protected]:
Quoted 2 lines
I'm running the full test now and may report more or success.
"make unit-tests" would suffice.
René
RE: [RFC PATCH 1/3] t: import the clar unit testing framework (better one)
Quoted 5 lines · René Scharfe
Am 01.08.24 um 19:43 schrieb [email protected]:
Quoted 2 lines
I'm running the full test now and may report more or success.
"make unit-tests" would suffice.
Well, that takes a whole lot less time, thanks. 100% pass after the prior fix was applied.
Thank you.
Randall
RFC PATCH 2/3 Makefile: wire up the clar unit testing framework
In reply to Patrick SteinhardtWire up the clar unit testing framework by introducing a new "unit-tests" executable. In contrast to the existing framework, this will result in a single executable for all test suites. The ability to pick specific tests to execute is retained via functionality built into the clar itself.
Note that we need to be a bit careful about how we need to invalidate our Makefile rules. While we obviously have to regenerate the clar suite when our test suites change, we also have to invalidate it in case any of the test suites gets removed. We do so by using our typical pattern of creating a GIT-TEST-SUITES file that gets updated whenever the set of test suites changes, so that we can easily depend on that file.
Another specialty is that we generate a "clar-decls.h" file. The test functions are neither static, nor do they have external declarations. This is because they are getting parsed via "generate.py", which then creates the external generations that get populated into an array. These declarations are only seen by the main function though.
The consequence is that we will get a bunch of "missing prototypes" errors from our compiler for each of these test functions. To fix those errors, we extract the extern declarations from "clar.suite" and put them into a standalone header that then gets included by each of our unit tests. This gets rid of compiler warnings for every function which has been extracted by "generate.py". More importantly though, it does _not_ get rid of warnings in case a function really isn't being used by anything. Thus, it would cause a compiler error if a function name was mistyped and thus not picked up by "generate.py".
--- .gitignore | 1 + Makefile | 34 ++++++++++++++++++++++++++++++---- t/Makefile | 1 + t/unit-tests/.gitignore | 3 +++ t/unit-tests/unit-test.c | 16 ++++++++++++++++ t/unit-tests/unit-test.h | 3 +++ 6 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 t/unit-tests/unit-test.c create mode 100644 t/unit-tests/unit-test.h
.gitignore +1 -0
@@ -9,6 +9,7 @@ /GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES /GIT-SPATCH-DEFINES +/GIT-TEST-SUITES /GIT-USER-AGENT /GIT-VERSION-FILE /bin-wrappers/
Makefile +30 -4
@@ -1332,6 +1332,11 @@ THIRD_PARTY_SOURCES += compat/regex/% THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% +UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) +UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/unit-test.o + UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate UNIT_TEST_PROGRAMS += t-hash
@@ -2711,6 +2716,7 @@ OBJECTS += $(XDIFF_OBJS) OBJECTS += $(FUZZ_OBJS) OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS) OBJECTS += $(UNIT_TEST_OBJS) +OBJECTS += $(UNIT_TESTS_OBJS) ifndef NO_CURL OBJECTS += http.o http-walker.o remote-curl.o
@@ -3213,7 +3219,7 @@ endif test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X)) -all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) +all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) bin-wrappers/%: wrap-for-bin.sh $(call mkdir_p_parent_template)
@@ -3644,7 +3650,7 @@ endif artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \ GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \ - $(UNIT_TEST_PROGS) $(MOFILES) + $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) $(MOFILES) $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \ SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)' test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3700,6 +3706,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) -r .build $(UNIT_TEST_BIN) + $(RM) $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/.clarcache $(RM) po/git.pot po/git-core.pot $(RM) git.res $(RM) $(OBJECTS)
@@ -3859,7 +3866,26 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) +GIT-TEST-SUITES: FORCE + @FLAGS='$(UNIT_TESTS_SUITES)'; \ + if test x"$$FLAGS" != x"`cat GIT-TEST-SUITES 2>/dev/null`" ; then \ + echo >&2 " * new test suites"; \ + echo "$$FLAGS" >GIT-TEST-SUITES; \ + rm -f $(UNIT_TESTS_DIR)/.clarcache; \ + fi + +$(UNIT_TEST_DIR)/clar.suite: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES + $(QUIET_GEN)$(UNIT_TEST_DIR)/clar/generate.py $(UNIT_TEST_DIR) >/dev/null + @touch $@ +$(UNIT_TEST_DIR)/clar-decls.h: $(UNIT_TEST_DIR)/clar.suite + $(QUIET_GEN)grep '^extern void' $^ >$@ +$(UNIT_TESTS_OBJS): $(UNIT_TEST_DIR)/clar-decls.h +$(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR) -I$(UNIT_TEST_DIR)/clar +$(UNIT_TESTS_PROG): $(UNIT_TEST_DIR)/clar.suite $(UNIT_TESTS_OBJS) $(GITLIBS) GIT-LDFLAGS + $(call mkdir_p_parent_template) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) + .PHONY: build-unit-tests unit-tests -build-unit-tests: $(UNIT_TEST_PROGS) -unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X +build-unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) +unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) t/helper/test-tool$X $(MAKE) -C t/ unit-tests
t/Makefile +1 -0
@@ -48,6 +48,7 @@ CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.tes CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c) UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES)) +UNIT_TEST_PROGRAMS += unit-tests/bin/unit-tests$(X) UNIT_TESTS = $(sort $(UNIT_TEST_PROGRAMS)) UNIT_TESTS_NO_DIR = $(notdir $(UNIT_TESTS))
t/unit-tests/.gitignore +3 -0
@@ -1 +1,4 @@ /bin +/.clarcache +/clar.suite +/clar-decls.h
t/unit-tests/unit-test.c new file +16 -0
@@ -0,0 +1,16 @@ +#include "unit-test.h" + +int cmd_main (int argc,const char **argv) +{ +const char **args; +int ret; + +/* Append the "-t" flag such that the tests generate TAP output. */ +DUP_ARRAY (args, argv, argc +1 ); + args[argc++] ="-t" ; + + ret =clar_test (argc, (char **) args); + +free (args); +return ret; +}
t/unit-tests/unit-test.h new file +3 -0
@@ -0,0 +1,3 @@ +#include "git-compat-util.h" +#include "clar.h" +#include "clar-decls.h"
Signature · 1 line
2.46.0.dirty
Re: [RFC PATCH 2/3] Makefile: wire up the clar unit testing framework
Am 31.07.24 um 11:04 schrieb Patrick Steinhardt:
Quoted 31 lines
Wire up the clar unit testing framework by introducing a new "unit-tests" executable. In contrast to the existing framework, this will result in a single executable for all test suites. The ability to pick specific tests to execute is retained via functionality built into the clar itself.
Note that we need to be a bit careful about how we need to invalidate our
Makefilerules. While we obviously have to regenerate the clar suite when our test suites change, we also have to invalidate it in case any of the test suites gets removed. We do so by using our typical pattern of creating aGIT-TEST-SUITESfile that gets updated whenever the set of test suites changes, so that we can easily depend on that file.Another specialty is that we generate a "
clar-decls.h" file. The test functions are neither static, nor do they have external declarations. This is because they are getting parsed via "generate.py", which then creates the external generations that get populated into an array. These declarations are only seen by the main function though.The consequence is that we will get a bunch of "missing prototypes" errors from our compiler for each of these test functions. To fix those errors, we extract the
externdeclarations from "clar.suite" and put them into a standalone header that then gets included by each of our unit tests. This gets rid of compiler warnings for every function which has been extracted by "generate.py". More importantly though, it does _not_ get rid of warnings in case a function really isn't being used by anything. Thus, it would cause a compiler error if a function name was mistyped and thus not picked up by "generate.py".+$(UNIT_TEST_DIR)/clar.suite: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES
Review
Quoted 1 line
(quoted diff) +1
-0+ $(QUIET_GEN)$(UNIT_TEST_DIR)/clar/generate.py $(UNIT_TEST_DIR) >/dev/null
This uses the Python interpreter from the shebang line in generate.py, which is python. On my system I only have python3 and python3.12, but not python. Easily fixed, of course, but a way to configure the interpreter name would be nice.
This gave me extra motivation to come up with the clunky patch below to replace Python with sed and awk. That and your statement that clar doesn't have to be perfect in the other thread. ;)
It reverses the order of dependencies (builds clar-decls.h first), not sure if that has downsides. And the sed pattern is simpler than the one in generate.py, just out of laziness.
René
--- .gitignore | 1 - Makefile | 20 ++++++----------- t/unit-tests/.gitignore | 1 - t/unit-tests/generate.awk | 47 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 t/unit-tests/generate.awk
.gitignore +0 -1
@@ -9,7 +9,6 @@ /GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES /GIT-SPATCH-DEFINES -/GIT-TEST-SUITES /GIT-USER-AGENT /GIT-VERSION-FILE /bin-wrappers/
Makefile +7 -13
@@ -3706,7 +3706,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) -r .build $(UNIT_TEST_BIN) - $(RM) $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/.clarcache + $(RM) $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/clar-decls.h $(RM) po/git.pot po/git-core.pot $(RM) git.res $(RM) $(OBJECTS)
@@ -3866,19 +3866,13 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) -GIT-TEST-SUITES: FORCE - @FLAGS='$(UNIT_TESTS_SUITES)'; \ - if test x"$$FLAGS" != x"`cat GIT-TEST-SUITES 2>/dev/null`" ; then \ - echo >&2 " * new test suites"; \ - echo "$$FLAGS" >GIT-TEST-SUITES; \ - rm -f $(UNIT_TESTS_DIR)/.clarcache; \ - fi +$(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h $(UNIT_TEST_DIR)/generate.awk + $(QUIET_GEN)awk -f $(UNIT_TEST_DIR)/generate.awk $(UNIT_TEST_DIR)/clar-decls.h >$@ +$(UNIT_TEST_DIR)/clar-decls.h: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) + $(QUIET_GEN)for suite in $(UNIT_TESTS_SUITES); do \ + sed -ne "s/^\(void test_$${suite}__[a-zA-Z_0-9][a-zA-Z_0-9]*(void)$$\)/extern \1;/p" $(UNIT_TEST_DIR)/$$suite.c; \ + done >$@ -$(UNIT_TEST_DIR)/clar.suite: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES - $(QUIET_GEN)$(UNIT_TEST_DIR)/clar/generate.py $(UNIT_TEST_DIR) >/dev/null - @touch $@ -$(UNIT_TEST_DIR)/clar-decls.h: $(UNIT_TEST_DIR)/clar.suite - $(QUIET_GEN)grep '^extern void' $^ >$@ $(UNIT_TESTS_OBJS): $(UNIT_TEST_DIR)/clar-decls.h $(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR) -I$(UNIT_TEST_DIR)/clar $(UNIT_TESTS_PROG): $(UNIT_TEST_DIR)/clar.suite $(UNIT_TESTS_OBJS) $(GITLIBS) GIT-LDFLAGS
t/unit-tests/.gitignore +0 -1
@@ -1,4 +1,3 @@ /bin -/.clarcache /clar.suite /clar-decls.h
t/unit-tests/generate.awk new file +47 -0
47 lines not on this page · raw message
--
2.46.0
Re: [RFC PATCH 2/3] Makefile: wire up the clar unit testing framework
Quoted 39 lines · René Scharfe
Am 31.07.24 um 11:04 schrieb Patrick Steinhardt:
Quoted 31 lines
Wire up the clar unit testing framework by introducing a new "unit-tests" executable. In contrast to the existing framework, this will result in a single executable for all test suites. The ability to pick specific tests to execute is retained via functionality built into the clar itself.
Note that we need to be a bit careful about how we need to invalidate our
Makefilerules. While we obviously have to regenerate the clar suite when our test suites change, we also have to invalidate it in case any of the test suites gets removed. We do so by using our typical pattern of creating aGIT-TEST-SUITESfile that gets updated whenever the set of test suites changes, so that we can easily depend on that file.Another specialty is that we generate a "
clar-decls.h" file. The test functions are neither static, nor do they have external declarations. This is because they are getting parsed via "generate.py", which then creates the external generations that get populated into an array. These declarations are only seen by the main function though.The consequence is that we will get a bunch of "missing prototypes" errors from our compiler for each of these test functions. To fix those errors, we extract the
externdeclarations from "clar.suite" and put them into a standalone header that then gets included by each of our unit tests. This gets rid of compiler warnings for every function which has been extracted by "generate.py". More importantly though, it does _not_ get rid of warnings in case a function really isn't being used by anything. Thus, it would cause a compiler error if a function name was mistyped and thus not picked up by "generate.py".+$(UNIT_TEST_DIR)/clar.suite: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES
Review
Quoted 1 line
(quoted diff) +1
-0+ $(QUIET_GEN)$(UNIT_TEST_DIR)/clar/generate.py $(UNIT_TEST_DIR) >/dev/nullThis uses the Python interpreter from the shebang line in
generate.py, which is python. On my system I only have python3 and python3.12, but not python. Easily fixed, of course, but a way to configure the interpreter name would be nice.
Quoted 3 lines
This gave me extra motivation to come up with the clunky patch below to replace Python with sed and awk. That and your statement that clar doesn't have to be perfect in the other thread. ;)
Neat! I would certainly prefer to not have a dependency on Python, and I think awk(1) is a good alternative here that we already require anyway.
Also, another benefit of having our own script is that it allows us to be more flexible with how exactly our tests are structured.
Quoted 3 lines
It reverses the order of dependencies (builds
clar-decls.hfirst), not sure if that has downsides. And the sed pattern is simpler than the one ingenerate.py, just out of laziness.
We could even integrate the generation of clar-decls.h with the AWK script.
I'll play around a bit with what you have, thanks a lot for working on it!
Patrick
Re: [RFC PATCH 2/3] Makefile: wire up the clar unit testing framework
In reply to Patrick SteinhardtA trivial fix-up to be squashed into this step.
Makefile | 1 + 1 file changed, 1 insertion(+)
Makefile +1 -0
@@ -3735,6 +3735,7 @@ ifndef NO_TCLTK $(MAKE) -C git-gui clean endif $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS + $(RM) GIT-TEST-SUITES $(UNIT_TEST_DIR)/clar-decls.h $(RM) GIT-USER-AGENT GIT-PREFIX $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS ifdef MSVC
Re: [RFC PATCH 2/3] Makefile: wire up the clar unit testing framework
Quoted 1 line · Junio C Hamano
A trivial fix-up to be squashed into this step.
I won't be able to address this today, but "make sparse" and other auxiliary targets also seem to break, due to their lack of dependence on the generated clar.suite file.
Re: [RFC PATCH 2/3] Makefile: wire up the clar unit testing framework
Quoted 7 lines · Junio C Hamano
Quoted 1 line · Junio C Hamano
A trivial fix-up to be squashed into this step.
I won't be able to address this today, but "make sparse" and other auxiliary targets also seem to break, due to their lack of dependence on the generated clar.suite file.
Yeah, as said in the cover letter, this was in quite an unpolished state, so it is not surprising at all that things break. I just wanted to provide an unfinished PoC to demonstrate how things would roughly look like so that we have something to discuss. Definitely not material for seen yet.
Given that initial feedback didn't seem to be all that negative I'm happy to polish things a bit more.
Patrick
RFC PATCH 3/3 t/unit-tests: convert strvec tests to use clar
In reply to Patrick SteinhardtConvert the strvec tests to use the new clar unit testing framework. This is a first test balloon that demonstrates how the testing infra for clar-based tests looks like.
The tests are part of the "t/unit-tests/bin/unit-tests" binary. When running that binary, it generates TAP output:
# ./t/unit-tests/bin/unit-tests
TAP version 13
# start of suite 1: strvec
ok 1 - strvec::init
ok 2 - strvec::dynamic_init
ok 3 - strvec::clear
ok 4 - strvec::push
ok 5 - strvec::pushft_pushf
ok 6 - strvec::pushl
ok 7 - strvec::pushv
ok 8 - strvec::replace_at_head
ok 9 - strvec::replace_at_tail
ok 10 - strvec::replace_in_between
ok 11 - strvec::replace_with_substring
ok 12 - strvec::remove_at_head
ok 13 - strvec::remove_at_tail
ok 14 - strvec::remove_in_between
ok 15 - strvec::pop_empty_array
ok 16 - strvec::pop_non_empty_array
ok 17 - strvec::split_empty_string
ok 18 - strvec::split_single_item
ok 19 - strvec::split_multiple_items
ok 20 - strvec::split_whitespace_only
ok 21 - strvec::split_multiple_consecutive_whitespaces
ok 22 - strvec::detach
1..22The binary also supports some parameters that allow us to run only a subset of unit tests or alter the output:
$ ./t/unit-tests/bin/unit-tests -h
Usage: ./t/unit-tests/bin/unit-tests [options] Options:
-sname Run only the suite with name (can go to individual test name)
-iname Include the suite with name
-xname Exclude the suite with name
-v Increase verbosity (show suite names)
-q Only report tests that had an error
-Q Quit as soon as a test fails
-t Display results in tap format
-l Print suite names
-r[filename] Write summary file (to the optional filename)
Furthermore, running make unit-tests runs the binary along with all the other unit tests we have.
---
Makefile | 2 +-
t/unit-tests/{t-strvec.c => strvec.c} | 124 ++++++++++----------------
2 files changed, 46 insertions(+), 80 deletions(-)
rename t/unit-tests/{t-strvec.c => strvec.c} (54%)Makefile +1 -1
@@ -1332,6 +1332,7 @@ THIRD_PARTY_SOURCES += compat/regex/% THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% +UNIT_TESTS_SUITES += strvec UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
@@ -1348,7 +1349,6 @@ UNIT_TEST_PROGRAMS += t-reftable-basics UNIT_TEST_PROGRAMS += t-reftable-record UNIT_TEST_PROGRAMS += t-strbuf UNIT_TEST_PROGRAMS += t-strcmp-offset -UNIT_TEST_PROGRAMS += t-strvec UNIT_TEST_PROGRAMS += t-trailer UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS)) UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
t/unit-tests/strvec.c renamed from t/unit-tests/t-strvec.c +45 -79
276 lines not on this page · raw message
Signature · 1 line
2.46.0.dirty
Quoted 3 lines · Patrick Steinhardt
- The clar gives us the ability to pick which tests to run via command line parameters, which I personally like more than picking the specific binary to run.
One thing I am very unhappy about the current t/unit-tests/ is that the GIT_SKIP_TESTS mechanism is not effective at all. If we can wrap clar's test selection syntax inside t/Makefile to work with GIT_SKIP_TESTS (or its superset equivalent), that would be a great plus.
Quoted 4 lines
- The clar replaces some test assertions that we already have. They feel a bit more mature, but overall there aren't all that many assertions available. If we wanted to pick it up, then we'd likely have to add some more wrappers.
That is a slight bummer, as importing an externally developed one is with the hope that we won't have to enhance or maintain it, but we'll see how much burden it will be.
Quoted 11 lines
- The clar uses longjmp instead of manually having to
returnfrom functions in case there was an assertion failure. This is easier to work with in my opinion.Also, note that I only tested this on my Linux machine. I have no clue whether this works as-is on Windows, but I do know that libgit2 tests run on Linux, macOS and Windows. So it should work in theory, it's just a matter of polishing this series.
I'm happy to hear your thoughts on this, even if it ultimately ends up being shot down.
Thanks for getting the ball going. Let's see how fast and far it rolls.
Quoted 29 lines · Junio C Hamano
Quoted 3 lines · Patrick Steinhardt
- The clar gives us the ability to pick which tests to run via command line parameters, which I personally like more than picking the specific binary to run.
One thing I am very unhappy about the current t/unit-tests/ is that the
GIT_SKIP_TESTSmechanism is not effective at all. If we can wrap clar's test selection syntax inside t/Makefile to work withGIT_SKIP_TESTS(or its superset equivalent), that would be a great plus.Quoted 4 lines
- The clar replaces some test assertions that we already have. They feel a bit more mature, but overall there aren't all that many assertions available. If we wanted to pick it up, then we'd likely have to add some more wrappers.
That is a slight bummer, as importing an externally developed one is with the hope that we won't have to enhance or maintain it, but we'll see how much burden it will be.
Quoted 9 lines
- The clar uses longjmp instead of manually having to
returnfrom functions in case there was an assertion failure. This is easier to work with in my opinion.Also, note that I only tested this on my Linux machine. I have no clue whether this works as-is on Windows, but I do know that libgit2 tests run on Linux, macOS and Windows. So it should work in theory, it's just a matter of polishing this series.
Quoted 4 lines
Quoted 2 lines
I'm happy to hear your thoughts on this, even if it ultimately ends up being shot down.
Thanks for getting the ball going. Let's see how fast and far it rolls.
I'm sorry for being so behind the curve... what is clar and where does it run?
Quoted 1 line · <[email protected]> writes:
I'm sorry for being so behind the curve... what is clar and where does it run?
We have t/unit-test/test-lib.[ch] that are our home-grown unit test framework. A handful of tests have been written to use it, when you say "make test", or "(cd t && make)", unit tests binaries linked with the home-grown unit test framework run.
clar is a _potential_ replacement for our home-grown framework, suggested here because it would be nicer if we can use off-the-shelf component instead of having to enhance and maintain our own.
Where and how it runs does not change even after clar turns out to be good enough for our purpose and we commit to replace our home-grown unit test framework with it.
Quoted 8 lines · Junio C Hamano
Quoted 1 line · <[email protected]> writes:
I'm sorry for being so behind the curve... what is clar and where does it run?
We have t/unit-test/test-lib.[ch] that are our home-grown unit test framework. A handful of tests have been written to use it, when you say "make test", or "(cd t && make)", unit tests binaries linked with the home-grown unit test framework run.
Quoted 7 lines
clar is a _potential_ replacement for our home-grown framework, suggested here because it would be nicer if we can use off-the-shelf component instead of having to enhance and maintain our own.
Where and how it runs does not change even after clar turns out to be good enough for our purpose and we commit to replace our home-grown unit test framework with it.
Well... I would like to be able to see whether this can be built/used on NonStop just so I can stay ahead of the curve or be far enough in advance of it to request any required fixes to make it work on platform.
Quoted 14 lines · On Wednesday, July 31, 2024 4:25 PM, I wrote:
Quoted 7 lines · Junio C Hamano
Quoted 1 line · <[email protected]> writes:
I'm sorry for being so behind the curve... what is clar and where does it run?
We have t/unit-test/test-lib.[ch] that are our home-grown unit test framework. A handful of tests have been written to use it, when you say "make test", or "(cd t && make)", unit tests binaries linked with the home-
grown unit test framework run.
Quoted 5 lines
clar is a _potential_ replacement for our home-grown framework, suggested here because it would be nicer if we can use off-the-shelf component instead of having to enhance and maintain our own.
Quoted 7 lines
Quoted 3 lines
Where and how it runs does not change even after clar turns out to be good enough for our purpose and we commit to replace our home-grown unit test framework with it.
Well... I would like to be able to see whether this can be built/used on NonStop just so I can stay ahead of the curve or be far enough in advance of it to request any required fixes to make it work on platform.
After checking out clar, I would say it has potential, but needs to be slightly more portable to depend on more than gcc CFLAGS (specifically -Wall). I will ask that of that team. It does look like it might be possible, but it should integrate with the config.mak.uname settings, so c99 or c11 (or soon c17) could be used with other CFLAGS from that file so maintainers only have to worry about one location.
--Randall
Quoted 23 lines · On Wed, Jul 31, 2024 at 04:37:37PM -0400, [email protected] wrote:
Quoted 14 lines · On Wednesday, July 31, 2024 4:25 PM, I wrote:
Quoted 7 lines · Junio C Hamano
Quoted 1 line · <[email protected]> writes:
I'm sorry for being so behind the curve... what is clar and where does it run?
We have t/unit-test/test-lib.[ch] that are our home-grown unit test framework. A handful of tests have been written to use it, when you say "make test", or "(cd t && make)", unit tests binaries linked with the home-
grown unit test framework run.
Quoted 5 lines
clar is a _potential_ replacement for our home-grown framework, suggested here because it would be nicer if we can use off-the-shelf component instead of having to enhance and maintain our own.
Quoted 7 lines
Quoted 3 lines
Where and how it runs does not change even after clar turns out to be good enough for our purpose and we commit to replace our home-grown unit test framework with it.
Well... I would like to be able to see whether this can be built/used on NonStop just so I can stay ahead of the curve or be far enough in advance of it to request any required fixes to make it work on platform.
Quoted 5 lines
After checking out clar, I would say it has potential, but needs to be slightly more portable to depend on more than gcc CFLAGS (specifically -Wall). I will ask that of that team. It does look like it might be possible, but it should integrate with the config.mak.uname settings, so c99 or c11 (or soon c17) could be used with other CFLAGS from that file so maintainers only have to worry about one location.
I'm not sure I follow what you're saying. We don't use the Makefile of clar at all but integrate it into our own build system, so honoring config.mak.uname should come for free, I think.
In any case, if there are C constructs in clar that do not work right now, then I'd be happy to address those issues. The clar is overall quite small, so it shouldn't be all that involved.
Patrick
Quoted 11 lines · Junio C Hamano
Quoted 3 lines · Patrick Steinhardt
- The clar gives us the ability to pick which tests to run via command line parameters, which I personally like more than picking the specific binary to run.
One thing I am very unhappy about the current t/unit-tests/ is that the
GIT_SKIP_TESTSmechanism is not effective at all. If we can wrap clar's test selection syntax inside t/Makefile to work withGIT_SKIP_TESTS(or its superset equivalent), that would be a great plus.
Yeah, I guess that shouldn't be too hard.
Quoted 8 lines
Quoted 4 lines
- The clar replaces some test assertions that we already have. They feel a bit more mature, but overall there aren't all that many assertions available. If we wanted to pick it up, then we'd likely have to add some more wrappers.
That is a slight bummer, as importing an externally developed one is with the hope that we won't have to enhance or maintain it, but we'll see how much burden it will be.
The clar exposes generic helpers like cl_assert, but also comparison functions like cl_assert_equal_$t for pointers, integers and strings. If anything is missing, it also exposes the building blocks to add project-specific assertions so that it would be easy to add for example cl_assert_equal_oid.
One thing I have been missing is non-equality comparisons like cl_assert_lt_i/cl_assert_gt_i. But adding that to the clar itself and upstreaming it should be easy enough, also because we know the people maintaining it.
Other than that I think it's mostly fine and should serve as a good baseline.
Patrick
Quoted 31 lines · Patrick Steinhardt
Hi,
there's been some discussion around extending our unit testing framework to avoid duplication when declaring test functions. Right now, a testing function has to be declared and then wired up via the test's main function, which can be a bit annoying. In the thread, René proposes an alternative that gets rid of this duplication by using macros. And while that does solve the issue, there were some concerns about things being too much "magic" while at the same time not being flexible enough.
Part of the discussion revolved around whether we maybe want to have a proper unit testing framework in our codebase instead of reinventing the wheel. As I quite liked the "clar" [2] testing framework from back when I was still developing libgit2 I proposed it as a possible alternative. This patch series wires up the clar framework as a proof of concept and converts the strvec test suite to use it.
The magic to avoid the above code duplication is quite self-contained in a "
generate.py" file. This script extracts function declarations from all unit test suites and then writes those into a "clar.suite" header file. All that one needs to do is thus to declare a function with a specific name "test_<suite>__<name>" and then everything else gets wired up automatically.Whether this is better than the solution proposed by René is probably a matter of taste. While I'm not a huge fan of the macro-based solution, I don't want to veto it either (not that I'd have that power anyway). So please, you should rather read this as a proof of concept to see how alternatives could look like such that we have a better picture of where we want to end up.
Quoted 8 lines
Some random thoughts:
- The mandated Python dependency is suboptimal in my opinion. Rewriting the script in e.g. Perl should be easy enough though, it's no rocket science.
- I prefer that the proposed solution results in a single binary as compared to one binary per test system.
Does clar allow running test functions in parallel? With multiple binaries, we can at least run independent tests in parallel (although right now the unit tests are fewer and so much faster than the shell tests that it's hardly noticeable).
Quoted 3 lines
- The clar gives us the ability to pick which tests to run via command line parameters, which I personally like more than picking the specific binary to run.
Yes this is a nice improvement.
Quoted 9 lines
- The clar replaces some test assertions that we already have. They feel a bit more mature, but overall there aren't all that many assertions available. If we wanted to pick it up, then we'd likely have to add some more wrappers.
- The clar uses longjmp instead of manually having to
returnfrom functions in case there was an assertion failure. This is easier to work with in my opinion.
Quoted 7 lines
Also, note that I only tested this on my Linux machine. I have no clue whether this works as-is on Windows, but I do know that libgit2 tests run on Linux, macOS and Windows. So it should work in theory, it's just a matter of polishing this series.
I'm happy to hear your thoughts on this, even if it ultimately ends up being shot down.
As part of the original unit-test series, I wrote a comparison between different frameworks: Documentation/technical/unit-tests.txt, poorly rendered at [1]. Could you add a row to the table evaluating clar on the individual points there?
[1] https://git-scm.com/docs/unit-tests#framework-selection
Quoted 61 lines
Patrick
[1]: <[email protected]> [2]: https://github.com/clar-test/clar
Patrick Steinhardt (3):
t: import the clar unit testing framework
Makefile: wire up the clar unit testing framework t/unit-tests: convert strvec tests to use clar.gitignore | 1 + Makefile | 36 +- t/Makefile | 1 + t/unit-tests/.gitignore | 3 + t/unit-tests/clar/.github/workflows/ci.yml | 23 + t/unit-tests/clar/COPYING | 15 + t/unit-tests/clar/README.md | 329 ++++++++ t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++ t/unit-tests/clar/clar.h | 173 +++++ t/unit-tests/clar/clar/fixtures.h | 50 ++ t/unit-tests/clar/clar/fs.h | 522 +++++++++++++ t/unit-tests/clar/clar/print.h | 211 ++++++ t/unit-tests/clar/clar/sandbox.h | 154 ++++ t/unit-tests/clar/clar/summary.h | 143 ++++ t/unit-tests/clar/generate.py | 267 +++++++ t/unit-tests/clar/test/.gitignore | 5 + t/unit-tests/clar/test/Makefile | 39 + t/unit-tests/clar/test/clar_test.h | 16 + t/unit-tests/clar/test/main.c | 40 + t/unit-tests/clar/test/main.c.sample | 27 + t/unit-tests/clar/test/resources/test/file | 1 + t/unit-tests/clar/test/sample.c | 84 ++ t/unit-tests/{t-strvec.c => strvec.c} | 124 ++- t/unit-tests/unit-test.c | 16 + t/unit-tests/unit-test.h | 3 + 25 files changed, 3041 insertions(+), 84 deletions(-) create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml create mode 100644 t/unit-tests/clar/COPYING create mode 100644 t/unit-tests/clar/README.md create mode 100644 t/unit-tests/clar/clar.c create mode 100644 t/unit-tests/clar/clar.h create mode 100644 t/unit-tests/clar/clar/fixtures.h create mode 100644 t/unit-tests/clar/clar/fs.h create mode 100644 t/unit-tests/clar/clar/print.h create mode 100644 t/unit-tests/clar/clar/sandbox.h create mode 100644 t/unit-tests/clar/clar/summary.h create mode 100755 t/unit-tests/clar/generate.py create mode 100644 t/unit-tests/clar/test/.gitignore create mode 100644 t/unit-tests/clar/test/Makefile create mode 100644 t/unit-tests/clar/test/clar_test.h create mode 100644 t/unit-tests/clar/test/main.c create mode 100644 t/unit-tests/clar/test/main.c.sample create mode 100644 t/unit-tests/clar/test/resources/test/file create mode 100644 t/unit-tests/clar/test/sample.c rename t/unit-tests/{t-strvec.c => strvec.c} (54%) create mode 100644 t/unit-tests/unit-test.c create mode 100644 t/unit-tests/unit-test.hSignature · 1 line
2.46.0.dirty
Quoted 8 lines · Josh Steadmon
Quoted 2 lines · Patrick Steinhardt
- I prefer that the proposed solution results in a single binary as compared to one binary per test system.
Does clar allow running test functions in parallel? With multiple binaries, we can at least run independent tests in parallel (although right now the unit tests are fewer and so much faster than the shell tests that it's hardly noticeable).
Ah, that's something I didn't think of. clar does not support running tests in parallel.
As you say, I guess for now that is fine and I'd claim that it likely is faster to just run all tests sequentially with a single binary if you also include build times. If that claim isn't true, or if we eventually grow a huge body of tests, then we should likely revert to having separate binaries.
Quoted 6 lines
As part of the original unit-test series, I wrote a comparison between different frameworks:
Documentation/technical/unit-tests.txt, poorly rendered at [1]. Could you add a row to the table evaluating clar on the individual points there?
Ah, I wasn't aware of this document. I can update it depending on how the discussion goes overall :)
Patrick
v2 cover Introduce clar testing framework
In reply to Patrick SteinhardtHi,
this is the second version of my RFC patch series that introduces the clar testing framework into our unit tests. The intent is to not have to hand-craft all features of a proper unit testing framework, while still not painting us into a corner. As such, the clar itself is small and extensible while still bringing some nice features to the table.
Changes compared to v1:
- Convert the ctypes unit tests to use clar, as I had it lying around anyway and René was asking for it.
- Adopt the AWK-based script by René, replacing the Python-based one. This gets rid of the mandatory Python dependency and at the same time also allows us to be more flexible going forward.
- Wire up the third party sources in our Makefiles such that they do not get linted via hdr-check or Coccinelle.
- Fix t/Makefile to not pass GIT_TEST_OPTS to our unit tests. They don't know how to handle those options, and clar-based tests error out when they see unknown options.
- Adapt Documentation/technical/unit-tests.txt to mention the clar.
- Rebase the series onto the latest master at 406f326d27 (The second
batch, 2024-08-01) to avoid some conflicts.
- Cherry-pick a whitespace fix that otherwise makes git-apply(1) unhappy. The CI job is still broken because the first patch that imports clar continues to be broken. I've created an upstream PR.
- Cherry-pick a fix by Randall to make clar work on HP NonStop. The fix has been proposed upstream in a PR.
With these changes, the CI jobs at GitLab all pass, except for the whitespace and clang-format checks.
I've also Cc'd Ed, one of the maintainers of clar. I also noticed that I'm a maintainer of that project back from my libgit2 times, so I think it should be relatively easy to land changes upstream.
Thanks!
Patrick
Patrick Steinhardt (7):
t: do not pass GIT_TEST_OPTS to unit tests with prove
t: import the clar unit testing framework t/clar: fix whitespace errors
t/clar: fix compatibility with NonStop
Makefile: wire up the clar unit testing framework t/unit-tests: convert strvec tests to use clar t/unit-tests: convert ctype tests to use clar
.gitignore | 1 +
Documentation/technical/unit-tests.txt | 2 +
Makefile | 42 +-
t/Makefile | 4 +-
t/run-test.sh | 2 +-
t/unit-tests/.gitignore | 2 +
t/unit-tests/clar-generate.awk | 50 ++
t/unit-tests/clar/.github/workflows/ci.yml | 23 +
t/unit-tests/clar/COPYING | 15 +
t/unit-tests/clar/README.md | 329 ++++++++
t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++
t/unit-tests/clar/clar.h | 173 +++++
t/unit-tests/clar/clar/fixtures.h | 50 ++
t/unit-tests/clar/clar/fs.h | 522 +++++++++++++
t/unit-tests/clar/clar/print.h | 211 ++++++
t/unit-tests/clar/clar/sandbox.h | 159 ++++
t/unit-tests/clar/clar/summary.h | 143 ++++
t/unit-tests/clar/generate.py | 266 +++++++
t/unit-tests/clar/test/.gitignore | 4 +
t/unit-tests/clar/test/Makefile | 39 +
t/unit-tests/clar/test/clar_test.h | 16 +
t/unit-tests/clar/test/main.c | 40 +
t/unit-tests/clar/test/main.c.sample | 27 +
t/unit-tests/clar/test/resources/test/file | 1 +
t/unit-tests/clar/test/sample.c | 84 ++
t/unit-tests/{t-ctype.c => ctype.c} | 71 +-
t/unit-tests/{t-strvec.c => strvec.c} | 119 ++-
t/unit-tests/unit-test.c | 17 +
t/unit-tests/unit-test.h | 3 +
29 files changed, 3159 insertions(+), 98 deletions(-)
create mode 100644 t/unit-tests/clar-generate.awk
create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml
create mode 100644 t/unit-tests/clar/COPYING
create mode 100644 t/unit-tests/clar/README.md
create mode 100644 t/unit-tests/clar/clar.c
create mode 100644 t/unit-tests/clar/clar.h
create mode 100644 t/unit-tests/clar/clar/fixtures.h
create mode 100644 t/unit-tests/clar/clar/fs.h
create mode 100644 t/unit-tests/clar/clar/print.h
create mode 100644 t/unit-tests/clar/clar/sandbox.h
create mode 100644 t/unit-tests/clar/clar/summary.h
create mode 100755 t/unit-tests/clar/generate.py
create mode 100644 t/unit-tests/clar/test/.gitignore
create mode 100644 t/unit-tests/clar/test/Makefile
create mode 100644 t/unit-tests/clar/test/clar_test.h
create mode 100644 t/unit-tests/clar/test/main.c
create mode 100644 t/unit-tests/clar/test/main.c.sample
create mode 100644 t/unit-tests/clar/test/resources/test/file
create mode 100644 t/unit-tests/clar/test/sample.c
rename t/unit-tests/{t-ctype.c => ctype.c} (71%)
rename t/unit-tests/{t-strvec.c => strvec.c} (54%)
create mode 100644 t/unit-tests/unit-test.c
create mode 100644 t/unit-tests/unit-test.hRange-diff against v1:
-: ---------- > 1: 78a9cc1162 t: do not pass GIT_TEST_OPTS to unit tests with prove 1: 4e3862991a ! 2: 6a88cf22a5 t: import the clar unit testing framework
@@ Commit message
+ ## Documentation/technical/unit-tests.txt ##
+@@ Documentation/technical/unit-tests.txt: GitHub / GitLab stars to estimate this.
+ :criterion: https://github.com/Snaipe/Criterion[Criterion]
+ :c-tap: https://github.com/rra/c-tap-harness/[C TAP]
+ :check: https://libcheck.github.io/check/[Check]
++:clar: https://github.com/clar-test/clar[Clar]
+
+ [format="csv",options="header",width="33%",subs="specialcharacters,attributes,quotes,macros"]
+ |=====
+@@ Documentation/technical/unit-tests.txt: Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiqui
+ {criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800
+ {c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33
+ {check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973 ++{clar},{lgpl},{false},{partial},{true},{true},{true},{true},{false},{false},{true},1,192
+ |=====
+
+ === Additional framework candidates
+
+ ## Makefile ##
+@@ Makefile: THIRD_PARTY_SOURCES += compat/poll/%
+ THIRD_PARTY_SOURCES += compat/regex/%
+ THIRD_PARTY_SOURCES += sha1collisiondetection/%
+ THIRD_PARTY_SOURCES += sha1dc/%
++THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/%
++THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/%
+
+ UNIT_TEST_PROGRAMS += t-ctype
+ UNIT_TEST_PROGRAMS += t-example-decorate
+@@ Makefile: $(SP_OBJ): %.sp: %.c %.o
+ .PHONY: sparse
+ sparse: $(SP_OBJ)
+
+-EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/%
++EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/%
+ ifndef OPENSSL_SHA1
+ EXCEPT_HDRS += sha1/openssl.h
+ endif
+
## t/unit-tests/clar/.github/workflows/ci.yml (new) ##
@@
+name: CI
-: ---------- > 3: a52ee59bf4 t/clar: fix whitespace errors
-: ---------- > 4: 02fb86dfbc t/clar: fix compatibility with NonStop
2: 7a5dfd5065 ! 5: 848dc673c4 Makefile: wire up the clar unit testing framework
@@ .gitignore
/bin-wrappers/
## Makefile ##
-@@ Makefile: THIRD_PARTY_SOURCES += compat/regex/%
- THIRD_PARTY_SOURCES += sha1collisiondetection/%
- THIRD_PARTY_SOURCES += sha1dc/%
+@@ Makefile: THIRD_PARTY_SOURCES += sha1dc/%
+ THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/%
+ THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/%
+UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
+UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES))
@@ Makefile: endif
bin-wrappers/%: wrap-for-bin.sh
$(call mkdir_p_parent_template)
+@@ Makefile: $(SP_OBJ): %.sp: %.c %.o
+ .PHONY: sparse
+ sparse: $(SP_OBJ)
+
+-EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/%
++EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/unit-test.h $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/%
+ ifndef OPENSSL_SHA1
+ EXCEPT_HDRS += sha1/openssl.h
+ endif
@@ Makefile: endif
artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
@@ Makefile: cocciclean:
clean: profile-clean coverage-clean cocciclean
$(RM) -r .build $(UNIT_TEST_BIN)
-+ $(RM) $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/.clarcache
++ $(RM) GIT-TEST-SUITES $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/clar-decls.h
$(RM) po/git.pot po/git-core.pot
$(RM) git.res
$(RM) $(OBJECTS)
@@ Makefile: $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \
+ if test x"$$FLAGS" != x"`cat GIT-TEST-SUITES 2>/dev/null`" ; then \
+ echo >&2 " * new test suites"; \
+ echo "$$FLAGS" >GIT-TEST-SUITES; \
-+ rm -f $(UNIT_TESTS_DIR)/.clarcache; \
+ fi
+
-+$(UNIT_TEST_DIR)/clar.suite: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES
-+ $(QUIET_GEN)$(UNIT_TEST_DIR)/clar/generate.py $(UNIT_TEST_DIR) >/dev/null
-+ @touch $@
-+$(UNIT_TEST_DIR)/clar-decls.h: $(UNIT_TEST_DIR)/clar.suite
-+ $(QUIET_GEN)grep '^extern void' $^ >$@
++$(UNIT_TEST_DIR)/clar-decls.h: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES
++ $(QUIET_GEN)for suite in $(UNIT_TESTS_SUITES); do \
++ sed -ne "s/^\(void test_$${suite}__[a-zA-Z_0-9][a-zA-Z_0-9]*(void)$$\)/extern \1;/p" $(UNIT_TEST_DIR)/$$suite.c; \
++ done >$@
++$(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h
++ $(QUIET_GEN)awk -f $(UNIT_TEST_DIR)/clar-generate.awk $< >$(UNIT_TEST_DIR)/clar.suite
+$(UNIT_TESTS_OBJS): $(UNIT_TEST_DIR)/clar-decls.h
+$(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR) -I$(UNIT_TEST_DIR)/clar
+$(UNIT_TESTS_PROG): $(UNIT_TEST_DIR)/clar.suite $(UNIT_TESTS_OBJS) $(GITLIBS) GIT-LDFLAGS
@@ t/Makefile: CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard cha
## t/unit-tests/.gitignore ##
@@
/bin
-+/.clarcache
+/clar.suite
+/clar-decls.h
+ ## t/unit-tests/clar-generate.awk (new) ##
+@@
++function add_suite(suite, initialize, cleanup, count) {
++ if (!suite) return
++ suite_count++
++ callback_count += count
++ suites = suites " {\n"
++ suites = suites " \"" suite "\",\n"
++ suites = suites " " initialize ",\n"
++ suites = suites " " cleanup ",\n"
++ suites = suites " _clar_cb_" suite ", " count ", 1\n"
++ suites = suites " },\n"
++}
++
++BEGIN {
++ suites = "static struct clar_suite _clar_suites[] = {\n"
++}
++
++{
++ print
++ name = $3; sub(/\(.*$/, "", name)
++ suite = name; sub(/^test_/, "", suite); sub(/__.*$/, "", suite)
++ short_name = name; sub(/^.*__/, "", short_name)
++ cb = "{ \"" short_name "\", &" name " }"
++ if (suite != prev_suite) {
++ add_suite(prev_suite, initialize, cleanup, count)
++ if (callbacks) callbacks = callbacks "};\n"
++ callbacks = callbacks "static const struct clar_func _clar_cb_" suite "[] = {\n"
++ initialize = "{ NULL, NULL }"
++ cleanup = "{ NULL, NULL }"
++ count = 0
++ prev_suite = suite
++ }
++ if (short_name == "initialize") {
++ initialize = cb
++ } else if (short_name == "cleanup") {
++ cleanup = cb
++ } else {
++ callbacks = callbacks " " cb ",\n"
++ count++
++ }
++}
++
++END {
++ add_suite(suite, initialize, cleanup, count)
++ suites = suites "};"
++ if (callbacks) callbacks = callbacks "};"
++ print callbacks
++ print suites
++ print "static const size_t _clar_suite_count = " suite_count ";"
++ print "static const size_t _clar_callback_count = " callback_count ";"
++}
+
## t/unit-tests/unit-test.c (new) ##
@@
+#include "unit-test.h"
+
+int cmd_main(int argc, const char **argv)
+{
-+ const char **args;
++ const char **argv_copy;
+ int ret;
+
+ /* Append the "-t" flag such that the tests generate TAP output. */
-+ DUP_ARRAY(args, argv, argc + 1);
-+ args[argc++] = "-t";
++ ALLOC_ARRAY(argv_copy, argc + 2);
++ COPY_ARRAY(argv_copy, argv, argc);
++ argv_copy[argc++] = "-t";
++ argv_copy[argc] = NULL;
+
-+ ret = clar_test(argc, (char **) args);
++ ret = clar_test(argc, (char **) argv_copy);
+
-+ free(args);
++ free(argv_copy);
+ return ret;
+}3: 1c2a510547 ! 6: 578e657269 t/unit-tests: convert strvec tests to use clar
@@ Commit message
Signed-off-by: Patrick Steinhardt <[email protected]>
## Makefile ##
-@@ Makefile: THIRD_PARTY_SOURCES += compat/regex/%
- THIRD_PARTY_SOURCES += sha1collisiondetection/%
- THIRD_PARTY_SOURCES += sha1dc/%
+@@ Makefile: THIRD_PARTY_SOURCES += sha1dc/%
+ THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/%
+ THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/%
+UNIT_TESTS_SUITES += strvec
UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES))
UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
-@@ Makefile: UNIT_TEST_PROGRAMS += t-reftable-basics
+@@ Makefile: UNIT_TEST_PROGRAMS += t-reftable-merged
UNIT_TEST_PROGRAMS += t-reftable-record
UNIT_TEST_PROGRAMS += t-strbuf
UNIT_TEST_PROGRAMS += t-strcmp-offset
@@ t/unit-tests/t-strvec.c => t/unit-tests/strvec.c
#include "strvec.h"
#define check_strvec(vec, ...) \
-- check_strvec_loc(TEST_LOCATION(), vec, __VA_ARGS__)
-+ check_strvec_loc(__FILE__, __func__, __LINE__, vec, __VA_ARGS__)
- LAST_ARG_MUST_BE_NULL
--static void check_strvec_loc(const char *loc, struct strvec *vec, ...)
-+static void check_strvec_loc(const char *file, const char *func, size_t line, struct strvec *vec, ...)
- {
- va_list ap;
- size_t nr = 0;
-@@ t/unit-tests/strvec.c: static void check_strvec_loc(const char *loc, struct strvec *vec, ...)
- if (!str)
- break;
-
-- if (!check_uint(vec->nr, >, nr) ||
-- !check_uint(vec->alloc, >, nr) ||
-- !check_str(vec->v[nr], str)) {
-- struct strbuf msg = STRBUF_INIT;
-- strbuf_addf(&msg, "strvec index %"PRIuMAX, (uintmax_t) nr);
-- test_assert(loc, msg.buf, 0);
-- strbuf_release(&msg);
-- va_end(ap);
-- return;
-- }
-+ clar__assert(vec->nr > nr, file, func, line, "Vector size too small", NULL, 1);
-+ clar__assert(vec->alloc > nr, file, func, line, "Vector allocation too small", NULL, 1);
-+ cl_assert_equal_s(vec->v[nr], str);
-
- nr++;
- }
- va_end(ap);
-
-- check_uint(vec->nr, ==, nr);
-- check_uint(vec->alloc, >=, nr);
-- check_pointer_eq(vec->v[nr], NULL);
-+ cl_assert(vec->nr == nr);
-+ cl_assert(vec->alloc >= nr);
-+ cl_assert_equal_p(vec->v[nr], NULL);
- }
+ do { \
+ const char *expect[] = { __VA_ARGS__ }; \
+- if (check_uint(ARRAY_SIZE(expect), >, 0) && \
+- check_pointer_eq(expect[ARRAY_SIZE(expect) - 1], NULL) && \
+- check_uint((vec)->nr, ==, ARRAY_SIZE(expect) - 1) && \
+- check_uint((vec)->nr, <=, (vec)->alloc)) { \
+- for (size_t i = 0; i < ARRAY_SIZE(expect); i++) { \
+- if (!check_str((vec)->v[i], expect[i])) { \
+- test_msg(" i: %"PRIuMAX, \
+- (uintmax_t)i); \
+- break; \
+- } \
+- } \
+- } \
++ cl_assert(ARRAY_SIZE(expect) > 0); \
++ cl_assert_equal_p(expect[ARRAY_SIZE(expect) - 1], NULL); \
++ cl_assert_equal_i((vec)->nr, ARRAY_SIZE(expect) - 1); \
++ cl_assert((vec)->nr <= (vec)->alloc); \
++ for (size_t i = 0; i < ARRAY_SIZE(expect); i++) \
++ cl_assert_equal_s((vec)->v[i], expect[i]); \
+ } while (0)
-static void t_static_init(void)
+void test_strvec__init(void)
@@ t/unit-tests/strvec.c: static void t_push(void)
}
-static void t_pushf(void)
-+void test_strvec__pushft_pushf(void)
++void test_strvec__pushf(void)
{
struct strvec vec = STRVEC_INIT;
strvec_pushf(&vec, "foo: %d", 1);
@@ t/unit-tests/strvec.c: static void t_detach(void)
- TEST(t_detach(), "detach");
- return test_done();
-}
+
+ ## t/unit-tests/unit-test.c ##
+@@ t/unit-tests/unit-test.c: int cmd_main(int argc, const char **argv)
+ int ret;
+
+ /* Append the "-t" flag such that the tests generate TAP output. */
+- ALLOC_ARRAY(argv_copy, argc + 2);
++ ALLOC_ARRAY(argv_copy, argc + 1);
+ COPY_ARRAY(argv_copy, argv, argc);
+ argv_copy[argc++] = "-t";
+- argv_copy[argc] = NULL;
+
+ ret = clar_test(argc, (char **) argv_copy);
+ -: ---------- > 7: 238de33b93 t/unit-tests: convert ctype tests to use clar
Signature · 1 line
2.46.0.dirty
RFC PATCH v2 1/7 t: do not pass GIT_TEST_OPTS to unit tests with prove
When using the prove target, we append GIT_TEST_OPTS to the arguments that we execute each of the tests with. This doesn't only include the intended test scripts, but also ends up passing the arguments to our unit tests. This is unintentional though as they do not even know to interpret those arguments, and is inconsistent with how we execute unit tests without prove.
This isn't much of an issue because our current set of unit tests mostly ignore their arguments anyway. With the introduction of clar-based unit tests this is about to become an issue though, as these do parse their command line argument to alter behaviour.
Prepare for this by passing GIT_TEST_OPTS to "run-test.sh" via an environment variable. Like this, we can conditionally forward it to our test scripts, only.
--- t/Makefile | 3 ++- t/run-test.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
t/Makefile +2 -1
@@ -68,7 +68,8 @@ failed: test -z "$$failed" || $(MAKE) $$failed prove: pre-clean check-chainlint $(TEST_LINT) - @echo "*** prove (shell & unit tests) ***"; $(CHAINLINTSUPPRESS) TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) :: $(GIT_TEST_OPTS) + @echo "*** prove (shell & unit tests) ***" + @$(CHAINLINTSUPPRESS) TEST_OPTIONS='$(GIT_TEST_OPTS)' TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) $(MAKE) clean-except-prove-cache $(T):
t/run-test.sh +1 -1
@@ -10,7 +10,7 @@ case "$1" in echo >&2 "ERROR: TEST_SHELL_PATH is empty or not set" exit 1 fi - exec "${TEST_SHELL_PATH}" "$@" + exec "${TEST_SHELL_PATH}" "$@" ${TEST_OPTIONS} ;; *) exec "$@"
Signature · 1 line
2.46.0.dirty
RFC PATCH v2 2/7 t: import the clar unit testing framework
In reply to Patrick SteinhardtImport the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
--- Documentation/technical/unit-tests.txt | 2 + Makefile | 4 +- t/unit-tests/clar/.github/workflows/ci.yml | 23 + t/unit-tests/clar/COPYING | 15 + t/unit-tests/clar/README.md | 329 ++++++++ t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++ t/unit-tests/clar/clar.h | 173 +++++ t/unit-tests/clar/clar/fixtures.h | 50 ++ t/unit-tests/clar/clar/fs.h | 522 +++++++++++++ t/unit-tests/clar/clar/print.h | 211 ++++++ t/unit-tests/clar/clar/sandbox.h | 154 ++++ t/unit-tests/clar/clar/summary.h | 143 ++++ t/unit-tests/clar/generate.py | 267 +++++++ t/unit-tests/clar/test/.gitignore | 5 + t/unit-tests/clar/test/Makefile | 39 + t/unit-tests/clar/test/clar_test.h | 16 + t/unit-tests/clar/test/main.c | 40 + t/unit-tests/clar/test/main.c.sample | 27 + t/unit-tests/clar/test/resources/test/file | 1 + t/unit-tests/clar/test/sample.c | 84 ++ 20 files changed, 2946 insertions(+), 1 deletion(-) create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml create mode 100644 t/unit-tests/clar/COPYING create mode 100644 t/unit-tests/clar/README.md create mode 100644 t/unit-tests/clar/clar.c create mode 100644 t/unit-tests/clar/clar.h create mode 100644 t/unit-tests/clar/clar/fixtures.h create mode 100644 t/unit-tests/clar/clar/fs.h create mode 100644 t/unit-tests/clar/clar/print.h create mode 100644 t/unit-tests/clar/clar/sandbox.h create mode 100644 t/unit-tests/clar/clar/summary.h create mode 100755 t/unit-tests/clar/generate.py create mode 100644 t/unit-tests/clar/test/.gitignore create mode 100644 t/unit-tests/clar/test/Makefile create mode 100644 t/unit-tests/clar/test/clar_test.h create mode 100644 t/unit-tests/clar/test/main.c create mode 100644 t/unit-tests/clar/test/main.c.sample create mode 100644 t/unit-tests/clar/test/resources/test/file create mode 100644 t/unit-tests/clar/test/sample.c
Documentation/technical/unit-tests.txt +2 -0
@@ -203,6 +203,7 @@ GitHub / GitLab stars to estimate this. :criterion: https://github.com/Snaipe/Criterion[Criterion] :c-tap: https://github.com/rra/c-tap-harness/[C TAP] :check: https://libcheck.github.io/check/[Check] +:clar: https://github.com/clar-test/clar[Clar] [format="csv",options="header",width="33%",subs="specialcharacters,attributes,quotes,macros"] |=====
@@ -212,6 +213,7 @@ Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiqui {criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800 {c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33 {check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973 +{clar},{lgpl},{false},{partial},{true},{true},{true},{true},{false},{false},{true},1,192 |===== === Additional framework candidates
Makefile +3 -1
@@ -1331,6 +1331,8 @@ THIRD_PARTY_SOURCES += compat/poll/% THIRD_PARTY_SOURCES += compat/regex/% THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% +THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% +THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate
@@ -3260,7 +3262,7 @@ $(SP_OBJ): %.sp: %.c %.o .PHONY: sparse sparse: $(SP_OBJ) -EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% +EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/% ifndef OPENSSL_SHA1 EXCEPT_HDRS += sha1/openssl.h endif
t/unit-tests/clar/.github/workflows/ci.yml new file +23 -0
@@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + strategy: + matrix: + os: [ ubuntu-latest, macos-latest ] + + runs-on: ${{ matrix.os }} + + steps: + - name: Check out + uses: actions/checkout@v2 + - name: Build + run: | + cd test + make
t/unit-tests/clar/COPYING new file +15 -0
@@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2011-2015 Vicent Marti + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
t/unit-tests/clar/README.md new file +329 -0
329 lines not on this page · raw message
t/unit-tests/clar/clar.c new file +842 -0
842 lines not on this page · raw message
t/unit-tests/clar/clar.h new file +173 -0
173 lines not on this page · raw message
t/unit-tests/clar/clar/fixtures.h new file +50 -0
50 lines not on this page · raw message
t/unit-tests/clar/clar/fs.h new file +522 -0
522 lines not on this page · raw message
t/unit-tests/clar/clar/print.h new file +211 -0
211 lines not on this page · raw message
t/unit-tests/clar/clar/sandbox.h new file +154 -0
154 lines not on this page · raw message
t/unit-tests/clar/clar/summary.h new file +143 -0
143 lines not on this page · raw message
t/unit-tests/clar/generate.py new file +267 -0
267 lines not on this page · raw message
t/unit-tests/clar/test/.gitignore new file +5 -0
@@ -0,0 +1,5 @@ +clar.suite +.clarcache +clar_test +*.o +
t/unit-tests/clar/test/Makefile new file +39 -0
@@ -0,0 +1,39 @@ +# +# Copyright (c) Vicent Marti. All rights reserved. +# +# This file is part of clar, distributed under the ISC license. +# For full terms see the included COPYING file. +# + +# +# Set up the path to the clar sources and to the fixtures directory +# +# The fixture path needs to be an absolute path so it can be used +# even after we have chdir'ed into the test directory while testing. +# +CURRENT_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +TEST_DIRECTORY := $(abspath $(dir $(CURRENT_MAKEFILE))) +CLAR_PATH := $(dir $(TEST_DIRECTORY)) +CLAR_FIXTURE_PATH := $(TEST_DIRECTORY)/resources/ + +CFLAGS=-g -I.. -I. -Wall -DCLAR_FIXTURE_PATH=\"$(CLAR_FIXTURE_PATH)\" + +.PHONY: clean + +# list the objects that go into our test +objects = main.o sample.o + +# build the test executable itself +clar_test: $(objects) clar_test.h clar.suite $(CLAR_PATH)clar.c + $(CC) $(CFLAGS) -o $@ "$(CLAR_PATH)clar.c" $(objects) + +# test object files depend on clar macros +$(objects) : $(CLAR_PATH)clar.h + +# build the clar.suite file of test metadata +clar.suite: + python "$(CLAR_PATH)generate.py" . + +# remove all generated files +clean: + $(RM) -rf *.o clar.suite .clarcache clar_test clar_test.dSYM
t/unit-tests/clar/test/clar_test.h new file +16 -0
@@ -0,0 +1,16 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ +#ifndef __CLAR_TEST__ +#define __CLAR_TEST__ + +/* Import the standard clar helper functions */ +#include "clar.h" + +/* Your custom shared includes / defines here */ +extern int global_test_counter; + +#endif
t/unit-tests/clar/test/main.c new file +40 -0
@@ -0,0 +1,40 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ + +#include "clar_test.h" + +/* +* Sample main() for clar tests. +* +* You should write your own main routine for clar tests that does specific +* setup and teardown as necessary for your application. The only required +* line is the call to `clar_test(argc, argv)`, which will execute the test +* suite. If you want to check the return value of the test application, +* your main() should return the same value returned by clar_test(). +*/ + +int global_test_counter =0 ; + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ +int ret; + +/* Your custom initialization here */ + global_test_counter =0 ; + +/* Run the test suite */ + ret =clar_test (argc, argv); + +/* Your custom cleanup here */ +cl_assert_equal_i (8 , global_test_counter); + +return ret; +}
t/unit-tests/clar/test/main.c.sample new file +27 -0
@@ -0,0 +1,27 @@ +/* + * Copyright (c) Vicent Marti. All rights reserved. + * + * This file is part of clar, distributed under the ISC license. + * For full terms see the included COPYING file. + */ + +#include "clar_test.h" + +/* + * Minimal main() for clar tests. + * + * Modify this with any application specific setup or teardown that you need. + * The only required line is the call to `clar_test(argc, argv)`, which will + * execute the test suite. If you want to check the return value of the test + * application, main() should return the same value returned by clar_test(). + */ + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + /* Run the test suite */ + return clar_test(argc, argv); +}
t/unit-tests/clar/test/resources/test/file new file +1 -0
@@ -0,0 +1 @@ +File
t/unit-tests/clar/test/sample.c new file +84 -0
84 lines not on this page · raw message
Signature · 1 line
2.46.0.dirty
Re: [RFC PATCH v2 2/7] t: import the clar unit testing framework
Quoted 59 lines · Patrick Steinhardt
Import the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
Signed-off-by Patrick Steinhardt <[email protected]>--- Documentation/technical/unit-tests.txt | 2 + Makefile | 4 +- t/unit-tests/clar/.github/workflows/ci.yml | 23 + t/unit-tests/clar/COPYING | 15 + t/unit-tests/clar/README.md | 329 ++++++++ t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++ t/unit-tests/clar/clar.h | 173 +++++ t/unit-tests/clar/clar/fixtures.h | 50 ++ t/unit-tests/clar/clar/fs.h | 522 +++++++++++++ t/unit-tests/clar/clar/print.h | 211 ++++++ t/unit-tests/clar/clar/sandbox.h | 154 ++++ t/unit-tests/clar/clar/summary.h | 143 ++++ t/unit-tests/clar/generate.py | 267 +++++++ t/unit-tests/clar/test/.gitignore | 5 + t/unit-tests/clar/test/Makefile | 39 + t/unit-tests/clar/test/clar_test.h | 16 + t/unit-tests/clar/test/main.c | 40 + t/unit-tests/clar/test/main.c.sample | 27 + t/unit-tests/clar/test/resources/test/file | 1 + t/unit-tests/clar/test/sample.c | 84 ++ 20 files changed, 2946 insertions(+), 1 deletion(-) create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml create mode 100644 t/unit-tests/clar/COPYING create mode 100644 t/unit-tests/clar/README.md create mode 100644 t/unit-tests/clar/clar.c create mode 100644 t/unit-tests/clar/clar.h create mode 100644 t/unit-tests/clar/clar/fixtures.h create mode 100644 t/unit-tests/clar/clar/fs.h create mode 100644 t/unit-tests/clar/clar/print.h create mode 100644 t/unit-tests/clar/clar/sandbox.h create mode 100644 t/unit-tests/clar/clar/summary.h create mode 100755 t/unit-tests/clar/generate.py create mode 100644 t/unit-tests/clar/test/.gitignore create mode 100644 t/unit-tests/clar/test/Makefile create mode 100644 t/unit-tests/clar/test/clar_test.h create mode 100644 t/unit-tests/clar/test/main.c create mode 100644 t/unit-tests/clar/test/main.c.sample create mode 100644 t/unit-tests/clar/test/resources/test/file create mode 100644 t/unit-tests/clar/test/sample.cDocumentation/technical/unit-tests.txt +1
-0@@ -203,6 +203,7 @@ GitHub / GitLab stars to estimate this. :criterion: https://github.com/Snaipe/Criterion[Criterion] :c-tap: https://github.com/rra/c-tap-harness/[C TAP] :check: https://libcheck.github.io/check/[Check] +:clar: https://github.com/clar-test/clar[Clar] [format="csv",options="header",width="33%",subs="specialcharacters,attributes,quotes,macros"] |=====
Documentation/technical/unit-tests.txt Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiqui · line 217
Quoted 5 lines
Documentation/technical/unit-tests.txt +1
-0@@ -212,6 +213,7 @@ Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiqui {criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800 {c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33 {check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973 +{clar},{lgpl},{false},{partial},{true},{true},{true},{true},{false},{false},{true},1,192 |=====
Isn't clar ISC-licensed, not LGPL?
https://github.com/clar-test/clar/blob/main/COPYING
Re: [RFC PATCH v2 2/7] t: import the clar unit testing framework
Quoted 10 lines · Josh Steadmon
Documentation/technical/unit-tests.txt Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiqui · line 217
Quoted 6 lines · Patrick Steinhardt
Documentation/technical/unit-tests.txt +1
-0@@ -212,6 +213,7 @@ Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiqui {criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800 {c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33 {check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973 +{clar},{lgpl},{false},{partial},{true},{true},{true},{true},{false},{false},{true},1,192 |=====Isn't clar ISC-licensed, not LGPL?
https://github.com/clar-test/clar/blob/main/COPYING
Yes it is, and that's also what I said in other mails. Guess this here was a copy-paste error, thanks for spotting it!
Patrick
RFC PATCH v2 3/7 t/clar: fix whitespace errors
In reply to Patrick SteinhardtFix whitespace errors in the clar that make git-apply(1) unhappy. This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/97
--- t/unit-tests/clar/clar/sandbox.h | 5 ++--- t/unit-tests/clar/generate.py | 1 - t/unit-tests/clar/test/.gitignore | 1 - 3 files changed, 2 insertions(+), 5 deletions(-)
t/unit-tests/clar/clar/sandbox.h +2 -3
@@ -25,9 +25,9 @@ find_tmp_path(char *buffer, size_t length)static const size_t var_count =5 ;static const char *env_vars[] = {"CLAR_TMP" ,"TMPDIR" ,"TMP" ,"TEMP" ,"USERPROFILE" - }; + }; -size_t i; +size_t i;for (i =0 ; i < var_count; ++i) {const char *env =getenv (env_vars[i]);
@@ -151,4 +151,3 @@ const char *clar_sandbox_path(void) {return _clar_path; } -
t/unit-tests/clar/generate.py +0 -1
@@ -264,4 +264,3 @@ def write(self): suite.disable(options.excluded) if suite.write(): print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count())) -
t/unit-tests/clar/test/.gitignore +0 -1
@@ -2,4 +2,3 @@ clar.suite .clarcache clar_test *.o -
Signature · 1 line
2.46.0.dirty
RFC PATCH v2 4/7 t/clar: fix compatibility with NonStop
In reply to Patrick SteinhardtThe NonStop platform does not have mkdtemp() available, which we rely on in build_sandbox_path(). Fix this issue by using mktemp() and mkdir() instead on this platform.
This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/96
--- t/unit-tests/clar/clar/sandbox.h | 6 ++++++ 1 file changed, 6 insertions(+)
t/unit-tests/clar/clar/sandbox.h +6 -0
@@ -120,6 +120,12 @@ static int build_sandbox_path(void)if (_mktemp (_clar_path) ==NULL )return -1 ; +if (mkdir (_clar_path,0700 ) !=0 ) +return -1 ; +#elif defined(__TANDEM) +if (mktemp (_clar_path) ==NULL ) +return -1 ; +if (mkdir (_clar_path,0700 ) !=0 )return -1 ;#elif defined(_WIN32)
Signature · 1 line
2.46.0.dirty
RFC PATCH v2 5/7 Makefile: wire up the clar unit testing framework
In reply to Patrick SteinhardtWire up the clar unit testing framework by introducing a new "unit-tests" executable. In contrast to the existing framework, this will result in a single executable for all test suites. The ability to pick specific tests to execute is retained via functionality built into the clar itself.
Note that we need to be a bit careful about how we need to invalidate our Makefile rules. While we obviously have to regenerate the clar suite when our test suites change, we also have to invalidate it in case any of the test suites gets removed. We do so by using our typical pattern of creating a GIT-TEST-SUITES file that gets updated whenever the set of test suites changes, so that we can easily depend on that file.
Another specialty is that we generate a "clar-decls.h" file. The test functions are neither static, nor do they have external declarations. This is because they are getting parsed via "generate.py", which then creates the external generations that get populated into an array. These declarations are only seen by the main function though.
The consequence is that we will get a bunch of "missing prototypes" errors from our compiler for each of these test functions. To fix those errors, we extract the extern declarations from "clar.suite" and put them into a standalone header that then gets included by each of our unit tests. This gets rid of compiler warnings for every function which has been extracted by "generate.py". More importantly though, it does _not_ get rid of warnings in case a function really isn't being used by anything. Thus, it would cause a compiler error if a function name was mistyped and thus not picked up by "generate.py".
--- .gitignore | 1 + Makefile | 36 ++++++++++++++++++++---- t/Makefile | 1 + t/unit-tests/.gitignore | 2 ++ t/unit-tests/clar-generate.awk | 50 ++++++++++++++++++++++++++++++++++ t/unit-tests/unit-test.c | 18 ++++++++++++ t/unit-tests/unit-test.h | 3 ++ 7 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 t/unit-tests/clar-generate.awk create mode 100644 t/unit-tests/unit-test.c create mode 100644 t/unit-tests/unit-test.h
.gitignore +1 -0
@@ -9,6 +9,7 @@ /GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES /GIT-SPATCH-DEFINES +/GIT-TEST-SUITES /GIT-USER-AGENT /GIT-VERSION-FILE /bin-wrappers/
Makefile +31 -5
@@ -1334,6 +1334,11 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) +UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/unit-test.o + UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate UNIT_TEST_PROGRAMS += t-hash
@@ -2713,6 +2718,7 @@ OBJECTS += $(XDIFF_OBJS) OBJECTS += $(FUZZ_OBJS) OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS) OBJECTS += $(UNIT_TEST_OBJS) +OBJECTS += $(UNIT_TESTS_OBJS) ifndef NO_CURL OBJECTS += http.o http-walker.o remote-curl.o
@@ -3215,7 +3221,7 @@ endif test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X)) -all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) +all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) bin-wrappers/%: wrap-for-bin.sh $(call mkdir_p_parent_template)
@@ -3262,7 +3268,7 @@ $(SP_OBJ): %.sp: %.c %.o .PHONY: sparse sparse: $(SP_OBJ) -EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/% +EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/unit-test.h $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/% ifndef OPENSSL_SHA1 EXCEPT_HDRS += sha1/openssl.h endif
@@ -3646,7 +3652,7 @@ endif artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \ GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \ - $(UNIT_TEST_PROGS) $(MOFILES) + $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) $(MOFILES) $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \ SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)' test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3702,6 +3708,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) -r .build $(UNIT_TEST_BIN) + $(RM) GIT-TEST-SUITES $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/clar-decls.h $(RM) po/git.pot po/git-core.pot $(RM) git.res $(RM) $(OBJECTS)
@@ -3861,7 +3868,26 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) +GIT-TEST-SUITES: FORCE + @FLAGS='$(UNIT_TESTS_SUITES)'; \ + if test x"$$FLAGS" != x"`cat GIT-TEST-SUITES 2>/dev/null`" ; then \ + echo >&2 " * new test suites"; \ + echo "$$FLAGS" >GIT-TEST-SUITES; \ + fi + +$(UNIT_TEST_DIR)/clar-decls.h: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES + $(QUIET_GEN)for suite in $(UNIT_TESTS_SUITES); do \ + sed -ne "s/^\(void test_$${suite}__[a-zA-Z_0-9][a-zA-Z_0-9]*(void)$$\)/extern \1;/p" $(UNIT_TEST_DIR)/$$suite.c; \ + done >$@ +$(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h + $(QUIET_GEN)awk -f $(UNIT_TEST_DIR)/clar-generate.awk $< >$(UNIT_TEST_DIR)/clar.suite +$(UNIT_TESTS_OBJS): $(UNIT_TEST_DIR)/clar-decls.h +$(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR) -I$(UNIT_TEST_DIR)/clar +$(UNIT_TESTS_PROG): $(UNIT_TEST_DIR)/clar.suite $(UNIT_TESTS_OBJS) $(GITLIBS) GIT-LDFLAGS + $(call mkdir_p_parent_template) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) + .PHONY: build-unit-tests unit-tests -build-unit-tests: $(UNIT_TEST_PROGS) -unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X +build-unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) +unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) t/helper/test-tool$X $(MAKE) -C t/ unit-tests
t/Makefile +1 -0
@@ -48,6 +48,7 @@ CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.tes CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c) UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES)) +UNIT_TEST_PROGRAMS += unit-tests/bin/unit-tests$(X) UNIT_TESTS = $(sort $(UNIT_TEST_PROGRAMS)) UNIT_TESTS_NO_DIR = $(notdir $(UNIT_TESTS))
t/unit-tests/.gitignore +2 -0
@@ -1 +1,3 @@ /bin +/clar.suite +/clar-decls.h
t/unit-tests/clar-generate.awk new file +50 -0
50 lines not on this page · raw message
t/unit-tests/unit-test.c new file +18 -0
@@ -0,0 +1,18 @@ +#include "unit-test.h" + +int cmd_main (int argc,const char **argv) +{ +const char **argv_copy; +int ret; + +/* Append the "-t" flag such that the tests generate TAP output. */ +ALLOC_ARRAY (argv_copy, argc +2 ); +COPY_ARRAY (argv_copy, argv, argc); + argv_copy[argc++] ="-t" ; + argv_copy[argc] =NULL ; + + ret =clar_test (argc, (char **) argv_copy); + +free (argv_copy); +return ret; +}
t/unit-tests/unit-test.h new file +3 -0
@@ -0,0 +1,3 @@ +#include "git-compat-util.h" +#include "clar.h" +#include "clar-decls.h"
Signature · 1 line
2.46.0.dirty
RFC PATCH v2 6/7 t/unit-tests: convert strvec tests to use clar
In reply to Patrick SteinhardtConvert the strvec tests to use the new clar unit testing framework. This is a first test balloon that demonstrates how the testing infra for clar-based tests looks like.
The tests are part of the "t/unit-tests/bin/unit-tests" binary. When running that binary, it generates TAP output:
# ./t/unit-tests/bin/unit-tests
TAP version 13
# start of suite 1: strvec
ok 1 - strvec::init
ok 2 - strvec::dynamic_init
ok 3 - strvec::clear
ok 4 - strvec::push
ok 5 - strvec::pushft_pushf
ok 6 - strvec::pushl
ok 7 - strvec::pushv
ok 8 - strvec::replace_at_head
ok 9 - strvec::replace_at_tail
ok 10 - strvec::replace_in_between
ok 11 - strvec::replace_with_substring
ok 12 - strvec::remove_at_head
ok 13 - strvec::remove_at_tail
ok 14 - strvec::remove_in_between
ok 15 - strvec::pop_empty_array
ok 16 - strvec::pop_non_empty_array
ok 17 - strvec::split_empty_string
ok 18 - strvec::split_single_item
ok 19 - strvec::split_multiple_items
ok 20 - strvec::split_whitespace_only
ok 21 - strvec::split_multiple_consecutive_whitespaces
ok 22 - strvec::detach
1..22The binary also supports some parameters that allow us to run only a subset of unit tests or alter the output:
$ ./t/unit-tests/bin/unit-tests -h
Usage: ./t/unit-tests/bin/unit-tests [options] Options:
-sname Run only the suite with name (can go to individual test name)
-iname Include the suite with name
-xname Exclude the suite with name
-v Increase verbosity (show suite names)
-q Only report tests that had an error
-Q Quit as soon as a test fails
-t Display results in tap format
-l Print suite names
-r[filename] Write summary file (to the optional filename)
Furthermore, running make unit-tests runs the binary along with all the other unit tests we have.
---
Makefile | 2 +-
t/unit-tests/{t-strvec.c => strvec.c} | 119 ++++++++++----------------
t/unit-tests/unit-test.c | 3 +-
3 files changed, 45 insertions(+), 79 deletions(-)
rename t/unit-tests/{t-strvec.c => strvec.c} (54%)Makefile +1 -1
@@ -1334,6 +1334,7 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_SUITES += strvec UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
@@ -1351,7 +1352,6 @@ UNIT_TEST_PROGRAMS += t-reftable-merged UNIT_TEST_PROGRAMS += t-reftable-record UNIT_TEST_PROGRAMS += t-strbuf UNIT_TEST_PROGRAMS += t-strcmp-offset -UNIT_TEST_PROGRAMS += t-strvec UNIT_TEST_PROGRAMS += t-trailer UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS)) UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
t/unit-tests/strvec.c renamed from t/unit-tests/t-strvec.c +43 -76
261 lines not on this page · raw message
t/unit-tests/unit-test.c +1 -2
@@ -6,10 +6,9 @@ int cmd_main(int argc, const char **argv)int ret;/* Append the "-t" flag such that the tests generate TAP output. */ -ALLOC_ARRAY (argv_copy, argc +2 ); +ALLOC_ARRAY (argv_copy, argc +1 );COPY_ARRAY (argv_copy, argv, argc); argv_copy[argc++] ="-t" ; - argv_copy[argc] =NULL ; ret =clar_test (argc, (char **) argv_copy);
Signature · 1 line
2.46.0.dirty
Re: [RFC PATCH v2 6/7] t/unit-tests: convert strvec tests to use clar
Quoted 118 lines · Patrick Steinhardt
Convert the strvec tests to use the new clar unit testing framework. This is a first test balloon that demonstrates how the testing infra for clar-based tests looks like.
The tests are part of the "t/unit-tests/bin/unit-tests" binary. When running that binary, it generates TAP output:
# ./t/unit-tests/bin/unit-tests TAP version 13 # start of suite 1: strvec ok 1 - strvec::init ok 2 - strvec::dynamic_init ok 3 - strvec::clear ok 4 - strvec::push ok 5 - strvec::pushft_pushf ok 6 - strvec::pushl ok 7 - strvec::pushv ok 8 - strvec::replace_at_head ok 9 - strvec::replace_at_tail ok 10 - strvec::replace_in_between ok 11 - strvec::replace_with_substring ok 12 - strvec::remove_at_head ok 13 - strvec::remove_at_tail ok 14 - strvec::remove_in_between ok 15 - strvec::pop_empty_array ok 16 - strvec::pop_non_empty_array ok 17 - strvec::split_empty_string ok 18 - strvec::split_single_item ok 19 - strvec::split_multiple_items ok 20 - strvec::split_whitespace_only ok 21 - strvec::split_multiple_consecutive_whitespaces ok 22 - strvec::detach 1..22The binary also supports some parameters that allow us to run only a subset of unit tests or alter the output:
$ ./t/unit-tests/bin/unit-tests -h Usage: ./t/unit-tests/bin/unit-tests [options]Options:
-sname Run only the suite withname(can go to individual test name)
-iname Include the suite withname
-xname Exclude the suite withname
-v Increase verbosity (show suite names)
-q Only report tests that had an error
-Q Quit as soon as a test fails
-t Display results in tap format
-l Print suite names
-r[filename] Write summary file (to the optional filename)Furthermore, running
make unit-testsruns the binary along with all the other unit tests we have.Signed-off-by Patrick Steinhardt <[email protected]>--- Makefile | 2 +- t/unit-tests/{t-strvec.c => strvec.c} | 119 ++++++++++---------------- t/unit-tests/unit-test.c | 3 +- 3 files changed, 45 insertions(+), 79 deletions(-) rename t/unit-tests/{t-strvec.c => strvec.c} (54%)Makefile +1
-1@@ -1334,6 +1334,7 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_SUITES += strvec UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o@@ -1351,7 +1352,6 @@ UNIT_TEST_PROGRAMS += t-reftable-merged UNIT_TEST_PROGRAMS += t-reftable-record UNIT_TEST_PROGRAMS += t-strbuf UNIT_TEST_PROGRAMS += t-strcmp-offset -UNIT_TEST_PROGRAMS += t-strvec UNIT_TEST_PROGRAMS += t-trailer UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS)) UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))t/unit-tests/strvec.c renamed from t/unit-tests/t-strvec.c +7
-13@@ -1,52 +1,46 @@ -#include "test-lib.h" +#include "unit-test.h" #include "strbuf.h" #include "strvec.h" #define check_strvec (vec, ...) \ do { \ const char *expect[] = { __VA_ARGS__ }; \ - if (check_uint(ARRAY_SIZE(expect), >, 0) && \ - check_pointer_eq(expect[ARRAY_SIZE(expect) - 1], NULL) && \ - check_uint((vec)->nr, ==, ARRAY_SIZE(expect) - 1) && \ - check_uint((vec)->nr, <=, (vec)->alloc)) { \ - for (size_t i = 0; i < ARRAY_SIZE(expect); i++) { \ - if (!check_str((vec)->v[i], expect[i])) { \ - test_msg(" i: %"PRIuMAX, \ - (uintmax_t)i); \ - break; \ - } \ - } \ - } \ + cl_assert(ARRAY_SIZE(expect) > 0); \ + cl_assert_equal_p(expect[ARRAY_SIZE(expect) - 1], NULL); \ + cl_assert_equal_i((vec)->nr, ARRAY_SIZE(expect) - 1); \ + cl_assert((vec)->nr <= (vec)->alloc); \ + for (size_t i = 0; i < ARRAY_SIZE(expect); i++) \ + cl_assert_equal_s((vec)->v[i], expect[i]); \ } while (0)
t/unit-tests/strvec.c line 21
Quoted 10 lines
t/unit-tests/strvec.c renamed from t/unit-tests/t-strvec.c +4
-4-static void t_static_init (void ) +void test_strvec__init (void ) {struct strvec vec =STRVEC_INIT ; -check_pointer_eq (vec.v, empty_strvec); - check_uint(vec.nr, ==, 0); - check_uint(vec.alloc, ==, 0); +cl_assert_equal_p (vec.v, empty_strvec); +cl_assert (vec.nr ==0 ); +cl_assert (vec.alloc ==0 );
Is there a reason you used cl_assert() instead of cl_assert_equal_i() for the .nr and .alloc checks here and below?
Quoted 261 lines
t/unit-tests/strvec.c renamed from t/unit-tests/t-strvec.c +32
-59} -static void t_dynamic_init (void ) +void test_strvec__dynamic_init (void ) {struct strvec vec;strvec_init (&vec); -check_pointer_eq (vec.v, empty_strvec); - check_uint(vec.nr, ==, 0); - check_uint(vec.alloc, ==, 0); +cl_assert_equal_p (vec.v, empty_strvec); +cl_assert (vec.nr ==0 ); +cl_assert (vec.alloc ==0 ); } -static void t_clear (void ) +void test_strvec__clear (void ) {struct strvec vec =STRVEC_INIT ;strvec_push (&vec,"foo" );strvec_clear (&vec); -check_pointer_eq (vec.v, empty_strvec); - check_uint(vec.nr, ==, 0); - check_uint(vec.alloc, ==, 0); +cl_assert_equal_p (vec.v, empty_strvec); +cl_assert (vec.nr ==0 ); +cl_assert (vec.alloc ==0 ); } -static void t_push (void ) +void test_strvec__push (void ) {struct strvec vec =STRVEC_INIT ;@@ -59,7 +53,7 @@ static void t_push(void)strvec_clear (&vec); } -static void t_pushf (void ) +void test_strvec__pushf (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushf (&vec,"foo: %d" ,1 );@@ -67,7 +61,7 @@ static void t_pushf(void)strvec_clear (&vec); } -static void t_pushl (void ) +void test_strvec__pushl (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushl (&vec,"foo" ,"bar" ,"baz" ,NULL );@@ -75,7 +69,7 @@ static void t_pushl(void)strvec_clear (&vec); } -static void t_pushv (void ) +void test_strvec__pushv (void ) {const char *strings[] = {"foo" ,"bar" ,"baz" ,NULL ,@@ -88,7 +82,7 @@ static void t_pushv(void)strvec_clear (&vec); } -static void t_replace_at_head (void ) +void test_strvec__replace_at_head (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushl (&vec,"foo" ,"bar" ,"baz" ,NULL );@@ -97,7 +91,7 @@ static void t_replace_at_head(void)strvec_clear (&vec); } -static void t_replace_at_tail (void ) +void test_strvec__replace_at_tail (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushl (&vec,"foo" ,"bar" ,"baz" ,NULL );@@ -106,7 +100,7 @@ static void t_replace_at_tail(void)strvec_clear (&vec); } -static void t_replace_in_between (void ) +void test_strvec__replace_in_between (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushl (&vec,"foo" ,"bar" ,"baz" ,NULL );@@ -115,7 +109,7 @@ static void t_replace_in_between(void)strvec_clear (&vec); } -static void t_replace_with_substring (void ) +void test_strvec__replace_with_substring (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushl (&vec,"foo" ,NULL );@@ -124,7 +118,7 @@ static void t_replace_with_substring(void)strvec_clear (&vec); } -static void t_remove_at_head (void ) +void test_strvec__remove_at_head (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushl (&vec,"foo" ,"bar" ,"baz" ,NULL );@@ -133,7 +127,7 @@ static void t_remove_at_head(void)strvec_clear (&vec); } -static void t_remove_at_tail (void ) +void test_strvec__remove_at_tail (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushl (&vec,"foo" ,"bar" ,"baz" ,NULL );@@ -142,7 +136,7 @@ static void t_remove_at_tail(void)strvec_clear (&vec); } -static void t_remove_in_between (void ) +void test_strvec__remove_in_between (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushl (&vec,"foo" ,"bar" ,"baz" ,NULL );@@ -151,7 +145,7 @@ static void t_remove_in_between(void)strvec_clear (&vec); } -static void t_pop_empty_array (void ) +void test_strvec__pop_empty_array (void ) {struct strvec vec =STRVEC_INIT ;strvec_pop (&vec);@@ -159,7 +153,7 @@ static void t_pop_empty_array(void)strvec_clear (&vec); } -static void t_pop_non_empty_array (void ) +void test_strvec__pop_non_empty_array (void ) {struct strvec vec =STRVEC_INIT ;strvec_pushl (&vec,"foo" ,"bar" ,"baz" ,NULL );@@ -168,7 +162,7 @@ static void t_pop_non_empty_array(void)strvec_clear (&vec); } -static void t_split_empty_string (void ) +void test_strvec__split_empty_string (void ) {struct strvec vec =STRVEC_INIT ;strvec_split (&vec,"" );@@ -176,7 +170,7 @@ static void t_split_empty_string(void)strvec_clear (&vec); } -static void t_split_single_item (void ) +void test_strvec__split_single_item (void ) {struct strvec vec =STRVEC_INIT ;strvec_split (&vec,"foo" );@@ -184,7 +178,7 @@ static void t_split_single_item(void)strvec_clear (&vec); } -static void t_split_multiple_items (void ) +void test_strvec__split_multiple_items (void ) {struct strvec vec =STRVEC_INIT ;strvec_split (&vec,"foo bar baz" );@@ -192,7 +186,7 @@ static void t_split_multiple_items(void)strvec_clear (&vec); } -static void t_split_whitespace_only (void ) +void test_strvec__split_whitespace_only (void ) {struct strvec vec =STRVEC_INIT ;strvec_split (&vec," \t\n" );@@ -200,7 +194,7 @@ static void t_split_whitespace_only(void)strvec_clear (&vec); } -static void t_split_multiple_consecutive_whitespaces (void ) +void test_strvec__split_multiple_consecutive_whitespaces (void ) {struct strvec vec =STRVEC_INIT ;strvec_split (&vec,"foo\n\t bar" );@@ -208,7 +202,7 @@ static void t_split_multiple_consecutive_whitespaces(void)strvec_clear (&vec); } -static void t_detach (void ) +void test_strvec__detach (void ) {struct strvec vec =STRVEC_INIT ;const char **detached;@@ -216,40 +210,13 @@ static void t_detach(void)strvec_push (&vec,"foo" ); detached =strvec_detach (&vec); -check_str (detached[0 ],"foo" ); -check_pointer_eq (detached[1 ],NULL ); +cl_assert_equal_s (detached[0 ],"foo" ); +cl_assert_equal_p (detached[1 ],NULL ); -check_pointer_eq (vec.v, empty_strvec); - check_uint(vec.nr, ==, 0); - check_uint(vec.alloc, ==, 0); +cl_assert_equal_p (vec.v, empty_strvec); +cl_assert (vec.nr ==0 ); +cl_assert (vec.alloc ==0 );free ((char *) detached[0 ]);free (detached); } - -int cmd_main (int argc,const char **argv) -{ -TEST (t_static_init (),"static initialization" ); -TEST (t_dynamic_init (),"dynamic initialization" ); -TEST (t_clear (),"clear" ); -TEST (t_push (),"push" ); -TEST (t_pushf (),"pushf" ); -TEST (t_pushl (),"pushl" ); -TEST (t_pushv (),"pushv" ); -TEST (t_replace_at_head (),"replace at head" ); -TEST (t_replace_in_between (),"replace in between" ); -TEST (t_replace_at_tail (),"replace at tail" ); -TEST (t_replace_with_substring (),"replace with substring" ); -TEST (t_remove_at_head (),"remove at head" ); -TEST (t_remove_in_between (),"remove in between" ); -TEST (t_remove_at_tail (),"remove at tail" ); -TEST (t_pop_empty_array (),"pop with empty array" ); -TEST (t_pop_non_empty_array (),"pop with non-empty array" ); -TEST (t_split_empty_string (),"split empty string" ); -TEST (t_split_single_item (),"split single item" ); -TEST (t_split_multiple_items (),"split multiple items" ); -TEST (t_split_whitespace_only (),"split whitespace only" ); -TEST (t_split_multiple_consecutive_whitespaces (),"split multiple consecutive whitespaces" ); -TEST (t_detach (),"detach" ); -return test_done (); -}t/unit-tests/unit-test.c +1
-2@@ -6,10 +6,9 @@ int cmd_main(int argc, const char **argv)int ret;/* Append the "-t" flag such that the tests generate TAP output. */ -ALLOC_ARRAY (argv_copy, argc +2 ); +ALLOC_ARRAY (argv_copy, argc +1 );COPY_ARRAY (argv_copy, argv, argc); argv_copy[argc++] ="-t" ; - argv_copy[argc] =NULL ; ret =clar_test (argc, (char **) argv_copy);Signature · 1 line
2.46.0.dirty
Re: [RFC PATCH v2 6/7] t/unit-tests: convert strvec tests to use clar
Quoted 14 lines · Josh Steadmon
t/unit-tests/strvec.c line 21
Quoted 10 lines · Patrick Steinhardt
t/unit-tests/strvec.c renamed from t/unit-tests/t-strvec.c +4
-4-static void t_static_init (void ) +void test_strvec__init (void ) {struct strvec vec =STRVEC_INIT ; -check_pointer_eq (vec.v, empty_strvec); - check_uint(vec.nr, ==, 0); - check_uint(vec.alloc, ==, 0); +cl_assert_equal_p (vec.v, empty_strvec); +cl_assert (vec.nr ==0 ); +cl_assert (vec.alloc ==0 );Is there a reason you used
cl_assert()instead ofcl_assert_equal_i()for the .nr and .alloc checks here and below?
No, there isn't. Let me adapt these indeed, thanks!
Patrick
RFC PATCH v2 7/7 t/unit-tests: convert ctype tests to use clar
In reply to Patrick SteinhardtConvert the ctype tests to use the new clar unit testing framework.
---
Makefile | 2 +-
t/unit-tests/{t-ctype.c => ctype.c} | 71 +++++++++++++++++++++++------
2 files changed, 59 insertions(+), 14 deletions(-)
rename t/unit-tests/{t-ctype.c => ctype.c} (71%)Makefile +1 -1
@@ -1334,13 +1334,13 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_SUITES += ctype UNIT_TESTS_SUITES += strvec UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/unit-test.o -UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate UNIT_TEST_PROGRAMS += t-hash UNIT_TEST_PROGRAMS += t-mem-pool
t/unit-tests/ctype.c renamed from t/unit-tests/t-ctype.c +58 -13
97 lines not on this page · raw message
Signature · 1 line
2.46.0.dirty
v3 cover Introduce clar testing framework
In reply to Patrick SteinhardtHi,
this is the third version of my RFC patch series that introduces the clar testing framework into our unit tests. The intent is to not have to hand-craft all features of a proper unit testing framework, while still not painting us into a corner. As such, the clar itself is small and extensible while still bringing some nice features to the table.
Changes compared to v2:
- Fix a copy/paste error for the clar license. It's ISC, not LGPL.
- Include "clar.h" via "clar/clar.h" such that we do not have to add "clar/" as in preprocessor include directive.
- Adapt strvec unit test to use cl_assert_equal_i() instead of
`cl_assert()`.
Thanks!
Patrick
Patrick Steinhardt (7):
t: do not pass GIT_TEST_OPTS to unit tests with prove
t: import the clar unit testing framework t/clar: fix whitespace errors
t/clar: fix compatibility with NonStop
Makefile: wire up the clar unit testing framework t/unit-tests: convert strvec tests to use clar t/unit-tests: convert ctype tests to use clar
.gitignore | 1 +
Documentation/technical/unit-tests.txt | 2 +
Makefile | 42 +-
t/Makefile | 4 +-
t/run-test.sh | 2 +-
t/unit-tests/.gitignore | 2 +
t/unit-tests/clar-generate.awk | 50 ++
t/unit-tests/clar/.github/workflows/ci.yml | 23 +
t/unit-tests/clar/COPYING | 15 +
t/unit-tests/clar/README.md | 329 ++++++++
t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++
t/unit-tests/clar/clar.h | 173 +++++
t/unit-tests/clar/clar/fixtures.h | 50 ++
t/unit-tests/clar/clar/fs.h | 522 +++++++++++++
t/unit-tests/clar/clar/print.h | 211 ++++++
t/unit-tests/clar/clar/sandbox.h | 159 ++++
t/unit-tests/clar/clar/summary.h | 143 ++++
t/unit-tests/clar/generate.py | 266 +++++++
t/unit-tests/clar/test/.gitignore | 4 +
t/unit-tests/clar/test/Makefile | 39 +
t/unit-tests/clar/test/clar_test.h | 16 +
t/unit-tests/clar/test/main.c | 40 +
t/unit-tests/clar/test/main.c.sample | 27 +
t/unit-tests/clar/test/resources/test/file | 1 +
t/unit-tests/clar/test/sample.c | 84 ++
t/unit-tests/{t-ctype.c => ctype.c} | 71 +-
t/unit-tests/{t-strvec.c => strvec.c} | 119 ++-
t/unit-tests/unit-test.c | 17 +
t/unit-tests/unit-test.h | 3 +
29 files changed, 3159 insertions(+), 98 deletions(-)
create mode 100644 t/unit-tests/clar-generate.awk
create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml
create mode 100644 t/unit-tests/clar/COPYING
create mode 100644 t/unit-tests/clar/README.md
create mode 100644 t/unit-tests/clar/clar.c
create mode 100644 t/unit-tests/clar/clar.h
create mode 100644 t/unit-tests/clar/clar/fixtures.h
create mode 100644 t/unit-tests/clar/clar/fs.h
create mode 100644 t/unit-tests/clar/clar/print.h
create mode 100644 t/unit-tests/clar/clar/sandbox.h
create mode 100644 t/unit-tests/clar/clar/summary.h
create mode 100755 t/unit-tests/clar/generate.py
create mode 100644 t/unit-tests/clar/test/.gitignore
create mode 100644 t/unit-tests/clar/test/Makefile
create mode 100644 t/unit-tests/clar/test/clar_test.h
create mode 100644 t/unit-tests/clar/test/main.c
create mode 100644 t/unit-tests/clar/test/main.c.sample
create mode 100644 t/unit-tests/clar/test/resources/test/file
create mode 100644 t/unit-tests/clar/test/sample.c
rename t/unit-tests/{t-ctype.c => ctype.c} (71%)
rename t/unit-tests/{t-strvec.c => strvec.c} (54%)
create mode 100644 t/unit-tests/unit-test.c
create mode 100644 t/unit-tests/unit-test.hRange-diff against v2:
1: 78a9cc1162 = 1: 78a9cc1162 t: do not pass GIT_TEST_OPTS to unit tests with prove
2: 6a88cf22a5 ! 2: b6c066ee4e t: import the clar unit testing framework
@@ Documentation/technical/unit-tests.txt: Framework,"<<license,License>>","<<vendo
{criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800
{c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33
{check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973
-+{clar},{lgpl},{false},{partial},{true},{true},{true},{true},{false},{false},{true},1,192
++{clar},{isc},{false},{partial},{true},{true},{true},{true},{false},{false},{true},1,192
|=====
=== Additional framework candidates
3: a52ee59bf4 = 3: 35682b7686 t/clar: fix whitespace errors
4: 02fb86dfbc = 4: 7a76c21bcb t/clar: fix compatibility with NonStop
5: 848dc673c4 ! 5: 68b3c65951 Makefile: wire up the clar unit testing framework
@@ Makefile: endif
bin-wrappers/%: wrap-for-bin.sh
$(call mkdir_p_parent_template)
-@@ Makefile: $(SP_OBJ): %.sp: %.c %.o
- .PHONY: sparse
- sparse: $(SP_OBJ)
-
--EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/%
-+EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/unit-test.h $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/%
- ifndef OPENSSL_SHA1
- EXCEPT_HDRS += sha1/openssl.h
- endif
@@ Makefile: endif
artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
@@ Makefile: $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \
+$(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h
+ $(QUIET_GEN)awk -f $(UNIT_TEST_DIR)/clar-generate.awk $< >$(UNIT_TEST_DIR)/clar.suite
+$(UNIT_TESTS_OBJS): $(UNIT_TEST_DIR)/clar-decls.h
-+$(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR) -I$(UNIT_TEST_DIR)/clar
++$(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR)
+$(UNIT_TESTS_PROG): $(UNIT_TEST_DIR)/clar.suite $(UNIT_TESTS_OBJS) $(GITLIBS) GIT-LDFLAGS
+ $(call mkdir_p_parent_template)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
@@ t/unit-tests/unit-test.c (new)
## t/unit-tests/unit-test.h (new) ##
@@
+#include "git-compat-util.h"
-+#include "clar.h"
++#include "clar/clar.h"
+#include "clar-decls.h"
6: 578e657269 ! 6: 4a0888380e t/unit-tests: convert strvec tests to use clar
@@ t/unit-tests/t-strvec.c => t/unit-tests/strvec.c
- check_uint(vec.nr, ==, 0);
- check_uint(vec.alloc, ==, 0);
+ cl_assert_equal_p(vec.v, empty_strvec);
-+ cl_assert(vec.nr == 0);
-+ cl_assert(vec.alloc == 0);
++ cl_assert_equal_i(vec.nr, 0);
++ cl_assert_equal_i(vec.alloc, 0);
}
-static void t_dynamic_init(void)
@@ t/unit-tests/t-strvec.c => t/unit-tests/strvec.c
- check_uint(vec.nr, ==, 0);
- check_uint(vec.alloc, ==, 0);
+ cl_assert_equal_p(vec.v, empty_strvec);
-+ cl_assert(vec.nr == 0);
-+ cl_assert(vec.alloc == 0);
++ cl_assert_equal_i(vec.nr, 0);
++ cl_assert_equal_i(vec.alloc, 0);
}
-static void t_clear(void)
@@ t/unit-tests/t-strvec.c => t/unit-tests/strvec.c
- check_uint(vec.nr, ==, 0);
- check_uint(vec.alloc, ==, 0);
+ cl_assert_equal_p(vec.v, empty_strvec);
-+ cl_assert(vec.nr == 0);
-+ cl_assert(vec.alloc == 0);
++ cl_assert_equal_i(vec.nr, 0);
++ cl_assert_equal_i(vec.alloc, 0);
}
-static void t_push(void)
@@ t/unit-tests/strvec.c: static void t_detach(void)
- check_uint(vec.nr, ==, 0);
- check_uint(vec.alloc, ==, 0);
+ cl_assert_equal_p(vec.v, empty_strvec);
-+ cl_assert(vec.nr == 0);
-+ cl_assert(vec.alloc == 0);
++ cl_assert_equal_i(vec.nr, 0);
++ cl_assert_equal_i(vec.alloc, 0);
free((char *) detached[0]);
free(detached);7: 238de33b93 = 7: f423b01c05 t/unit-tests: convert ctype tests to use clar
Signature · 1 line
2.46.0.dirty
RFC PATCH v3 1/7 t: do not pass GIT_TEST_OPTS to unit tests with prove
When using the prove target, we append GIT_TEST_OPTS to the arguments that we execute each of the tests with. This doesn't only include the intended test scripts, but also ends up passing the arguments to our unit tests. This is unintentional though as they do not even know to interpret those arguments, and is inconsistent with how we execute unit tests without prove.
This isn't much of an issue because our current set of unit tests mostly ignore their arguments anyway. With the introduction of clar-based unit tests this is about to become an issue though, as these do parse their command line argument to alter behaviour.
Prepare for this by passing GIT_TEST_OPTS to "run-test.sh" via an environment variable. Like this, we can conditionally forward it to our test scripts, only.
--- t/Makefile | 3 ++- t/run-test.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
t/Makefile +2 -1
@@ -68,7 +68,8 @@ failed: test -z "$$failed" || $(MAKE) $$failed prove: pre-clean check-chainlint $(TEST_LINT) - @echo "*** prove (shell & unit tests) ***"; $(CHAINLINTSUPPRESS) TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) :: $(GIT_TEST_OPTS) + @echo "*** prove (shell & unit tests) ***" + @$(CHAINLINTSUPPRESS) TEST_OPTIONS='$(GIT_TEST_OPTS)' TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) $(MAKE) clean-except-prove-cache $(T):
t/run-test.sh +1 -1
@@ -10,7 +10,7 @@ case "$1" in echo >&2 "ERROR: TEST_SHELL_PATH is empty or not set" exit 1 fi - exec "${TEST_SHELL_PATH}" "$@" + exec "${TEST_SHELL_PATH}" "$@" ${TEST_OPTIONS} ;; *) exec "$@"
Signature · 1 line
2.46.0.dirty
RFC PATCH v3 2/7 t: import the clar unit testing framework
In reply to Patrick SteinhardtImport the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
--- Documentation/technical/unit-tests.txt | 2 + Makefile | 4 +- t/unit-tests/clar/.github/workflows/ci.yml | 23 + t/unit-tests/clar/COPYING | 15 + t/unit-tests/clar/README.md | 329 ++++++++ t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++ t/unit-tests/clar/clar.h | 173 +++++ t/unit-tests/clar/clar/fixtures.h | 50 ++ t/unit-tests/clar/clar/fs.h | 522 +++++++++++++ t/unit-tests/clar/clar/print.h | 211 ++++++ t/unit-tests/clar/clar/sandbox.h | 154 ++++ t/unit-tests/clar/clar/summary.h | 143 ++++ t/unit-tests/clar/generate.py | 267 +++++++ t/unit-tests/clar/test/.gitignore | 5 + t/unit-tests/clar/test/Makefile | 39 + t/unit-tests/clar/test/clar_test.h | 16 + t/unit-tests/clar/test/main.c | 40 + t/unit-tests/clar/test/main.c.sample | 27 + t/unit-tests/clar/test/resources/test/file | 1 + t/unit-tests/clar/test/sample.c | 84 ++ 20 files changed, 2946 insertions(+), 1 deletion(-) create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml create mode 100644 t/unit-tests/clar/COPYING create mode 100644 t/unit-tests/clar/README.md create mode 100644 t/unit-tests/clar/clar.c create mode 100644 t/unit-tests/clar/clar.h create mode 100644 t/unit-tests/clar/clar/fixtures.h create mode 100644 t/unit-tests/clar/clar/fs.h create mode 100644 t/unit-tests/clar/clar/print.h create mode 100644 t/unit-tests/clar/clar/sandbox.h create mode 100644 t/unit-tests/clar/clar/summary.h create mode 100755 t/unit-tests/clar/generate.py create mode 100644 t/unit-tests/clar/test/.gitignore create mode 100644 t/unit-tests/clar/test/Makefile create mode 100644 t/unit-tests/clar/test/clar_test.h create mode 100644 t/unit-tests/clar/test/main.c create mode 100644 t/unit-tests/clar/test/main.c.sample create mode 100644 t/unit-tests/clar/test/resources/test/file create mode 100644 t/unit-tests/clar/test/sample.c
Documentation/technical/unit-tests.txt +2 -0
@@ -203,6 +203,7 @@ GitHub / GitLab stars to estimate this. :criterion: https://github.com/Snaipe/Criterion[Criterion] :c-tap: https://github.com/rra/c-tap-harness/[C TAP] :check: https://libcheck.github.io/check/[Check] +:clar: https://github.com/clar-test/clar[Clar] [format="csv",options="header",width="33%",subs="specialcharacters,attributes,quotes,macros"] |=====
@@ -212,6 +213,7 @@ Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiqui {criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800 {c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33 {check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973 +{clar},{isc},{false},{partial},{true},{true},{true},{true},{false},{false},{true},1,192 |===== === Additional framework candidates
Makefile +3 -1
@@ -1331,6 +1331,8 @@ THIRD_PARTY_SOURCES += compat/poll/% THIRD_PARTY_SOURCES += compat/regex/% THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% +THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% +THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate
@@ -3260,7 +3262,7 @@ $(SP_OBJ): %.sp: %.c %.o .PHONY: sparse sparse: $(SP_OBJ) -EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% +EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/% ifndef OPENSSL_SHA1 EXCEPT_HDRS += sha1/openssl.h endif
t/unit-tests/clar/.github/workflows/ci.yml new file +23 -0
@@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + strategy: + matrix: + os: [ ubuntu-latest, macos-latest ] + + runs-on: ${{ matrix.os }} + + steps: + - name: Check out + uses: actions/checkout@v2 + - name: Build + run: | + cd test + make
t/unit-tests/clar/COPYING new file +15 -0
@@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2011-2015 Vicent Marti + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
t/unit-tests/clar/README.md new file +329 -0
329 lines not on this page · raw message
t/unit-tests/clar/clar.c new file +842 -0
842 lines not on this page · raw message
t/unit-tests/clar/clar.h new file +173 -0
173 lines not on this page · raw message
t/unit-tests/clar/clar/fixtures.h new file +50 -0
50 lines not on this page · raw message
t/unit-tests/clar/clar/fs.h new file +522 -0
522 lines not on this page · raw message
t/unit-tests/clar/clar/print.h new file +211 -0
211 lines not on this page · raw message
t/unit-tests/clar/clar/sandbox.h new file +154 -0
154 lines not on this page · raw message
t/unit-tests/clar/clar/summary.h new file +143 -0
143 lines not on this page · raw message
t/unit-tests/clar/generate.py new file +267 -0
267 lines not on this page · raw message
t/unit-tests/clar/test/.gitignore new file +5 -0
@@ -0,0 +1,5 @@ +clar.suite +.clarcache +clar_test +*.o +
t/unit-tests/clar/test/Makefile new file +39 -0
@@ -0,0 +1,39 @@ +# +# Copyright (c) Vicent Marti. All rights reserved. +# +# This file is part of clar, distributed under the ISC license. +# For full terms see the included COPYING file. +# + +# +# Set up the path to the clar sources and to the fixtures directory +# +# The fixture path needs to be an absolute path so it can be used +# even after we have chdir'ed into the test directory while testing. +# +CURRENT_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +TEST_DIRECTORY := $(abspath $(dir $(CURRENT_MAKEFILE))) +CLAR_PATH := $(dir $(TEST_DIRECTORY)) +CLAR_FIXTURE_PATH := $(TEST_DIRECTORY)/resources/ + +CFLAGS=-g -I.. -I. -Wall -DCLAR_FIXTURE_PATH=\"$(CLAR_FIXTURE_PATH)\" + +.PHONY: clean + +# list the objects that go into our test +objects = main.o sample.o + +# build the test executable itself +clar_test: $(objects) clar_test.h clar.suite $(CLAR_PATH)clar.c + $(CC) $(CFLAGS) -o $@ "$(CLAR_PATH)clar.c" $(objects) + +# test object files depend on clar macros +$(objects) : $(CLAR_PATH)clar.h + +# build the clar.suite file of test metadata +clar.suite: + python "$(CLAR_PATH)generate.py" . + +# remove all generated files +clean: + $(RM) -rf *.o clar.suite .clarcache clar_test clar_test.dSYM
t/unit-tests/clar/test/clar_test.h new file +16 -0
@@ -0,0 +1,16 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ +#ifndef __CLAR_TEST__ +#define __CLAR_TEST__ + +/* Import the standard clar helper functions */ +#include "clar.h" + +/* Your custom shared includes / defines here */ +extern int global_test_counter; + +#endif
t/unit-tests/clar/test/main.c new file +40 -0
@@ -0,0 +1,40 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ + +#include "clar_test.h" + +/* +* Sample main() for clar tests. +* +* You should write your own main routine for clar tests that does specific +* setup and teardown as necessary for your application. The only required +* line is the call to `clar_test(argc, argv)`, which will execute the test +* suite. If you want to check the return value of the test application, +* your main() should return the same value returned by clar_test(). +*/ + +int global_test_counter =0 ; + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ +int ret; + +/* Your custom initialization here */ + global_test_counter =0 ; + +/* Run the test suite */ + ret =clar_test (argc, argv); + +/* Your custom cleanup here */ +cl_assert_equal_i (8 , global_test_counter); + +return ret; +}
t/unit-tests/clar/test/main.c.sample new file +27 -0
@@ -0,0 +1,27 @@ +/* + * Copyright (c) Vicent Marti. All rights reserved. + * + * This file is part of clar, distributed under the ISC license. + * For full terms see the included COPYING file. + */ + +#include "clar_test.h" + +/* + * Minimal main() for clar tests. + * + * Modify this with any application specific setup or teardown that you need. + * The only required line is the call to `clar_test(argc, argv)`, which will + * execute the test suite. If you want to check the return value of the test + * application, main() should return the same value returned by clar_test(). + */ + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + /* Run the test suite */ + return clar_test(argc, argv); +}
t/unit-tests/clar/test/resources/test/file new file +1 -0
@@ -0,0 +1 @@ +File
t/unit-tests/clar/test/sample.c new file +84 -0
84 lines not on this page · raw message
Signature · 1 line
2.46.0.dirty
RFC PATCH v3 3/7 t/clar: fix whitespace errors
In reply to Patrick SteinhardtFix whitespace errors in the clar that make git-apply(1) unhappy. This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/97
--- t/unit-tests/clar/clar/sandbox.h | 5 ++--- t/unit-tests/clar/generate.py | 1 - t/unit-tests/clar/test/.gitignore | 1 - 3 files changed, 2 insertions(+), 5 deletions(-)
t/unit-tests/clar/clar/sandbox.h +2 -3
@@ -25,9 +25,9 @@ find_tmp_path(char *buffer, size_t length)static const size_t var_count =5 ;static const char *env_vars[] = {"CLAR_TMP" ,"TMPDIR" ,"TMP" ,"TEMP" ,"USERPROFILE" - }; + }; -size_t i; +size_t i;for (i =0 ; i < var_count; ++i) {const char *env =getenv (env_vars[i]);
@@ -151,4 +151,3 @@ const char *clar_sandbox_path(void) {return _clar_path; } -
t/unit-tests/clar/generate.py +0 -1
@@ -264,4 +264,3 @@ def write(self): suite.disable(options.excluded) if suite.write(): print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count())) -
t/unit-tests/clar/test/.gitignore +0 -1
@@ -2,4 +2,3 @@ clar.suite .clarcache clar_test *.o -
Signature · 1 line
2.46.0.dirty
Re: [RFC PATCH v3 3/7] t/clar: fix whitespace errors
Quoted 11 lines · Patrick Steinhardt
Fix whitespace errors in the clar that make git-apply(1) unhappy. This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/97
Signed-off-by Patrick Steinhardt <[email protected]>--- t/unit-tests/clar/clar/sandbox.h | 5 ++--- t/unit-tests/clar/generate.py | 1 - t/unit-tests/clar/test/.gitignore | 1 - 3 files changed, 2 insertions(+), 5 deletions(-)
A funny thing about this step is that the copy I carried for the past few days was an empty patch, because out of habit I use the "--whitespace=fix" option while running "git am". As that fixes the whitespace breakage this step addresses while applying the previous step, this one becomes empty.
I'll requeue these patches with --whitespace=warn instead.
Thanks.
RE: [RFC PATCH v3 3/7] t/clar: fix whitespace errors
Quoted 14 lines · Junio C Hamano
Quoted 11 lines · Patrick Steinhardt
Fix whitespace errors in the clar that make git-apply(1) unhappy. This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/97
Signed-off-by Patrick Steinhardt <[email protected]>--- t/unit-tests/clar/clar/sandbox.h | 5 ++--- t/unit-tests/clar/generate.py | 1 - t/unit-tests/clar/test/.gitignore | 1 - 3 files changed, 2 insertions(+), 5 deletions(-)
Quoted 6 lines
A funny thing about this step is that the copy I carried for the past few days was an empty patch, because out of habit I use the "--whitespace=fix" option while running "git am". As that fixes the whitespace breakage this step addresses while applying the previous step, this one becomes empty.
I'll requeue these patches with --whitespace=warn instead.
Please forgive the third ask, but I really want to test the build early. Suggestions?
Re: [RFC PATCH v3 3/7] t/clar: fix whitespace errors
Quoted 15 lines · <[email protected]> writes:
Quoted 14 lines · Junio C Hamano
Quoted 11 lines · Patrick Steinhardt
Fix whitespace errors in the clar that make git-apply(1) unhappy. This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/97
Signed-off-by Patrick Steinhardt <[email protected]>--- t/unit-tests/clar/clar/sandbox.h | 5 ++--- t/unit-tests/clar/generate.py | 1 - t/unit-tests/clar/test/.gitignore | 1 - 3 files changed, 2 insertions(+), 5 deletions(-)
Quoted 8 lines
Quoted 6 lines
A funny thing about this step is that the copy I carried for the past few days was an empty patch, because out of habit I use the "--whitespace=fix" option while running "git am". As that fixes the whitespace breakage this step addresses while applying the previous step, this one becomes empty.
I'll requeue these patches with --whitespace=warn instead.
Please forgive the third ask, but I really want to test the build early. Suggestions?
Sorry, but I do not follow. If you have "git am" and "git clone" working, and if you have enough C toolchain to build the rest of Git, wouldn't it be sufficient to also build this addition? I think the 7 patches cleanly apply to any recent tip of 'master'.
RE: [RFC PATCH v3 3/7] t/clar: fix whitespace errors
Quoted 23 lines · Junio C Hamano
Quoted 15 lines · <[email protected]> writes:
Quoted 14 lines · Junio C Hamano
Quoted 11 lines · Patrick Steinhardt
Fix whitespace errors in the clar that make git-apply(1) unhappy. This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/97
Signed-off-by Patrick Steinhardt <[email protected]>--- t/unit-tests/clar/clar/sandbox.h | 5 ++--- t/unit-tests/clar/generate.py | 1 - t/unit-tests/clar/test/.gitignore | 1 - 3 files changed, 2 insertions(+), 5 deletions(-)Quoted 4 lines
Quoted 4 lines
A funny thing about this step is that the copy I carried for the past few days was an empty patch, because out of habit I use the "--whitespace=fix" option while running "git am". As that fixes the whitespace breakage this step addresses while applying the previous step, this
one becomes empty.
Quoted 1 line
Quoted 1 line
Quoted 8 lines
Quoted 3 lines
Quoted 1 line
I'll requeue these patches with --whitespace=warn instead.
Please forgive the third ask, but I really want to test the build early. Suggestions?
Sorry, but I do not follow. If you have "git am" and "git clone" working, and if you have enough C toolchain to build the rest of Git, wouldn't it be sufficient to also build this addition? I think the 7 patches cleanly apply to any recent tip of 'master'.
I am confused. My bad. The unit test works fine with the custom clar off seen and next.
Re: [RFC PATCH v3 3/7] t/clar: fix whitespace errors
In reply to Junio C HamanoQuoted 14 lines · Junio C Hamano
Quoted 11 lines · Patrick Steinhardt
Fix whitespace errors in the clar that make git-apply(1) unhappy. This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/97
Signed-off-by Patrick Steinhardt <[email protected]>--- t/unit-tests/clar/clar/sandbox.h | 5 ++--- t/unit-tests/clar/generate.py | 1 - t/unit-tests/clar/test/.gitignore | 1 - 3 files changed, 2 insertions(+), 5 deletions(-)
Quoted 7 lines
A funny thing about this step is that the copy I carried for the past few days was an empty patch, because out of habit I use the "--whitespace=fix" option while running "git am". As that fixes the whitespace breakage this step addresses while applying the previous step, this one becomes empty.
I'll requeue these patches with --whitespace=warn instead.
Oops, it turns out that this is insufficient. I'll add the following as a separate patch immediately after this step.
Thanks.
t/unit-tests/clar/clar/fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
t/unit-tests/clar/clar/fs.h +1 -1
@@ -146,7 +146,7 @@ fs_rm_wait(WCHAR *_wpath) ERROR_PATH_NOT_FOUND == last_error)return 0 ; -Sleep (RM_RETRY_DELAY * retries * retries); +Sleep (RM_RETRY_DELAY * retries * retries); }while (retries++ <=RM_RETRY_COUNT );
Re: [RFC PATCH v3 3/7] t/clar: fix whitespace errors
Quoted 37 lines · Junio C Hamano
Quoted 14 lines · Junio C Hamano
Quoted 11 lines · Patrick Steinhardt
Fix whitespace errors in the clar that make git-apply(1) unhappy. This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/97
Signed-off-by Patrick Steinhardt <[email protected]>--- t/unit-tests/clar/clar/sandbox.h | 5 ++--- t/unit-tests/clar/generate.py | 1 - t/unit-tests/clar/test/.gitignore | 1 - 3 files changed, 2 insertions(+), 5 deletions(-)Quoted 7 lines
A funny thing about this step is that the copy I carried for the past few days was an empty patch, because out of habit I use the "--whitespace=fix" option while running "git am". As that fixes the whitespace breakage this step addresses while applying the previous step, this one becomes empty.
I'll requeue these patches with --whitespace=warn instead.
Oops, it turns out that this is insufficient. I'll add the following as a separate patch immediately after this step.
Thanks.
t/unit-tests/clar/clar/fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)t/unit-tests/clar/clar/fs.h +0
-0
t/unit-tests/clar/clar/fs.h fs_rm_wait(WCHAR *_wpath) · line 151
Quoted 7 lines
t/unit-tests/clar/clar/fs.h +1
-1@@ -146,7 +146,7 @@ fs_rm_wait(WCHAR *_wpath) ERROR_PATH_NOT_FOUND == last_error)return 0 ; -Sleep (RM_RETRY_DELAY * retries * retries); +Sleep (RM_RETRY_DELAY * retries * retries); }while (retries++ <=RM_RETRY_COUNT );
Ah, didn't notice that one, thanks. I've also updated the upstream pull request accordingly.
Patrick
RFC PATCH v3 4/7 t/clar: fix compatibility with NonStop
In reply to Patrick SteinhardtThe NonStop platform does not have mkdtemp() available, which we rely on in build_sandbox_path(). Fix this issue by using mktemp() and mkdir() instead on this platform.
This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/96
--- t/unit-tests/clar/clar/sandbox.h | 6 ++++++ 1 file changed, 6 insertions(+)
t/unit-tests/clar/clar/sandbox.h +6 -0
@@ -120,6 +120,12 @@ static int build_sandbox_path(void)if (_mktemp (_clar_path) ==NULL )return -1 ; +if (mkdir (_clar_path,0700 ) !=0 ) +return -1 ; +#elif defined(__TANDEM) +if (mktemp (_clar_path) ==NULL ) +return -1 ; +if (mkdir (_clar_path,0700 ) !=0 )return -1 ;#elif defined(_WIN32)
Signature · 1 line
2.46.0.dirty
RFC PATCH v3 5/7 Makefile: wire up the clar unit testing framework
In reply to Patrick SteinhardtWire up the clar unit testing framework by introducing a new "unit-tests" executable. In contrast to the existing framework, this will result in a single executable for all test suites. The ability to pick specific tests to execute is retained via functionality built into the clar itself.
Note that we need to be a bit careful about how we need to invalidate our Makefile rules. While we obviously have to regenerate the clar suite when our test suites change, we also have to invalidate it in case any of the test suites gets removed. We do so by using our typical pattern of creating a GIT-TEST-SUITES file that gets updated whenever the set of test suites changes, so that we can easily depend on that file.
Another specialty is that we generate a "clar-decls.h" file. The test functions are neither static, nor do they have external declarations. This is because they are getting parsed via "generate.py", which then creates the external generations that get populated into an array. These declarations are only seen by the main function though.
The consequence is that we will get a bunch of "missing prototypes" errors from our compiler for each of these test functions. To fix those errors, we extract the extern declarations from "clar.suite" and put them into a standalone header that then gets included by each of our unit tests. This gets rid of compiler warnings for every function which has been extracted by "generate.py". More importantly though, it does _not_ get rid of warnings in case a function really isn't being used by anything. Thus, it would cause a compiler error if a function name was mistyped and thus not picked up by "generate.py".
--- .gitignore | 1 + Makefile | 34 ++++++++++++++++++++--- t/Makefile | 1 + t/unit-tests/.gitignore | 2 ++ t/unit-tests/clar-generate.awk | 50 ++++++++++++++++++++++++++++++++++ t/unit-tests/unit-test.c | 18 ++++++++++++ t/unit-tests/unit-test.h | 3 ++ 7 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 t/unit-tests/clar-generate.awk create mode 100644 t/unit-tests/unit-test.c create mode 100644 t/unit-tests/unit-test.h
.gitignore +1 -0
@@ -9,6 +9,7 @@ /GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES /GIT-SPATCH-DEFINES +/GIT-TEST-SUITES /GIT-USER-AGENT /GIT-VERSION-FILE /bin-wrappers/
Makefile +30 -4
@@ -1334,6 +1334,11 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) +UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/unit-test.o + UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate UNIT_TEST_PROGRAMS += t-hash
@@ -2713,6 +2718,7 @@ OBJECTS += $(XDIFF_OBJS) OBJECTS += $(FUZZ_OBJS) OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS) OBJECTS += $(UNIT_TEST_OBJS) +OBJECTS += $(UNIT_TESTS_OBJS) ifndef NO_CURL OBJECTS += http.o http-walker.o remote-curl.o
@@ -3215,7 +3221,7 @@ endif test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X)) -all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) +all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) bin-wrappers/%: wrap-for-bin.sh $(call mkdir_p_parent_template)
@@ -3646,7 +3652,7 @@ endif artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \ GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \ - $(UNIT_TEST_PROGS) $(MOFILES) + $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) $(MOFILES) $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \ SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)' test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3702,6 +3708,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) -r .build $(UNIT_TEST_BIN) + $(RM) GIT-TEST-SUITES $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/clar-decls.h $(RM) po/git.pot po/git-core.pot $(RM) git.res $(RM) $(OBJECTS)
@@ -3861,7 +3868,26 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) +GIT-TEST-SUITES: FORCE + @FLAGS='$(UNIT_TESTS_SUITES)'; \ + if test x"$$FLAGS" != x"`cat GIT-TEST-SUITES 2>/dev/null`" ; then \ + echo >&2 " * new test suites"; \ + echo "$$FLAGS" >GIT-TEST-SUITES; \ + fi + +$(UNIT_TEST_DIR)/clar-decls.h: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES + $(QUIET_GEN)for suite in $(UNIT_TESTS_SUITES); do \ + sed -ne "s/^\(void test_$${suite}__[a-zA-Z_0-9][a-zA-Z_0-9]*(void)$$\)/extern \1;/p" $(UNIT_TEST_DIR)/$$suite.c; \ + done >$@ +$(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h + $(QUIET_GEN)awk -f $(UNIT_TEST_DIR)/clar-generate.awk $< >$(UNIT_TEST_DIR)/clar.suite +$(UNIT_TESTS_OBJS): $(UNIT_TEST_DIR)/clar-decls.h +$(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR) +$(UNIT_TESTS_PROG): $(UNIT_TEST_DIR)/clar.suite $(UNIT_TESTS_OBJS) $(GITLIBS) GIT-LDFLAGS + $(call mkdir_p_parent_template) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) + .PHONY: build-unit-tests unit-tests -build-unit-tests: $(UNIT_TEST_PROGS) -unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X +build-unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) +unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) t/helper/test-tool$X $(MAKE) -C t/ unit-tests
t/Makefile +1 -0
@@ -48,6 +48,7 @@ CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.tes CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c) UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES)) +UNIT_TEST_PROGRAMS += unit-tests/bin/unit-tests$(X) UNIT_TESTS = $(sort $(UNIT_TEST_PROGRAMS)) UNIT_TESTS_NO_DIR = $(notdir $(UNIT_TESTS))
t/unit-tests/.gitignore +2 -0
@@ -1 +1,3 @@ /bin +/clar.suite +/clar-decls.h
t/unit-tests/clar-generate.awk new file +50 -0
50 lines not on this page · raw message
t/unit-tests/unit-test.c new file +18 -0
@@ -0,0 +1,18 @@ +#include "unit-test.h" + +int cmd_main (int argc,const char **argv) +{ +const char **argv_copy; +int ret; + +/* Append the "-t" flag such that the tests generate TAP output. */ +ALLOC_ARRAY (argv_copy, argc +2 ); +COPY_ARRAY (argv_copy, argv, argc); + argv_copy[argc++] ="-t" ; + argv_copy[argc] =NULL ; + + ret =clar_test (argc, (char **) argv_copy); + +free (argv_copy); +return ret; +}
t/unit-tests/unit-test.h new file +3 -0
@@ -0,0 +1,3 @@ +#include "git-compat-util.h" +#include "clar/clar.h" +#include "clar-decls.h"
Signature · 1 line
2.46.0.dirty
RFC PATCH v3 6/7 t/unit-tests: convert strvec tests to use clar
In reply to Patrick SteinhardtConvert the strvec tests to use the new clar unit testing framework. This is a first test balloon that demonstrates how the testing infra for clar-based tests looks like.
The tests are part of the "t/unit-tests/bin/unit-tests" binary. When running that binary, it generates TAP output:
# ./t/unit-tests/bin/unit-tests
TAP version 13
# start of suite 1: strvec
ok 1 - strvec::init
ok 2 - strvec::dynamic_init
ok 3 - strvec::clear
ok 4 - strvec::push
ok 5 - strvec::pushft_pushf
ok 6 - strvec::pushl
ok 7 - strvec::pushv
ok 8 - strvec::replace_at_head
ok 9 - strvec::replace_at_tail
ok 10 - strvec::replace_in_between
ok 11 - strvec::replace_with_substring
ok 12 - strvec::remove_at_head
ok 13 - strvec::remove_at_tail
ok 14 - strvec::remove_in_between
ok 15 - strvec::pop_empty_array
ok 16 - strvec::pop_non_empty_array
ok 17 - strvec::split_empty_string
ok 18 - strvec::split_single_item
ok 19 - strvec::split_multiple_items
ok 20 - strvec::split_whitespace_only
ok 21 - strvec::split_multiple_consecutive_whitespaces
ok 22 - strvec::detach
1..22The binary also supports some parameters that allow us to run only a subset of unit tests or alter the output:
$ ./t/unit-tests/bin/unit-tests -h
Usage: ./t/unit-tests/bin/unit-tests [options] Options:
-sname Run only the suite with name (can go to individual test name)
-iname Include the suite with name
-xname Exclude the suite with name
-v Increase verbosity (show suite names)
-q Only report tests that had an error
-Q Quit as soon as a test fails
-t Display results in tap format
-l Print suite names
-r[filename] Write summary file (to the optional filename)
Furthermore, running make unit-tests runs the binary along with all the other unit tests we have.
---
Makefile | 2 +-
t/unit-tests/{t-strvec.c => strvec.c} | 119 ++++++++++----------------
t/unit-tests/unit-test.c | 3 +-
3 files changed, 45 insertions(+), 79 deletions(-)
rename t/unit-tests/{t-strvec.c => strvec.c} (54%)Makefile +1 -1
@@ -1334,6 +1334,7 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_SUITES += strvec UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
@@ -1351,7 +1352,6 @@ UNIT_TEST_PROGRAMS += t-reftable-merged UNIT_TEST_PROGRAMS += t-reftable-record UNIT_TEST_PROGRAMS += t-strbuf UNIT_TEST_PROGRAMS += t-strcmp-offset -UNIT_TEST_PROGRAMS += t-strvec UNIT_TEST_PROGRAMS += t-trailer UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS)) UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
t/unit-tests/strvec.c renamed from t/unit-tests/t-strvec.c +43 -76
261 lines not on this page · raw message
t/unit-tests/unit-test.c +1 -2
@@ -6,10 +6,9 @@ int cmd_main(int argc, const char **argv)int ret;/* Append the "-t" flag such that the tests generate TAP output. */ -ALLOC_ARRAY (argv_copy, argc +2 ); +ALLOC_ARRAY (argv_copy, argc +1 );COPY_ARRAY (argv_copy, argv, argc); argv_copy[argc++] ="-t" ; - argv_copy[argc] =NULL ; ret =clar_test (argc, (char **) argv_copy);
Signature · 1 line
2.46.0.dirty
RFC PATCH v3 7/7 t/unit-tests: convert ctype tests to use clar
In reply to Patrick SteinhardtConvert the ctype tests to use the new clar unit testing framework.
---
Makefile | 2 +-
t/unit-tests/{t-ctype.c => ctype.c} | 71 +++++++++++++++++++++++------
2 files changed, 59 insertions(+), 14 deletions(-)
rename t/unit-tests/{t-ctype.c => ctype.c} (71%)Makefile +1 -1
@@ -1334,13 +1334,13 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_SUITES += ctype UNIT_TESTS_SUITES += strvec UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/unit-test.o -UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate UNIT_TEST_PROGRAMS += t-hash UNIT_TEST_PROGRAMS += t-mem-pool
t/unit-tests/ctype.c renamed from t/unit-tests/t-ctype.c +58 -13
97 lines not on this page · raw message
Signature · 1 line
2.46.0.dirty
Quoted 22 lines · Patrick Steinhardt
Hi,
this is the third version of my RFC patch series that introduces the clar testing framework into our unit tests. The intent is to not have to hand-craft all features of a proper unit testing framework, while still not painting us into a corner. As such, the clar itself is small and extensible while still bringing some nice features to the table.
Changes compared to v2:
- Fix a copy/paste error for the clar license. It's ISC, not LGPL.
- Include "
clar.h" via "clar/clar.h" such that we do not have to add "clar/" as in preprocessor include directive.- Adapt strvec unit test to use
cl_assert_equal_i()instead of`cl_assert()`.Thanks!
Patrick
Quoted 8 lines
Patrick Steinhardt (7):
t: do not passGIT_TEST_OPTSto unit tests with prove
t: import the clar unit testing framework t/clar: fix whitespace errors
t/clar: fix compatibility with NonStop
Makefile: wire up the clar unit testing framework t/unit-tests: convert strvec tests to use clar t/unit-tests: convert ctype tests to use clar
I'm generally in favor of this change, but I'm still unsure what our plan is for importing this from upstream clar. Are we going to vendor our own copy here and (hopefully) someone will pay attention to upstream fixes and apply them to our copy? Or will we replace this with a submodule?
Quoted 32 lines · Josh Steadmon
Quoted 22 lines · Patrick Steinhardt
Hi,
this is the third version of my RFC patch series that introduces the clar testing framework into our unit tests. The intent is to not have to hand-craft all features of a proper unit testing framework, while still not painting us into a corner. As such, the clar itself is small and extensible while still bringing some nice features to the table.
Changes compared to v2:
- Fix a copy/paste error for the clar license. It's ISC, not LGPL.
- Include "
clar.h" via "clar/clar.h" such that we do not have to add "clar/" as in preprocessor include directive.- Adapt strvec unit test to use
cl_assert_equal_i()instead of`cl_assert()`.Thanks!
Patrick
Quoted 8 lines
Patrick Steinhardt (7):
t: do not passGIT_TEST_OPTSto unit tests with prove
t: import the clar unit testing framework t/clar: fix whitespace errors
t/clar: fix compatibility with NonStop
Makefile: wire up the clar unit testing framework t/unit-tests: convert strvec tests to use clar t/unit-tests: convert ctype tests to use clar
Quoted 1 line
I'm generally in favor of this change, but I'm still unsure what our plan
is for
Quoted 1 line
importing this from upstream clar. Are we going to vendor our own copy here
and
Quoted 1 line
(hopefully) someone will pay attention to upstream fixes and apply them to
our
Quoted 1 line
copy? Or will we replace this with a submodule?
I think we have our own copy of clar, customized to integrate with the existing make
infrastructure.
Quoted 5 lines · Josh Steadmon
I'm generally in favor of this change, but I'm still unsure what our plan is for importing this from upstream clar. Are we going to vendor our own copy here and (hopefully) someone will pay attention to upstream fixes and apply them to our copy? Or will we replace this with a submodule?
As long as we do not have to make any changes to the "vendored" code ourselves, that would not matter. We will not randomly update the gitlink that specifies "we want to use _this_ version and not other version of upstream clar" without good reasons if you are using it as a submodule, and we would need to justify why we are updating the hierarchy if we import the hierarchy as vendored source. So the hassle of "updating from upstream" is pretty much the same.
For something as small as "clar", I think it is fine to start with the currently proposed layout and see what happens. If we can keep going without touching the imported part of the sources at all, and the system proves to be useful and stable, that is a good time to suggest moving it out and binding the selected version of the upstream as a submodule.
Thanks.
Quoted 15 lines · Junio C Hamano
Quoted 5 lines · Josh Steadmon
I'm generally in favor of this change, but I'm still unsure what our plan is for importing this from upstream clar. Are we going to vendor our own copy here and (hopefully) someone will pay attention to upstream fixes and apply them to our copy? Or will we replace this with a submodule?
As long as we do not have to make any changes to the "vendored" code ourselves, that would not matter. We will not randomly update the gitlink that specifies "we want to use _this_ version and not other version of upstream clar" without good reasons if you are using it as a submodule, and we would need to justify why we are updating the hierarchy if we import the hierarchy as vendored source. So the hassle of "updating from upstream" is pretty much the same.
Quoted 5 lines
For something as small as "clar", I think it is fine to start with the currently proposed layout and see what happens. If we can keep going without touching the imported part of the sources at all, and the system proves to be useful and stable, that is a good time to suggest moving it out and binding the selected version of the upstream as a submodule.
I think we already have a copy customized for git's use. The main clar repo on its own has portability issues. I have contributed a few fixes, but they need work.
Quoted 8 lines · <[email protected]> writes:
Quoted 5 lines
For something as small as "clar", I think it is fine to start with the currently proposed layout and see what happens. If we can keep going without touching the imported part of the sources at all, and the system proves to be useful and stable, that is a good time to suggest moving it out and binding the selected version of the upstream as a submodule.
I think we already have a copy customized for git's use. The main clar repo on its own has portability issues. I have contributed a few fixes, but they need work.
Yup, but as long as the changes we make are all upstreamable, the story does not change all that much. Changes like "#ifdef TANDEM" would be totally uncontroversial thing for them to accept and we should be able to upstream them fairly easily, and once we thin our local customization down to zero, we'd reach the state I outlined.
Starting out with a local copy helps us making these portability and other changes without much friction, regardless of how responsive the upstream is, and the request upstream would see is "here are the changes to make it available on more platforms and/or making it generally more useful. all of these changes have been used and battle-tested in the context of the Git project for N months, please apply."
Quoted 17 lines · Junio C Hamano
Quoted 8 lines · <[email protected]> writes:
Quoted 5 lines
For something as small as "clar", I think it is fine to start with the currently proposed layout and see what happens. If we can keep going without touching the imported part of the sources at all, and the system proves to be useful and stable, that is a good time to suggest moving it out and binding the selected version of the upstream as a submodule.
I think we already have a copy customized for git's use. The main clar repo on its own has portability issues. I have contributed a few fixes, but they need work.
Yup, but as long as the changes we make are all upstreamable, the story does not change all that much. Changes like "#ifdef TANDEM" would be totally uncontroversial thing for them to accept and we should be able to upstream them fairly easily, and once we thin our local customization down to zero, we'd reach the state I outlined.
Quoted 7 lines
Starting out with a local copy helps us making these portability and other changes without much friction, regardless of how responsive the upstream is, and the request upstream would see is "here are the changes to make it available on more platforms and/or making it generally more useful. all of these changes have been used and battle-tested in the context of the Git project for N months, please apply."
Yeah, agreed. My intention certainly is to upstream all required changes. Right now, it's only two minor fixes that we need, one for NonStop and one for whitespace fixes.
While I'm still one of the maintainers of the clar, I first want to connect with Ed before merging any of these fixes. I would otherwise consider it a bit overreaching to just go in and merge things after having been absent from that project for such a long time. But when things are sorted out I think the upstreaming process should be easy enough.
Meanwhile, I think keeping this in the tree is fine. We can then optionally convert the code to a submodule once the necessary changes are upstream.
Patrick
v4 cover Introduce clar testing framework
In reply to Patrick SteinhardtHi,
this is the fourth version of my patch series that introduces the clar testing framework for our unit tests.
Changes compared to v3:
- Seeing that the overall feedback seems to be rather positive, I've now dropped the "RFC" prefix.
- Fix another whitespace-damaged line in the clar unit test. I've adapted the upstream pull request accordingly.
- Fix make hdr-check, which failed due to the missing dependency on
"clar-decls.h".
Thanks!
Patrick
Patrick Steinhardt (7):
t: do not pass GIT_TEST_OPTS to unit tests with prove
t: import the clar unit testing framework t/clar: fix whitespace errors
t/clar: fix compatibility with NonStop
Makefile: wire up the clar unit testing framework t/unit-tests: convert strvec tests to use clar t/unit-tests: convert ctype tests to use clar
.gitignore | 1 +
Documentation/technical/unit-tests.txt | 2 +
Makefile | 43 +-
t/Makefile | 4 +-
t/run-test.sh | 2 +-
t/unit-tests/.gitignore | 2 +
t/unit-tests/clar-generate.awk | 50 ++
t/unit-tests/clar/.github/workflows/ci.yml | 23 +
t/unit-tests/clar/COPYING | 15 +
t/unit-tests/clar/README.md | 329 ++++++++
t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++
t/unit-tests/clar/clar.h | 173 +++++
t/unit-tests/clar/clar/fixtures.h | 50 ++
t/unit-tests/clar/clar/fs.h | 522 +++++++++++++
t/unit-tests/clar/clar/print.h | 211 ++++++
t/unit-tests/clar/clar/sandbox.h | 159 ++++
t/unit-tests/clar/clar/summary.h | 143 ++++
t/unit-tests/clar/generate.py | 266 +++++++
t/unit-tests/clar/test/.gitignore | 4 +
t/unit-tests/clar/test/Makefile | 39 +
t/unit-tests/clar/test/clar_test.h | 16 +
t/unit-tests/clar/test/main.c | 40 +
t/unit-tests/clar/test/main.c.sample | 27 +
t/unit-tests/clar/test/resources/test/file | 1 +
t/unit-tests/clar/test/sample.c | 84 ++
t/unit-tests/{t-ctype.c => ctype.c} | 71 +-
t/unit-tests/{t-strvec.c => strvec.c} | 119 ++-
t/unit-tests/unit-test.c | 17 +
t/unit-tests/unit-test.h | 3 +
29 files changed, 3160 insertions(+), 98 deletions(-)
create mode 100644 t/unit-tests/clar-generate.awk
create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml
create mode 100644 t/unit-tests/clar/COPYING
create mode 100644 t/unit-tests/clar/README.md
create mode 100644 t/unit-tests/clar/clar.c
create mode 100644 t/unit-tests/clar/clar.h
create mode 100644 t/unit-tests/clar/clar/fixtures.h
create mode 100644 t/unit-tests/clar/clar/fs.h
create mode 100644 t/unit-tests/clar/clar/print.h
create mode 100644 t/unit-tests/clar/clar/sandbox.h
create mode 100644 t/unit-tests/clar/clar/summary.h
create mode 100755 t/unit-tests/clar/generate.py
create mode 100644 t/unit-tests/clar/test/.gitignore
create mode 100644 t/unit-tests/clar/test/Makefile
create mode 100644 t/unit-tests/clar/test/clar_test.h
create mode 100644 t/unit-tests/clar/test/main.c
create mode 100644 t/unit-tests/clar/test/main.c.sample
create mode 100644 t/unit-tests/clar/test/resources/test/file
create mode 100644 t/unit-tests/clar/test/sample.c
rename t/unit-tests/{t-ctype.c => ctype.c} (71%)
rename t/unit-tests/{t-strvec.c => strvec.c} (54%)
create mode 100644 t/unit-tests/unit-test.c
create mode 100644 t/unit-tests/unit-test.hRange-diff against v3:
1: 78a9cc1162 = 1: 086dd728a7 t: do not pass GIT_TEST_OPTS to unit tests with prove 2: b6c066ee4e = 2: 5c22e0b3b9 t: import the clar unit testing framework 3: 35682b7686 ! 3: e0f99874cc t/clar: fix whitespace errors
@@ Commit message
+ ## t/unit-tests/clar/clar/fs.h ##
+@@ t/unit-tests/clar/clar/fs.h: fs_rm_wait(WCHAR *_wpath)
+ ERROR_PATH_NOT_FOUND == last_error)
+ return 0;
+
+- Sleep(RM_RETRY_DELAY * retries * retries);
++ Sleep(RM_RETRY_DELAY * retries * retries);
+ }
+ while (retries++ <= RM_RETRY_COUNT);
+
+
## t/unit-tests/clar/clar/sandbox.h ##
@@ t/unit-tests/clar/clar/sandbox.h: find_tmp_path(char *buffer, size_t length)
static const size_t var_count = 5;
4: 7a76c21bcb = 4: 75e097dfa4 t/clar: fix compatibility with NonStop 5: 68b3c65951 ! 5: 5b8a64ae79 Makefile: wire up the clar unit testing framework
@@ Makefile: endif
bin-wrappers/%: wrap-for-bin.sh
$(call mkdir_p_parent_template)
+@@ Makefile: CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(LIB_H))
+ HCO = $(patsubst %.h,%.hco,$(CHK_HDRS))
+ HCC = $(HCO:hco=hcc)
+
++$(UNIT_TEST_DIR)/unit-test.hcc: $(UNIT_TEST_DIR)/unit-test.h $(UNIT_TEST_DIR)/clar-decls.h
+ %.hcc: %.h
+ @echo '#include "git-compat-util.h"' >$@
+ @echo '#include "$<"' >>$@
@@ Makefile: endif
artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
6: 4a0888380e = 6: bc4e23d666 t/unit-tests: convert strvec tests to use clar 7: f423b01c05 = 7: 0a7fe8775a t/unit-tests: convert ctype tests to use clar
Signature · 1 line
2.46.0.46.g406f326d27.dirty
PATCH v4 1/7 t: do not pass GIT_TEST_OPTS to unit tests with prove
When using the prove target, we append GIT_TEST_OPTS to the arguments that we execute each of the tests with. This doesn't only include the intended test scripts, but also ends up passing the arguments to our unit tests. This is unintentional though as they do not even know to interpret those arguments, and is inconsistent with how we execute unit tests without prove.
This isn't much of an issue because our current set of unit tests mostly ignore their arguments anyway. With the introduction of clar-based unit tests this is about to become an issue though, as these do parse their command line argument to alter behaviour.
Prepare for this by passing GIT_TEST_OPTS to "run-test.sh" via an environment variable. Like this, we can conditionally forward it to our test scripts, only.
--- t/Makefile | 3 ++- t/run-test.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
t/Makefile +2 -1
@@ -68,7 +68,8 @@ failed: test -z "$$failed" || $(MAKE) $$failed prove: pre-clean check-chainlint $(TEST_LINT) - @echo "*** prove (shell & unit tests) ***"; $(CHAINLINTSUPPRESS) TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) :: $(GIT_TEST_OPTS) + @echo "*** prove (shell & unit tests) ***" + @$(CHAINLINTSUPPRESS) TEST_OPTIONS='$(GIT_TEST_OPTS)' TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) $(MAKE) clean-except-prove-cache $(T):
t/run-test.sh +1 -1
@@ -10,7 +10,7 @@ case "$1" in echo >&2 "ERROR: TEST_SHELL_PATH is empty or not set" exit 1 fi - exec "${TEST_SHELL_PATH}" "$@" + exec "${TEST_SHELL_PATH}" "$@" ${TEST_OPTIONS} ;; *) exec "$@"
Signature · 1 line
2.46.0.46.g406f326d27.dirty
PATCH v4 2/7 t: import the clar unit testing framework
In reply to Patrick SteinhardtImport the clar unit testing framework at commit faa8419 (Merge pull request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework will be wired up in subsequent commits.
--- Documentation/technical/unit-tests.txt | 2 + Makefile | 4 +- t/unit-tests/clar/.github/workflows/ci.yml | 23 + t/unit-tests/clar/COPYING | 15 + t/unit-tests/clar/README.md | 329 ++++++++ t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++ t/unit-tests/clar/clar.h | 173 +++++ t/unit-tests/clar/clar/fixtures.h | 50 ++ t/unit-tests/clar/clar/fs.h | 522 +++++++++++++ t/unit-tests/clar/clar/print.h | 211 ++++++ t/unit-tests/clar/clar/sandbox.h | 154 ++++ t/unit-tests/clar/clar/summary.h | 143 ++++ t/unit-tests/clar/generate.py | 267 +++++++ t/unit-tests/clar/test/.gitignore | 5 + t/unit-tests/clar/test/Makefile | 39 + t/unit-tests/clar/test/clar_test.h | 16 + t/unit-tests/clar/test/main.c | 40 + t/unit-tests/clar/test/main.c.sample | 27 + t/unit-tests/clar/test/resources/test/file | 1 + t/unit-tests/clar/test/sample.c | 84 ++ 20 files changed, 2946 insertions(+), 1 deletion(-) create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml create mode 100644 t/unit-tests/clar/COPYING create mode 100644 t/unit-tests/clar/README.md create mode 100644 t/unit-tests/clar/clar.c create mode 100644 t/unit-tests/clar/clar.h create mode 100644 t/unit-tests/clar/clar/fixtures.h create mode 100644 t/unit-tests/clar/clar/fs.h create mode 100644 t/unit-tests/clar/clar/print.h create mode 100644 t/unit-tests/clar/clar/sandbox.h create mode 100644 t/unit-tests/clar/clar/summary.h create mode 100755 t/unit-tests/clar/generate.py create mode 100644 t/unit-tests/clar/test/.gitignore create mode 100644 t/unit-tests/clar/test/Makefile create mode 100644 t/unit-tests/clar/test/clar_test.h create mode 100644 t/unit-tests/clar/test/main.c create mode 100644 t/unit-tests/clar/test/main.c.sample create mode 100644 t/unit-tests/clar/test/resources/test/file create mode 100644 t/unit-tests/clar/test/sample.c
Documentation/technical/unit-tests.txt +2 -0
@@ -203,6 +203,7 @@ GitHub / GitLab stars to estimate this. :criterion: https://github.com/Snaipe/Criterion[Criterion] :c-tap: https://github.com/rra/c-tap-harness/[C TAP] :check: https://libcheck.github.io/check/[Check] +:clar: https://github.com/clar-test/clar[Clar] [format="csv",options="header",width="33%",subs="specialcharacters,attributes,quotes,macros"] |=====
@@ -212,6 +213,7 @@ Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiqui {criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800 {c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33 {check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973 +{clar},{isc},{false},{partial},{true},{true},{true},{true},{false},{false},{true},1,192 |===== === Additional framework candidates
Makefile +3 -1
@@ -1331,6 +1331,8 @@ THIRD_PARTY_SOURCES += compat/poll/% THIRD_PARTY_SOURCES += compat/regex/% THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% +THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% +THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate
@@ -3260,7 +3262,7 @@ $(SP_OBJ): %.sp: %.c %.o .PHONY: sparse sparse: $(SP_OBJ) -EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% +EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/% ifndef OPENSSL_SHA1 EXCEPT_HDRS += sha1/openssl.h endif
t/unit-tests/clar/.github/workflows/ci.yml new file +23 -0
@@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + strategy: + matrix: + os: [ ubuntu-latest, macos-latest ] + + runs-on: ${{ matrix.os }} + + steps: + - name: Check out + uses: actions/checkout@v2 + - name: Build + run: | + cd test + make
t/unit-tests/clar/COPYING new file +15 -0
@@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2011-2015 Vicent Marti + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
t/unit-tests/clar/README.md new file +329 -0
329 lines not on this page · raw message
t/unit-tests/clar/clar.c new file +842 -0
842 lines not on this page · raw message
t/unit-tests/clar/clar.h new file +173 -0
173 lines not on this page · raw message
t/unit-tests/clar/clar/fixtures.h new file +50 -0
50 lines not on this page · raw message
t/unit-tests/clar/clar/fs.h new file +522 -0
522 lines not on this page · raw message
t/unit-tests/clar/clar/print.h new file +211 -0
211 lines not on this page · raw message
t/unit-tests/clar/clar/sandbox.h new file +154 -0
154 lines not on this page · raw message
t/unit-tests/clar/clar/summary.h new file +143 -0
143 lines not on this page · raw message
t/unit-tests/clar/generate.py new file +267 -0
267 lines not on this page · raw message
t/unit-tests/clar/test/.gitignore new file +5 -0
@@ -0,0 +1,5 @@ +clar.suite +.clarcache +clar_test +*.o +
t/unit-tests/clar/test/Makefile new file +39 -0
@@ -0,0 +1,39 @@ +# +# Copyright (c) Vicent Marti. All rights reserved. +# +# This file is part of clar, distributed under the ISC license. +# For full terms see the included COPYING file. +# + +# +# Set up the path to the clar sources and to the fixtures directory +# +# The fixture path needs to be an absolute path so it can be used +# even after we have chdir'ed into the test directory while testing. +# +CURRENT_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +TEST_DIRECTORY := $(abspath $(dir $(CURRENT_MAKEFILE))) +CLAR_PATH := $(dir $(TEST_DIRECTORY)) +CLAR_FIXTURE_PATH := $(TEST_DIRECTORY)/resources/ + +CFLAGS=-g -I.. -I. -Wall -DCLAR_FIXTURE_PATH=\"$(CLAR_FIXTURE_PATH)\" + +.PHONY: clean + +# list the objects that go into our test +objects = main.o sample.o + +# build the test executable itself +clar_test: $(objects) clar_test.h clar.suite $(CLAR_PATH)clar.c + $(CC) $(CFLAGS) -o $@ "$(CLAR_PATH)clar.c" $(objects) + +# test object files depend on clar macros +$(objects) : $(CLAR_PATH)clar.h + +# build the clar.suite file of test metadata +clar.suite: + python "$(CLAR_PATH)generate.py" . + +# remove all generated files +clean: + $(RM) -rf *.o clar.suite .clarcache clar_test clar_test.dSYM
t/unit-tests/clar/test/clar_test.h new file +16 -0
@@ -0,0 +1,16 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ +#ifndef __CLAR_TEST__ +#define __CLAR_TEST__ + +/* Import the standard clar helper functions */ +#include "clar.h" + +/* Your custom shared includes / defines here */ +extern int global_test_counter; + +#endif
t/unit-tests/clar/test/main.c new file +40 -0
@@ -0,0 +1,40 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ + +#include "clar_test.h" + +/* +* Sample main() for clar tests. +* +* You should write your own main routine for clar tests that does specific +* setup and teardown as necessary for your application. The only required +* line is the call to `clar_test(argc, argv)`, which will execute the test +* suite. If you want to check the return value of the test application, +* your main() should return the same value returned by clar_test(). +*/ + +int global_test_counter =0 ; + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ +int ret; + +/* Your custom initialization here */ + global_test_counter =0 ; + +/* Run the test suite */ + ret =clar_test (argc, argv); + +/* Your custom cleanup here */ +cl_assert_equal_i (8 , global_test_counter); + +return ret; +}
t/unit-tests/clar/test/main.c.sample new file +27 -0
@@ -0,0 +1,27 @@ +/* + * Copyright (c) Vicent Marti. All rights reserved. + * + * This file is part of clar, distributed under the ISC license. + * For full terms see the included COPYING file. + */ + +#include "clar_test.h" + +/* + * Minimal main() for clar tests. + * + * Modify this with any application specific setup or teardown that you need. + * The only required line is the call to `clar_test(argc, argv)`, which will + * execute the test suite. If you want to check the return value of the test + * application, main() should return the same value returned by clar_test(). + */ + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + /* Run the test suite */ + return clar_test(argc, argv); +}
t/unit-tests/clar/test/resources/test/file new file +1 -0
@@ -0,0 +1 @@ +File
t/unit-tests/clar/test/sample.c new file +84 -0
84 lines not on this page · raw message
Signature · 1 line
2.46.0.46.g406f326d27.dirty
PATCH v4 3/7 t/clar: fix whitespace errors
In reply to Patrick SteinhardtFix whitespace errors in the clar that make git-apply(1) unhappy. This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/97
--- t/unit-tests/clar/clar/fs.h | 2 +- t/unit-tests/clar/clar/sandbox.h | 5 ++--- t/unit-tests/clar/generate.py | 1 - t/unit-tests/clar/test/.gitignore | 1 - 4 files changed, 3 insertions(+), 6 deletions(-)
t/unit-tests/clar/clar/fs.h +1 -1
@@ -146,7 +146,7 @@ fs_rm_wait(WCHAR *_wpath) ERROR_PATH_NOT_FOUND == last_error)return 0 ; -Sleep (RM_RETRY_DELAY * retries * retries); +Sleep (RM_RETRY_DELAY * retries * retries); }while (retries++ <=RM_RETRY_COUNT );
t/unit-tests/clar/clar/sandbox.h +2 -3
@@ -25,9 +25,9 @@ find_tmp_path(char *buffer, size_t length)static const size_t var_count =5 ;static const char *env_vars[] = {"CLAR_TMP" ,"TMPDIR" ,"TMP" ,"TEMP" ,"USERPROFILE" - }; + }; -size_t i; +size_t i;for (i =0 ; i < var_count; ++i) {const char *env =getenv (env_vars[i]);
@@ -151,4 +151,3 @@ const char *clar_sandbox_path(void) {return _clar_path; } -
t/unit-tests/clar/generate.py +0 -1
@@ -264,4 +264,3 @@ def write(self): suite.disable(options.excluded) if suite.write(): print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count())) -
t/unit-tests/clar/test/.gitignore +0 -1
@@ -2,4 +2,3 @@ clar.suite .clarcache clar_test *.o -
Signature · 1 line
2.46.0.46.g406f326d27.dirty
PATCH v4 4/7 t/clar: fix compatibility with NonStop
In reply to Patrick SteinhardtThe NonStop platform does not have mkdtemp() available, which we rely on in build_sandbox_path(). Fix this issue by using mktemp() and mkdir() instead on this platform.
This has been cherry-picked from the upstream pull request at [1].
[1]: https://github.com/clar-test/clar/pull/96
--- t/unit-tests/clar/clar/sandbox.h | 6 ++++++ 1 file changed, 6 insertions(+)
t/unit-tests/clar/clar/sandbox.h +6 -0
@@ -120,6 +120,12 @@ static int build_sandbox_path(void)if (_mktemp (_clar_path) ==NULL )return -1 ; +if (mkdir (_clar_path,0700 ) !=0 ) +return -1 ; +#elif defined(__TANDEM) +if (mktemp (_clar_path) ==NULL ) +return -1 ; +if (mkdir (_clar_path,0700 ) !=0 )return -1 ;#elif defined(_WIN32)
Signature · 1 line
2.46.0.46.g406f326d27.dirty
PATCH v4 5/7 Makefile: wire up the clar unit testing framework
In reply to Patrick SteinhardtWire up the clar unit testing framework by introducing a new "unit-tests" executable. In contrast to the existing framework, this will result in a single executable for all test suites. The ability to pick specific tests to execute is retained via functionality built into the clar itself.
Note that we need to be a bit careful about how we need to invalidate our Makefile rules. While we obviously have to regenerate the clar suite when our test suites change, we also have to invalidate it in case any of the test suites gets removed. We do so by using our typical pattern of creating a GIT-TEST-SUITES file that gets updated whenever the set of test suites changes, so that we can easily depend on that file.
Another specialty is that we generate a "clar-decls.h" file. The test functions are neither static, nor do they have external declarations. This is because they are getting parsed via "generate.py", which then creates the external generations that get populated into an array. These declarations are only seen by the main function though.
The consequence is that we will get a bunch of "missing prototypes" errors from our compiler for each of these test functions. To fix those errors, we extract the extern declarations from "clar.suite" and put them into a standalone header that then gets included by each of our unit tests. This gets rid of compiler warnings for every function which has been extracted by "generate.py". More importantly though, it does _not_ get rid of warnings in case a function really isn't being used by anything. Thus, it would cause a compiler error if a function name was mistyped and thus not picked up by "generate.py".
--- .gitignore | 1 + Makefile | 35 +++++++++++++++++++++--- t/Makefile | 1 + t/unit-tests/.gitignore | 2 ++ t/unit-tests/clar-generate.awk | 50 ++++++++++++++++++++++++++++++++++ t/unit-tests/unit-test.c | 18 ++++++++++++ t/unit-tests/unit-test.h | 3 ++ 7 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 t/unit-tests/clar-generate.awk create mode 100644 t/unit-tests/unit-test.c create mode 100644 t/unit-tests/unit-test.h
.gitignore +1 -0
@@ -9,6 +9,7 @@ /GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES /GIT-SPATCH-DEFINES +/GIT-TEST-SUITES /GIT-USER-AGENT /GIT-VERSION-FILE /bin-wrappers/
Makefile +31 -4
@@ -1334,6 +1334,11 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) +UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o +UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/unit-test.o + UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate UNIT_TEST_PROGRAMS += t-hash
@@ -2713,6 +2718,7 @@ OBJECTS += $(XDIFF_OBJS) OBJECTS += $(FUZZ_OBJS) OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS) OBJECTS += $(UNIT_TEST_OBJS) +OBJECTS += $(UNIT_TESTS_OBJS) ifndef NO_CURL OBJECTS += http.o http-walker.o remote-curl.o
@@ -3215,7 +3221,7 @@ endif test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X)) -all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) +all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) bin-wrappers/%: wrap-for-bin.sh $(call mkdir_p_parent_template)
@@ -3279,6 +3285,7 @@ CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(LIB_H)) HCO = $(patsubst %.h,%.hco,$(CHK_HDRS)) HCC = $(HCO:hco=hcc) +$(UNIT_TEST_DIR)/unit-test.hcc: $(UNIT_TEST_DIR)/unit-test.h $(UNIT_TEST_DIR)/clar-decls.h %.hcc: %.h @echo '#include "git-compat-util.h"' >$@ @echo '#include "$<"' >>$@
@@ -3646,7 +3653,7 @@ endif artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \ GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \ - $(UNIT_TEST_PROGS) $(MOFILES) + $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) $(MOFILES) $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \ SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)' test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3702,6 +3709,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) -r .build $(UNIT_TEST_BIN) + $(RM) GIT-TEST-SUITES $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/clar-decls.h $(RM) po/git.pot po/git-core.pot $(RM) git.res $(RM) $(OBJECTS)
@@ -3861,7 +3869,26 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) +GIT-TEST-SUITES: FORCE + @FLAGS='$(UNIT_TESTS_SUITES)'; \ + if test x"$$FLAGS" != x"`cat GIT-TEST-SUITES 2>/dev/null`" ; then \ + echo >&2 " * new test suites"; \ + echo "$$FLAGS" >GIT-TEST-SUITES; \ + fi + +$(UNIT_TEST_DIR)/clar-decls.h: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(UNIT_TESTS_SUITES)) GIT-TEST-SUITES + $(QUIET_GEN)for suite in $(UNIT_TESTS_SUITES); do \ + sed -ne "s/^\(void test_$${suite}__[a-zA-Z_0-9][a-zA-Z_0-9]*(void)$$\)/extern \1;/p" $(UNIT_TEST_DIR)/$$suite.c; \ + done >$@ +$(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h + $(QUIET_GEN)awk -f $(UNIT_TEST_DIR)/clar-generate.awk $< >$(UNIT_TEST_DIR)/clar.suite +$(UNIT_TESTS_OBJS): $(UNIT_TEST_DIR)/clar-decls.h +$(UNIT_TESTS_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR) +$(UNIT_TESTS_PROG): $(UNIT_TEST_DIR)/clar.suite $(UNIT_TESTS_OBJS) $(GITLIBS) GIT-LDFLAGS + $(call mkdir_p_parent_template) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) + .PHONY: build-unit-tests unit-tests -build-unit-tests: $(UNIT_TEST_PROGS) -unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X +build-unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) +unit-tests: $(UNIT_TEST_PROGS) $(UNIT_TESTS_PROG) t/helper/test-tool$X $(MAKE) -C t/ unit-tests
t/Makefile +1 -0
@@ -48,6 +48,7 @@ CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.tes CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl UNIT_TEST_SOURCES = $(wildcard unit-tests/t-*.c) UNIT_TEST_PROGRAMS = $(patsubst unit-tests/%.c,unit-tests/bin/%$(X),$(UNIT_TEST_SOURCES)) +UNIT_TEST_PROGRAMS += unit-tests/bin/unit-tests$(X) UNIT_TESTS = $(sort $(UNIT_TEST_PROGRAMS)) UNIT_TESTS_NO_DIR = $(notdir $(UNIT_TESTS))
t/unit-tests/.gitignore +2 -0
@@ -1 +1,3 @@ /bin +/clar.suite +/clar-decls.h
t/unit-tests/clar-generate.awk new file +50 -0
50 lines not on this page · raw message
t/unit-tests/unit-test.c new file +18 -0
@@ -0,0 +1,18 @@ +#include "unit-test.h" + +int cmd_main (int argc,const char **argv) +{ +const char **argv_copy; +int ret; + +/* Append the "-t" flag such that the tests generate TAP output. */ +ALLOC_ARRAY (argv_copy, argc +2 ); +COPY_ARRAY (argv_copy, argv, argc); + argv_copy[argc++] ="-t" ; + argv_copy[argc] =NULL ; + + ret =clar_test (argc, (char **) argv_copy); + +free (argv_copy); +return ret; +}
t/unit-tests/unit-test.h new file +3 -0
@@ -0,0 +1,3 @@ +#include "git-compat-util.h" +#include "clar/clar.h" +#include "clar-decls.h"
Signature · 1 line
2.46.0.46.g406f326d27.dirty
PATCH v4 6/7 t/unit-tests: convert strvec tests to use clar
In reply to Patrick SteinhardtConvert the strvec tests to use the new clar unit testing framework. This is a first test balloon that demonstrates how the testing infra for clar-based tests looks like.
The tests are part of the "t/unit-tests/bin/unit-tests" binary. When running that binary, it generates TAP output:
# ./t/unit-tests/bin/unit-tests
TAP version 13
# start of suite 1: strvec
ok 1 - strvec::init
ok 2 - strvec::dynamic_init
ok 3 - strvec::clear
ok 4 - strvec::push
ok 5 - strvec::pushft_pushf
ok 6 - strvec::pushl
ok 7 - strvec::pushv
ok 8 - strvec::replace_at_head
ok 9 - strvec::replace_at_tail
ok 10 - strvec::replace_in_between
ok 11 - strvec::replace_with_substring
ok 12 - strvec::remove_at_head
ok 13 - strvec::remove_at_tail
ok 14 - strvec::remove_in_between
ok 15 - strvec::pop_empty_array
ok 16 - strvec::pop_non_empty_array
ok 17 - strvec::split_empty_string
ok 18 - strvec::split_single_item
ok 19 - strvec::split_multiple_items
ok 20 - strvec::split_whitespace_only
ok 21 - strvec::split_multiple_consecutive_whitespaces
ok 22 - strvec::detach
1..22The binary also supports some parameters that allow us to run only a subset of unit tests or alter the output:
$ ./t/unit-tests/bin/unit-tests -h
Usage: ./t/unit-tests/bin/unit-tests [options] Options:
-sname Run only the suite with name (can go to individual test name)
-iname Include the suite with name
-xname Exclude the suite with name
-v Increase verbosity (show suite names)
-q Only report tests that had an error
-Q Quit as soon as a test fails
-t Display results in tap format
-l Print suite names
-r[filename] Write summary file (to the optional filename)
Furthermore, running make unit-tests runs the binary along with all the other unit tests we have.
---
Makefile | 2 +-
t/unit-tests/{t-strvec.c => strvec.c} | 119 ++++++++++----------------
t/unit-tests/unit-test.c | 3 +-
3 files changed, 45 insertions(+), 79 deletions(-)
rename t/unit-tests/{t-strvec.c => strvec.c} (54%)Makefile +1 -1
@@ -1334,6 +1334,7 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_SUITES += strvec UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
@@ -1351,7 +1352,6 @@ UNIT_TEST_PROGRAMS += t-reftable-merged UNIT_TEST_PROGRAMS += t-reftable-record UNIT_TEST_PROGRAMS += t-strbuf UNIT_TEST_PROGRAMS += t-strcmp-offset -UNIT_TEST_PROGRAMS += t-strvec UNIT_TEST_PROGRAMS += t-trailer UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS)) UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
t/unit-tests/strvec.c renamed from t/unit-tests/t-strvec.c +43 -76
261 lines not on this page · raw message
t/unit-tests/unit-test.c +1 -2
@@ -6,10 +6,9 @@ int cmd_main(int argc, const char **argv)int ret;/* Append the "-t" flag such that the tests generate TAP output. */ -ALLOC_ARRAY (argv_copy, argc +2 ); +ALLOC_ARRAY (argv_copy, argc +1 );COPY_ARRAY (argv_copy, argv, argc); argv_copy[argc++] ="-t" ; - argv_copy[argc] =NULL ; ret =clar_test (argc, (char **) argv_copy);
Signature · 1 line
2.46.0.46.g406f326d27.dirty
PATCH v4 7/7 t/unit-tests: convert ctype tests to use clar
In reply to Patrick SteinhardtConvert the ctype tests to use the new clar unit testing framework.
---
Makefile | 2 +-
t/unit-tests/{t-ctype.c => ctype.c} | 71 +++++++++++++++++++++++------
2 files changed, 59 insertions(+), 14 deletions(-)
rename t/unit-tests/{t-ctype.c => ctype.c} (71%)Makefile +1 -1
@@ -1334,13 +1334,13 @@ THIRD_PARTY_SOURCES += sha1dc/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% +UNIT_TESTS_SUITES += ctype UNIT_TESTS_SUITES += strvec UNIT_TESTS_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) UNIT_TESTS_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TESTS_SUITES)) UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/clar/clar.o UNIT_TESTS_OBJS += $(UNIT_TEST_DIR)/unit-test.o -UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate UNIT_TEST_PROGRAMS += t-hash UNIT_TEST_PROGRAMS += t-mem-pool
t/unit-tests/ctype.c renamed from t/unit-tests/t-ctype.c +58 -13
97 lines not on this page · raw message
Signature · 1 line
2.46.0.46.g406f326d27.dirty
Quoted 7 lines · Patrick Steinhardt
Hi,
this is the fourth version of my patch series that introduces the clar testing framework for our unit tests.
Changes compared to v3:
Quoted 8 lines
- Seeing that the overall feedback seems to be rather positive, I've now dropped the "RFC" prefix.
- Fix another whitespace-damaged line in the clar unit test. I've adapted the upstream pull request accordingly.
- Fix
make hdr-check, which failed due to the missing dependency on"clar-decls.h".
Similarly, lack of clar.suite still makes "make sparse" fail, it seems.
$ make NO_REGEX=NoThanks -j32 sparse
...
SP builtin/help.c
CC t/unit-tests/ctype.o
CC t/unit-tests/strvec.o
CC t/unit-tests/clar/clar.o
CC t/unit-tests/unit-test.o
t/unit-tests/clar/clar.c:209:10: fatal error: clar.suite: No such file or directory
209 | #include "clar.suite"
| ^~~~~~~~~~~~
compilation terminated.
make: *** [Makefile:2762: t/unit-tests/clar/clar.o] Error 1
make: *** Waiting for unfinished jobs....The NO_REGEX is there to force us to go check the compat/regex.h as well.
Quoted 8 lines
+@@
Makefile:CHK_HDRS= $(filter-out $(EXCEPT_HDRS),$(LIB_H))
+ HCO = $(patsubst %.h,%.hco,$(CHK_HDRS))
+ HCC = $(HCO:hco=hcc)
+
++$(UNIT_TEST_DIR)/unit-test.hcc: $(UNIT_TEST_DIR)/unit-test.h $(UNIT_TEST_DIR)/clar-decls.h
+ %.hcc: %.h
+ @echo '#include "git-compat-util.h"' >$@
+ @echo '#include "$<"' >>$@
This is a nice addition.
Thanks.
Quoted 35 lines · Junio C Hamano
Quoted 7 lines · Patrick Steinhardt
Hi,
this is the fourth version of my patch series that introduces the clar testing framework for our unit tests.
Changes compared to v3:
Quoted 8 lines
- Seeing that the overall feedback seems to be rather positive, I've now dropped the "RFC" prefix.
- Fix another whitespace-damaged line in the clar unit test. I've adapted the upstream pull request accordingly.
- Fix
make hdr-check, which failed due to the missing dependency on"clar-decls.h".Similarly, lack of clar.suite still makes "make sparse" fail, it seems.
$ make NO_REGEX=NoThanks -j32 sparse ... SP builtin/help.c CC t/unit-tests/ctype.o CC t/unit-tests/strvec.o CC t/unit-tests/clar/clar.o CC t/unit-tests/unit-test.o t/unit-tests/clar/clar.c:209:10: fatal error: clar.suite: No such file or directory 209 | #include "clar.suite" | ^~~~~~~~~~~~ compilation terminated. make: *** [Makefile:2762: t/unit-tests/clar/clar.o] Error 1 make: *** Waiting for unfinished jobs....
Quoted 1 line
The
NO_REGEXis there to force us to go check thecompat/regex.has well.
Huh, OK. Wonder why the CI didn't catch this. I'll have another look, thanks.
Patrick
v5 cover Introduce clar testing framework
In reply to Patrick SteinhardtHi,
this is the fifth version of my patch series that introduces the clar testing framework for our unit tests.
Changes compared to v4:
- The whitespace fixes have been merged upstream, so I've updated the embedded copy of clar and dropped the subsequent patch that fixed them in our copy. The NonStop compatibility fixes have not yet been merged as the pull request needs some more work.
- Both "clar-decls.h" and "clar.suite" are now part of GENERATED_H. This brings removal of these files via "make clean" for free.
- The "sparse" target already depends on GENERATED_H, but in a broken way. I've fixed that in a new commit.
- The "sparse" target no longer checks external sources, including the clar sources.
- The "hdr-check" target now depends on GENERATED_H, as well. This avoids having to manually wire up dependencies on generated headers per file, which seems rather unmaintainable to me.
With this, the "hdr-check" and "sparse" targets all work on my machine now.
Thanks!
Patrick
Patrick Steinhardt (9):
t: do not pass GIT_TEST_OPTS to unit tests with prove
t: import the clar unit testing framework t/clar: fix compatibility with NonStop
Makefile: fix sparse dependency on GENERATED_H
Makefile: make hdr-check depend on generated headers
Makefile: do not use sparse on third-party sources
Makefile: wire up the clar unit testing framework t/unit-tests: convert strvec tests to use clar t/unit-tests: convert ctype tests to use clar
.gitignore | 1 +
Documentation/technical/unit-tests.txt | 2 +
Makefile | 53 +-
t/Makefile | 4 +-
t/run-test.sh | 2 +-
t/unit-tests/.gitignore | 2 +
t/unit-tests/clar-generate.awk | 50 ++
t/unit-tests/clar/.github/workflows/ci.yml | 23 +
t/unit-tests/clar/COPYING | 15 +
t/unit-tests/clar/README.md | 329 ++++++++
t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++
t/unit-tests/clar/clar.h | 173 +++++
t/unit-tests/clar/clar/fixtures.h | 50 ++
t/unit-tests/clar/clar/fs.h | 522 +++++++++++++
t/unit-tests/clar/clar/print.h | 211 ++++++
t/unit-tests/clar/clar/sandbox.h | 159 ++++
t/unit-tests/clar/clar/summary.h | 143 ++++
t/unit-tests/clar/generate.py | 266 +++++++
t/unit-tests/clar/test/.gitignore | 4 +
t/unit-tests/clar/test/Makefile | 39 +
t/unit-tests/clar/test/clar_test.h | 16 +
t/unit-tests/clar/test/main.c | 40 +
t/unit-tests/clar/test/main.c.sample | 27 +
t/unit-tests/clar/test/resources/test/file | 1 +
t/unit-tests/clar/test/sample.c | 84 ++
t/unit-tests/{t-ctype.c => ctype.c} | 71 +-
t/unit-tests/{t-strvec.c => strvec.c} | 119 ++-
t/unit-tests/unit-test.c | 17 +
t/unit-tests/unit-test.h | 3 +
29 files changed, 3166 insertions(+), 102 deletions(-)
create mode 100644 t/unit-tests/clar-generate.awk
create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml
create mode 100644 t/unit-tests/clar/COPYING
create mode 100644 t/unit-tests/clar/README.md
create mode 100644 t/unit-tests/clar/clar.c
create mode 100644 t/unit-tests/clar/clar.h
create mode 100644 t/unit-tests/clar/clar/fixtures.h
create mode 100644 t/unit-tests/clar/clar/fs.h
create mode 100644 t/unit-tests/clar/clar/print.h
create mode 100644 t/unit-tests/clar/clar/sandbox.h
create mode 100644 t/unit-tests/clar/clar/summary.h
create mode 100755 t/unit-tests/clar/generate.py
create mode 100644 t/unit-tests/clar/test/.gitignore
create mode 100644 t/unit-tests/clar/test/Makefile
create mode 100644 t/unit-tests/clar/test/clar_test.h
create mode 100644 t/unit-tests/clar/test/main.c
create mode 100644 t/unit-tests/clar/test/main.c.sample
create mode 100644 t/unit-tests/clar/test/resources/test/file
create mode 100644 t/unit-tests/clar/test/sample.c
rename t/unit-tests/{t-ctype.c => ctype.c} (71%)
rename t/unit-tests/{t-strvec.c => strvec.c} (54%)
create mode 100644 t/unit-tests/unit-test.c
create mode 100644 t/unit-tests/unit-test.hRange-diff against v4:
1: 086dd728a7 ! 1: 832dc0496f t: do not pass GIT_TEST_OPTS to unit tests with prove
@@ Commit message
environment variable. Like this, we can conditionally forward it to our test scripts, only.
+ Signed-off-by: Patrick Steinhardt <[email protected]>
+
## t/Makefile ##
@@ t/Makefile: failed:
test -z "$$failed" || $(MAKE) $$failed
2: 5c22e0b3b9 ! 2: 3690607933 t: import the clar unit testing framework
@@ Metadata
## Commit message ##
t: import the clar unit testing framework
- Import the clar unit testing framework at commit faa8419 (Merge pull
- request #93 from clar-test/ethomson/fixtures, 2023-12-14). The framework
+ Import the clar unit testing framework at commit 1516124 (Merge pull
+ request #97 from pks-t/pks-whitespace-fixes, 2024-08-15). The framework
will be wired up in subsequent commits.
Signed-off-by: Patrick Steinhardt <[email protected]>
@@ t/unit-tests/clar/clar/fs.h (new)
+ ERROR_PATH_NOT_FOUND == last_error)
+ return 0;
+
-+ Sleep(RM_RETRY_DELAY * retries * retries);
++ Sleep(RM_RETRY_DELAY * retries * retries);
+ }
+ while (retries++ <= RM_RETRY_COUNT);
+
@@ t/unit-tests/clar/clar/sandbox.h (new)
+ static const size_t var_count = 5;
+ static const char *env_vars[] = {
+ "CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE"
-+ };
++ };
+
-+ size_t i;
++ size_t i;
+
+ for (i = 0; i < var_count; ++i) {
+ const char *env = getenv(env_vars[i]);
@@ t/unit-tests/clar/clar/sandbox.h (new)
+{
+ return _clar_path;
+}
-+
## t/unit-tests/clar/clar/summary.h (new) ##
@@
@@ t/unit-tests/clar/generate.py (new)
+ suite.disable(options.excluded)
+ if suite.write():
+ print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))
-+
## t/unit-tests/clar/test/.gitignore (new) ##
@@
@@ t/unit-tests/clar/test/.gitignore (new)
+.clarcache
+clar_test
+*.o
-+
## t/unit-tests/clar/test/Makefile (new) ##
@@
4: 75e097dfa4 = 3: db53673294 t/clar: fix compatibility with NonStop
3: e0f99874cc ! 4: b6199c88dd t/clar: fix whitespace errors @@ Metadata
Author: Patrick Steinhardt <[email protected]>
## Commit message ##
- t/clar: fix whitespace errors
-
- Fix whitespace errors in the clar that make git-apply(1) unhappy. This
- has been cherry-picked from the upstream pull request at [1].
-
- [1]: https://github.com/clar-test/clar/pull/97
+ Makefile: fix sparse dependency on GENERATED_H
+
+ The "check" Makefile target is essentially an alias around the "sparse"
+ target. The one difference though is that it will tell users to instead
+ run the "test" target in case they do not have sparse(1) installed, as
+ chances are high that they wanted to execute the test suite rather than
+ doing semantic checks.
+
+ But even though the "check" target ultimately just ends up executing
+ make sparse, it still depends on our generated headers. This does not
+ make any sense though: they are irrelevant for the "test" target advice,
+ and if these headers are required for the "sparse" target they must be
+ declared as a dependency on the aliased target, not the alias.
+
+ But even moving the dependency to the "sparse" target is wrong, as
+ concurrent builds may then end up generating the headers and running
+ sparse concurrently. Instead, we make them a dependency of the specific
+ objects. While that is overly broad, it does ensure correct ordering.
+ The alternative, specifying which file depends on what generated header
+ explicitly, feels rather unmaintainable.
- ## t/unit-tests/clar/clar/fs.h ##
-@@ t/unit-tests/clar/clar/fs.h: fs_rm_wait(WCHAR *_wpath)
- ERROR_PATH_NOT_FOUND == last_error)
- return 0;
-
-- Sleep(RM_RETRY_DELAY * retries * retries);
-+ Sleep(RM_RETRY_DELAY * retries * retries);
- }
- while (retries++ <= RM_RETRY_COUNT);
+ ## Makefile ##
+@@ Makefile: check-sha1:: t/helper/test-tool$X
-
- ## t/unit-tests/clar/clar/sandbox.h ##
-@@ t/unit-tests/clar/clar/sandbox.h: find_tmp_path(char *buffer, size_t length)
- static const size_t var_count = 5;
- static const char *env_vars[] = {
- "CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE"
-- };
-+ };
+ SP_OBJ = $(patsubst %.o,%.sp,$(OBJECTS))
-- size_t i;
-+ size_t i;
+-$(SP_OBJ): %.sp: %.c %.o
++$(SP_OBJ): %.sp: %.c %.o $(GENERATED_H)
+ $(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) \
+ -Wsparse-error \
+ $(SPARSE_FLAGS) $(SP_EXTRA_FLAGS) $< && \
+@@ Makefile: style:
+ git clang-format --style file --diff --extensions c,h
- for (i = 0; i < var_count; ++i) {
- const char *env = getenv(env_vars[i]);
-@@ t/unit-tests/clar/clar/sandbox.h: const char *clar_sandbox_path(void)
- {
- return _clar_path;
- }
--
-
- ## t/unit-tests/clar/generate.py ##
-@@ t/unit-tests/clar/generate.py: def write(self):
- suite.disable(options.excluded)
- if suite.write():
- print("Written clar.suite (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))
--
-
- ## t/unit-tests/clar/test/.gitignore ##
-@@ t/unit-tests/clar/test/.gitignore: clar.suite
- .clarcache
- clar_test
- *.o
--
+ .PHONY: check
+-check: $(GENERATED_H)
++check:
+ @if sparse; \
+ then \
+ echo >&2 "Use 'make sparse' instead"; \
-: ---------- > 5: 06364b2b72 Makefile: make hdr-check depend on generated headers
-: ---------- > 6: 88ea94ce16 Makefile: do not use sparse on third-party sources
5: 5b8a64ae79 ! 7: 05bcb5bef6 Makefile: wire up the clar unit testing framework
@@ .gitignore
/bin-wrappers/
## Makefile ##
+@@ Makefile: REFTABLE_TEST_LIB = reftable/libreftable_test.a
+ GENERATED_H += command-list.h
+ GENERATED_H += config-list.h
+ GENERATED_H += hook-list.h
++GENERATED_H += $(UNIT_TEST_DIR)/clar-decls.h
++GENERATED_H += $(UNIT_TEST_DIR)/clar.suite
+
+ .PHONY: generated-hdrs
+ generated-hdrs: $(GENERATED_H)
@@ Makefile: THIRD_PARTY_SOURCES += sha1dc/%
THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/%
THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/%
@@ Makefile: endif
bin-wrappers/%: wrap-for-bin.sh
$(call mkdir_p_parent_template)
-@@ Makefile: CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(LIB_H))
- HCO = $(patsubst %.h,%.hco,$(CHK_HDRS))
- HCC = $(HCO:hco=hcc)
-
-+$(UNIT_TEST_DIR)/unit-test.hcc: $(UNIT_TEST_DIR)/unit-test.h $(UNIT_TEST_DIR)/clar-decls.h
- %.hcc: %.h
- @echo '#include "git-compat-util.h"' >$@
- @echo '#include "$<"' >>$@
@@ Makefile: endif
artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
@@ Makefile: cocciclean:
clean: profile-clean coverage-clean cocciclean
$(RM) -r .build $(UNIT_TEST_BIN)
-+ $(RM) GIT-TEST-SUITES $(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/clar-decls.h
++ $(RM) GIT-TEST-SUITES
$(RM) po/git.pot po/git-core.pot
$(RM) git.res
$(RM) $(OBJECTS)
6: bc4e23d666 = 8: 8f56b4d626 t/unit-tests: convert strvec tests to use clar
7: 0a7fe8775a = 9: ca09d19fd5 t/unit-tests: convert ctype tests to use clarbase-commit: 406f326d271e0bacecdb00425422c5fa3f314930
Signature · 1 line
2.46.0.46.g406f326d27.dirty
PATCH v5 1/9 t: do not pass GIT_TEST_OPTS to unit tests with prove
When using the prove target, we append GIT_TEST_OPTS to the arguments that we execute each of the tests with. This doesn't only include the intended test scripts, but also ends up passing the arguments to our unit tests. This is unintentional though as they do not even know to interpret those arguments, and is inconsistent with how we execute unit tests without prove.
This isn't much of an issue because our current set of unit tests mostly ignore their arguments anyway. With the introduction of clar-based unit tests this is about to become an issue though, as these do parse their command line argument to alter behaviour.
Prepare for this by passing GIT_TEST_OPTS to "run-test.sh" via an environment variable. Like this, we can conditionally forward it to our test scripts, only.
--- t/Makefile | 3 ++- t/run-test.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
t/Makefile +2 -1
@@ -68,7 +68,8 @@ failed: test -z "$$failed" || $(MAKE) $$failed prove: pre-clean check-chainlint $(TEST_LINT) - @echo "*** prove (shell & unit tests) ***"; $(CHAINLINTSUPPRESS) TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) :: $(GIT_TEST_OPTS) + @echo "*** prove (shell & unit tests) ***" + @$(CHAINLINTSUPPRESS) TEST_OPTIONS='$(GIT_TEST_OPTS)' TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS) $(MAKE) clean-except-prove-cache $(T):
t/run-test.sh +1 -1
@@ -10,7 +10,7 @@ case "$1" in echo >&2 "ERROR: TEST_SHELL_PATH is empty or not set" exit 1 fi - exec "${TEST_SHELL_PATH}" "$@" + exec "${TEST_SHELL_PATH}" "$@" ${TEST_OPTIONS} ;; *) exec "$@"
Signature · 1 line
2.46.0.46.g406f326d27.dirty
PATCH v5 2/9 t: import the clar unit testing framework
In reply to Patrick SteinhardtImport the clar unit testing framework at commit 1516124 (Merge pull request #97 from pks-t/pks-whitespace-fixes, 2024-08-15). The framework will be wired up in subsequent commits.
--- Documentation/technical/unit-tests.txt | 2 + Makefile | 4 +- t/unit-tests/clar/.github/workflows/ci.yml | 23 + t/unit-tests/clar/COPYING | 15 + t/unit-tests/clar/README.md | 329 ++++++++ t/unit-tests/clar/clar.c | 842 +++++++++++++++++++++ t/unit-tests/clar/clar.h | 173 +++++ t/unit-tests/clar/clar/fixtures.h | 50 ++ t/unit-tests/clar/clar/fs.h | 522 +++++++++++++ t/unit-tests/clar/clar/print.h | 211 ++++++ t/unit-tests/clar/clar/sandbox.h | 153 ++++ t/unit-tests/clar/clar/summary.h | 143 ++++ t/unit-tests/clar/generate.py | 266 +++++++ t/unit-tests/clar/test/.gitignore | 4 + t/unit-tests/clar/test/Makefile | 39 + t/unit-tests/clar/test/clar_test.h | 16 + t/unit-tests/clar/test/main.c | 40 + t/unit-tests/clar/test/main.c.sample | 27 + t/unit-tests/clar/test/resources/test/file | 1 + t/unit-tests/clar/test/sample.c | 84 ++ 20 files changed, 2943 insertions(+), 1 deletion(-) create mode 100644 t/unit-tests/clar/.github/workflows/ci.yml create mode 100644 t/unit-tests/clar/COPYING create mode 100644 t/unit-tests/clar/README.md create mode 100644 t/unit-tests/clar/clar.c create mode 100644 t/unit-tests/clar/clar.h create mode 100644 t/unit-tests/clar/clar/fixtures.h create mode 100644 t/unit-tests/clar/clar/fs.h create mode 100644 t/unit-tests/clar/clar/print.h create mode 100644 t/unit-tests/clar/clar/sandbox.h create mode 100644 t/unit-tests/clar/clar/summary.h create mode 100755 t/unit-tests/clar/generate.py create mode 100644 t/unit-tests/clar/test/.gitignore create mode 100644 t/unit-tests/clar/test/Makefile create mode 100644 t/unit-tests/clar/test/clar_test.h create mode 100644 t/unit-tests/clar/test/main.c create mode 100644 t/unit-tests/clar/test/main.c.sample create mode 100644 t/unit-tests/clar/test/resources/test/file create mode 100644 t/unit-tests/clar/test/sample.c
Documentation/technical/unit-tests.txt +2 -0
@@ -203,6 +203,7 @@ GitHub / GitLab stars to estimate this. :criterion: https://github.com/Snaipe/Criterion[Criterion] :c-tap: https://github.com/rra/c-tap-harness/[C TAP] :check: https://libcheck.github.io/check/[Check] +:clar: https://github.com/clar-test/clar[Clar] [format="csv",options="header",width="33%",subs="specialcharacters,attributes,quotes,macros"] |=====
@@ -212,6 +213,7 @@ Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiqui {criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800 {c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33 {check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973 +{clar},{isc},{false},{partial},{true},{true},{true},{true},{false},{false},{true},1,192 |===== === Additional framework candidates
Makefile +3 -1
@@ -1331,6 +1331,8 @@ THIRD_PARTY_SOURCES += compat/poll/% THIRD_PARTY_SOURCES += compat/regex/% THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% +THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% +THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% UNIT_TEST_PROGRAMS += t-ctype UNIT_TEST_PROGRAMS += t-example-decorate
@@ -3260,7 +3262,7 @@ $(SP_OBJ): %.sp: %.c %.o .PHONY: sparse sparse: $(SP_OBJ) -EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% +EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% $(UNIT_TEST_DIR)/clar/% $(UNIT_TEST_DIR)/clar/clar/% ifndef OPENSSL_SHA1 EXCEPT_HDRS += sha1/openssl.h endif
t/unit-tests/clar/.github/workflows/ci.yml new file +23 -0
@@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + strategy: + matrix: + os: [ ubuntu-latest, macos-latest ] + + runs-on: ${{ matrix.os }} + + steps: + - name: Check out + uses: actions/checkout@v2 + - name: Build + run: | + cd test + make
t/unit-tests/clar/COPYING new file +15 -0
@@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2011-2015 Vicent Marti + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
t/unit-tests/clar/README.md new file +329 -0
329 lines not on this page · raw message
t/unit-tests/clar/clar.c new file +842 -0
842 lines not on this page · raw message
t/unit-tests/clar/clar.h new file +173 -0
173 lines not on this page · raw message
t/unit-tests/clar/clar/fixtures.h new file +50 -0
50 lines not on this page · raw message
t/unit-tests/clar/clar/fs.h new file +522 -0
522 lines not on this page · raw message
t/unit-tests/clar/clar/print.h new file +211 -0
211 lines not on this page · raw message
t/unit-tests/clar/clar/sandbox.h new file +153 -0
153 lines not on this page · raw message
t/unit-tests/clar/clar/summary.h new file +143 -0
143 lines not on this page · raw message
t/unit-tests/clar/generate.py new file +266 -0
266 lines not on this page · raw message
t/unit-tests/clar/test/.gitignore new file +4 -0
@@ -0,0 +1,4 @@ +clar.suite +.clarcache +clar_test +*.o
t/unit-tests/clar/test/Makefile new file +39 -0
@@ -0,0 +1,39 @@ +# +# Copyright (c) Vicent Marti. All rights reserved. +# +# This file is part of clar, distributed under the ISC license. +# For full terms see the included COPYING file. +# + +# +# Set up the path to the clar sources and to the fixtures directory +# +# The fixture path needs to be an absolute path so it can be used +# even after we have chdir'ed into the test directory while testing. +# +CURRENT_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +TEST_DIRECTORY := $(abspath $(dir $(CURRENT_MAKEFILE))) +CLAR_PATH := $(dir $(TEST_DIRECTORY)) +CLAR_FIXTURE_PATH := $(TEST_DIRECTORY)/resources/ + +CFLAGS=-g -I.. -I. -Wall -DCLAR_FIXTURE_PATH=\"$(CLAR_FIXTURE_PATH)\" + +.PHONY: clean + +# list the objects that go into our test +objects = main.o sample.o + +# build the test executable itself +clar_test: $(objects) clar_test.h clar.suite $(CLAR_PATH)clar.c + $(CC) $(CFLAGS) -o $@ "$(CLAR_PATH)clar.c" $(objects) + +# test object files depend on clar macros +$(objects) : $(CLAR_PATH)clar.h + +# build the clar.suite file of test metadata +clar.suite: + python "$(CLAR_PATH)generate.py" . + +# remove all generated files +clean: + $(RM) -rf *.o clar.suite .clarcache clar_test clar_test.dSYM
t/unit-tests/clar/test/clar_test.h new file +16 -0
@@ -0,0 +1,16 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ +#ifndef __CLAR_TEST__ +#define __CLAR_TEST__ + +/* Import the standard clar helper functions */ +#include "clar.h" + +/* Your custom shared includes / defines here */ +extern int global_test_counter; + +#endif
t/unit-tests/clar/test/main.c new file +40 -0
@@ -0,0 +1,40 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ + +#include "clar_test.h" + +/* +* Sample main() for clar tests. +* +* You should write your own main routine for clar tests that does specific +* setup and teardown as necessary for your application. The only required +* line is the call to `clar_test(argc, argv)`, which will execute the test +* suite. If you want to check the return value of the test application, +* your main() should return the same value returned by clar_test(). +*/ + +int global_test_counter =0 ; + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ +int ret; + +/* Your custom initialization here */ + global_test_counter =0 ; + +/* Run the test suite */ + ret =clar_test (argc, argv); + +/* Your custom cleanup here */ +cl_assert_equal_i (8 , global_test_counter); + +return ret; +}
t/unit-tests/clar/test/main.c.sample new file +27 -0
@@ -0,0 +1,27 @@ +/* + * Copyright (c) Vicent Marti. All rights reserved. + * + * This file is part of clar, distributed under the ISC license. + * For full terms see the included COPYING file. + */ + +#include "clar_test.h" + +/* + * Minimal main() for clar tests. + * + * Modify this with any application specific setup or teardown that you need. + * The only required line is the call to `clar_test(argc, argv)`, which will + * execute the test suite. If you want to check the return value of the test + * application, main() should return the same value returned by clar_test(). + */ + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + /* Run the test suite */ + return clar_test(argc, argv); +}
t/unit-tests/clar/test/resources/test/file new file +1 -0
@@ -0,0 +1 @@ +File
t/unit-tests/clar/test/sample.c new file +84 -0
84 lines not on this page · raw message
Signature · 1 line
2.46.0.46.g406f326d27.dirty
Re: [PATCH v5 2/9] t: import the clar unit testing framework
Johannes - are these format specifiers supported by the version of MSVCRT that is used by git for windows? c.f. https://lore.kernel.org/git/[email protected]/
t/unit-tests/clar/clar.c line 69
Quoted 10 lines · Patrick Steinhardt
t/unit-tests/clar/clar.c +7
-0+#ifdef _WIN32 +# ifndef PRIuZ +# define PRIuZ "Iu" +# endif +# ifndef PRIxZ +# define PRIxZ "Ix" +# endif
Thanks
Phillip
Quoted 2,585 lines
t/unit-tests/clar/clar.c +772
-0+# if defined(_MSC_VER) || defined(__MINGW32__) +typedef struct stat STAT_T ; +# else +typedef struct _stat STAT_T ; +# endif +#else +# include <sys/wait.h> /* waitpid(2) */ +# include <unistd.h> +# define _MAIN_CC +# define p_snprintf snprintf +# ifndef PRIuZ +# define PRIuZ "zu" +# endif +# ifndef PRIxZ +# define PRIxZ "zx" +# endif +typedef struct stat STAT_T ; +#endif + +#define MAX (x, y) (((x) > (y)) ? (x) : (y)) + +#include "clar.h" + +static void fs_rm (const char *_source); +static void fs_copy (const char *_source,const char *dest); + +#ifdef CLAR_FIXTURE_PATH +static const char * +fixture_path (const char *base,const char *fixture_name); +#endif + +struct clar_error { +const char *file; +const char *function; +size_t line_number; +const char *error_msg; +char *description; + +struct clar_error *next; +}; + +struct clar_explicit { +size_t suite_idx; +const char *filter; + +struct clar_explicit *next; +}; + +struct clar_report { +const char *test; +int test_number; +const char *suite; + +enum cl_test_status status; +time_t start; +double elapsed; + +struct clar_error *errors; +struct clar_error *last_error; + +struct clar_report *next; +}; + +struct clar_summary { +const char *filename; +FILE *fp; +}; + +static struct { +enum cl_test_status test_status; + +const char *active_test; +const char *active_suite; + +int total_skipped; +int total_errors; + +int tests_ran; +int suites_ran; + +enum cl_output_format output_format; + +int report_errors_only; +int exit_on_error; +int verbosity; + +int write_summary; +char *summary_filename; +struct clar_summary *summary; + +struct clar_explicit *explicit; +struct clar_explicit *last_explicit; + +struct clar_report *reports; +struct clar_report *last_report; + +void (*local_cleanup)(void *); +void *local_cleanup_payload; + +jmp_buf trampoline; +int trampoline_enabled; + +cl_trace_cb *pfn_trace_cb; +void *trace_payload; + +} _clar; + +struct clar_func { +const char *name; +void (*ptr)(void ); +}; + +struct clar_suite { +const char *name; +struct clar_func initialize; +struct clar_func cleanup; +const struct clar_func *tests; +size_t test_count; +int enabled; +}; + +/* From clar_print_*.c */ +static void clar_print_init (int test_count,int suite_count,const char *suite_names); +static void clar_print_shutdown (int test_count,int suite_count,int error_count); +static void clar_print_error (int num,const struct clar_report *report,const struct clar_error *error); +static void clar_print_ontest (const char *suite_name,const char *test_name,int test_number,enum cl_test_status failed); +static void clar_print_onsuite (const char *suite_name,int suite_index); +static void clar_print_onabort (const char *msg, ...); + +/* From clar_sandbox.c */ +static void clar_unsandbox (void ); +static int clar_sandbox (void ); + +/* From summary.h */ +static struct clar_summary *clar_summary_init (const char *filename); +static int clar_summary_shutdown (struct clar_summary *fp); + +/* Load the declarations for the test suite */ +#include "clar.suite" + + +#define CL_TRACE (ev) \ + do { \ + if (_clar.pfn_trace_cb) \ + _clar.pfn_trace_cb(ev, \ + _clar.active_suite, \ + _clar.active_test, \ + _clar.trace_payload); \ + } while (0) + +void cl_trace_register (cl_trace_cb *cb,void *payload) +{ + _clar.pfn_trace_cb = cb; + _clar.trace_payload = payload; +} + + +/* Core test functions */ +static void +clar_report_errors (struct clar_report *report) +{ +struct clar_error *error; +int i =1 ; + +for (error = report->errors; error; error = error->next) +clar_print_error (i++, _clar.last_report, error); +} + +static void +clar_report_all (void ) +{ +struct clar_report *report; +struct clar_error *error; +int i =1 ; + +for (report = _clar.reports; report; report = report->next) { +if (report->status !=CL_TEST_FAILURE ) +continue ; + +for (error = report->errors; error; error = error->next) +clar_print_error (i++, report, error); + } +} + +#ifdef WIN32 +# define clar_time DWORD + +static void clar_time_now (clar_time *out) +{ + *out =GetTickCount (); +} + +static double clar_time_diff (clar_time *start,clar_time *end) +{ +return ((double )*end - (double )*start) /1000 ; +} +#else +# include <sys/time.h> + +# define clar_time struct timeval + +static void clar_time_now (clar_time *out) +{ +struct timezone tz; + +gettimeofday (out, &tz); +} + +static double clar_time_diff (clar_time *start,clar_time *end) +{ +return ((double )end->tv_sec + (double )end->tv_usec /1.0E6 ) - + ((double )start->tv_sec + (double )start->tv_usec /1.0E6 ); +} +#endif + +static void +clar_run_test ( +const struct clar_suite *suite, +const struct clar_func *test, +const struct clar_func *initialize, +const struct clar_func *cleanup) +{ +clar_time start, end; + + _clar.trampoline_enabled =1 ; + +CL_TRACE (CL_TRACE__TEST__BEGIN ); + + _clar.last_report->start =time (NULL ); +clar_time_now (&start); + +if (setjmp (_clar.trampoline) ==0 ) { +if (initialize->ptr !=NULL ) + initialize->ptr (); + +CL_TRACE (CL_TRACE__TEST__RUN_BEGIN ); + test->ptr (); +CL_TRACE (CL_TRACE__TEST__RUN_END ); + } + +clar_time_now (&end); + + _clar.trampoline_enabled =0 ; + +if (_clar.last_report->status ==CL_TEST_NOTRUN ) + _clar.last_report->status =CL_TEST_OK ; + + _clar.last_report->elapsed =clar_time_diff (&start, &end); + +if (_clar.local_cleanup !=NULL ) + _clar.local_cleanup (_clar.local_cleanup_payload); + +if (cleanup->ptr !=NULL ) + cleanup->ptr (); + +CL_TRACE (CL_TRACE__TEST__END ); + + _clar.tests_ran++; + +/* remove any local-set cleanup methods */ + _clar.local_cleanup =NULL ; + _clar.local_cleanup_payload =NULL ; + +if (_clar.report_errors_only) { +clar_report_errors (_clar.last_report); + }else { +clar_print_ontest (suite->name, test->name, _clar.tests_ran, _clar.last_report->status); + } +} + +static void +clar_run_suite (const struct clar_suite *suite,const char *filter) +{ +const struct clar_func *test = suite->tests; +size_t i, matchlen; +struct clar_report *report; +int exact =0 ; + +if (!suite->enabled) +return ; + +if (_clar.exit_on_error && _clar.total_errors) +return ; + +if (!_clar.report_errors_only) +clar_print_onsuite (suite->name, ++_clar.suites_ran); + + _clar.active_suite = suite->name; + _clar.active_test =NULL ; +CL_TRACE (CL_TRACE__SUITE_BEGIN ); + +if (filter) { +size_t suitelen =strlen (suite->name); + matchlen =strlen (filter); +if (matchlen <= suitelen) { + filter =NULL ; + }else { + filter += suitelen; +while (*filter ==':' ) + ++filter; + matchlen =strlen (filter); + +if (matchlen && filter[matchlen -1 ] =='$' ) { + exact =1 ; + matchlen--; + } + } + } + +for (i =0 ; i < suite->test_count; ++i) { +if (filter &&strncmp (test[i].name, filter, matchlen)) +continue ; + +if (exact &&strlen (test[i].name) != matchlen) +continue ; + + _clar.active_test = test[i].name; + + report =calloc (1 ,sizeof (struct clar_report )); + report->suite = _clar.active_suite; + report->test = _clar.active_test; + report->test_number = _clar.tests_ran; + report->status =CL_TEST_NOTRUN ; + +if (_clar.reports ==NULL ) + _clar.reports = report; + +if (_clar.last_report !=NULL ) + _clar.last_report->next = report; + + _clar.last_report = report; + +clar_run_test (suite, &test[i], &suite->initialize, &suite->cleanup); + +if (_clar.exit_on_error && _clar.total_errors) +return ; + } + + _clar.active_test =NULL ; +CL_TRACE (CL_TRACE__SUITE_END ); +} + +static void +clar_usage (const char *arg) +{ +printf ("Usage: %s [options]\n\n" , arg); +printf ("Options:\n" ); +printf (" -sname Run only the suite with `name` (can go to individual test name)\n" ); +printf (" -iname Include the suite with `name`\n" ); +printf (" -xname Exclude the suite with `name`\n" ); +printf (" -v Increase verbosity (show suite names)\n" ); +printf (" -q Only report tests that had an error\n" ); +printf (" -Q Quit as soon as a test fails\n" ); +printf (" -t Display results in tap format\n" ); +printf (" -l Print suite names\n" ); +printf (" -r[filename] Write summary file (to the optional filename)\n" ); +exit (-1 ); +} + +static void +clar_parse_args (int argc,char **argv) +{ +int i; + +/* Verify options before execute */ +for (i =1 ; i < argc; ++i) { +char *argument = argv[i]; + +if (argument[0 ] !='-' || argument[1 ] =='\0' + ||strchr ("sixvqQtlr" , argument[1 ]) ==NULL ) { +clar_usage (argv[0 ]); + } + } + +for (i =1 ; i < argc; ++i) { +char *argument = argv[i]; + +switch (argument[1 ]) { +case 's' : +case 'i' : +case 'x' : {/* given suite name */ +int offset = (argument[2 ] =='=' ) ?3 :2 , found =0 ; +char action = argument[1 ]; +size_t j, arglen, suitelen, cmplen; + + argument += offset; + arglen =strlen (argument); + +if (arglen ==0 ) +clar_usage (argv[0 ]); + +for (j =0 ; j < _clar_suite_count; ++j) { + suitelen =strlen (_clar_suites[j].name); + cmplen = (arglen < suitelen) ? arglen : suitelen; + +if (strncmp (argument, _clar_suites[j].name, cmplen) ==0 ) { +int exact = (arglen >= suitelen); + +/* Do we have a real suite prefix separated by a +* trailing '::' or just a matching substring? */ +if (arglen > suitelen && (argument[suitelen] !=':' + || argument[suitelen +1 ] !=':' )) +continue ; + + ++found; + +if (!exact) + _clar.verbosity =MAX (_clar.verbosity,1 ); + +switch (action) { +case 's' : { +struct clar_explicit *explicit = +calloc (1 ,sizeof (struct clar_explicit )); +assert (explicit); + + explicit->suite_idx = j; + explicit->filter = argument; + +if (_clar.explicit ==NULL ) + _clar.explicit = explicit; + +if (_clar.last_explicit !=NULL ) + _clar.last_explicit->next = explicit; + + _clar_suites[j].enabled =1 ; + _clar.last_explicit = explicit; +break ; + } +case 'i' : _clar_suites[j].enabled =1 ;break ; +case 'x' : _clar_suites[j].enabled =0 ;break ; + } + +if (exact) +break ; + } + } + +if (!found) { +clar_print_onabort ("No suite matching '%s' found.\n" , argument); +exit (-1 ); + } +break ; + } + +case 'q' : + _clar.report_errors_only =1 ; +break ; + +case 'Q' : + _clar.exit_on_error =1 ; +break ; + +case 't' : + _clar.output_format =CL_OUTPUT_TAP ; +break ; + +case 'l' : { +size_t j; +printf ("Test suites (use -s<name> to run just one):\n" ); +for (j =0 ; j < _clar_suite_count; ++j) +printf (" %3d: %s\n" , (int )j, _clar_suites[j].name); + +exit (0 ); + } + +case 'v' : + _clar.verbosity++; +break ; + +case 'r' : + _clar.write_summary =1 ; +free (_clar.summary_filename); + _clar.summary_filename = *(argument +2 ) ?strdup (argument +2 ) :NULL ; +break ; + +default : +assert (!"Unexpected commandline argument!" ); + } + } +} + +void +clar_test_init (int argc,char **argv) +{ +const char *summary_env; + +if (argc >1 ) +clar_parse_args (argc, argv); + +clar_print_init ( + (int )_clar_callback_count, + (int )_clar_suite_count, +"" + ); + +if (!_clar.summary_filename && + (summary_env =getenv ("CLAR_SUMMARY" )) !=NULL ) { + _clar.write_summary =1 ; + _clar.summary_filename =strdup (summary_env); + } + +if (_clar.write_summary && !_clar.summary_filename) + _clar.summary_filename =strdup ("summary.xml" ); + +if (_clar.write_summary && + !(_clar.summary =clar_summary_init (_clar.summary_filename))) { +clar_print_onabort ("Failed to open the summary file\n" ); +exit (-1 ); + } + +if (clar_sandbox () <0 ) { +clar_print_onabort ("Failed to sandbox the test runner.\n" ); +exit (-1 ); + } +} + +int +clar_test_run (void ) +{ +size_t i; +struct clar_explicit *explicit; + +if (_clar.explicit) { +for (explicit = _clar.explicit; explicit; explicit = explicit->next) +clar_run_suite (&_clar_suites[explicit->suite_idx], explicit->filter); + }else { +for (i =0 ; i < _clar_suite_count; ++i) +clar_run_suite (&_clar_suites[i],NULL ); + } + +return _clar.total_errors; +} + +void +clar_test_shutdown (void ) +{ +struct clar_explicit *explicit, *explicit_next; +struct clar_report *report, *report_next; + +clar_print_shutdown ( + _clar.tests_ran, + (int )_clar_suite_count, + _clar.total_errors + ); + +clar_unsandbox (); + +if (_clar.write_summary &&clar_summary_shutdown (_clar.summary) <0 ) { +clar_print_onabort ("Failed to write the summary file\n" ); +exit (-1 ); + } + +for (explicit = _clar.explicit; explicit; explicit = explicit_next) { + explicit_next = explicit->next; +free (explicit); + } + +for (report = _clar.reports; report; report = report_next) { + report_next = report->next; +free (report); + } + +free (_clar.summary_filename); +} + +int +clar_test (int argc,char **argv) +{ +int errors; + +clar_test_init (argc, argv); + errors =clar_test_run (); +clar_test_shutdown (); + +return errors; +} + +static void abort_test (void ) +{ +if (!_clar.trampoline_enabled) { +clar_print_onabort ( +"Fatal error: a cleanup method raised an exception." ); +clar_report_errors (_clar.last_report); +exit (-1 ); + } + +CL_TRACE (CL_TRACE__TEST__LONGJMP ); +longjmp (_clar.trampoline,-1 ); +} + +void clar__skip (void ) +{ + _clar.last_report->status =CL_TEST_SKIP ; + _clar.total_skipped++; +abort_test (); +} + +void clar__fail ( +const char *file, +const char *function, +size_t line, +const char *error_msg, +const char *description, +int should_abort) +{ +struct clar_error *error =calloc (1 ,sizeof (struct clar_error )); + +if (_clar.last_report->errors ==NULL ) + _clar.last_report->errors = error; + +if (_clar.last_report->last_error !=NULL ) + _clar.last_report->last_error->next = error; + + _clar.last_report->last_error = error; + + error->file = file; + error->function = function; + error->line_number = line; + error->error_msg = error_msg; + +if (description !=NULL ) + error->description =strdup (description); + + _clar.total_errors++; + _clar.last_report->status =CL_TEST_FAILURE ; + +if (should_abort) +abort_test (); +} + +void clar__assert ( +int condition, +const char *file, +const char *function, +size_t line, +const char *error_msg, +const char *description, +int should_abort) +{ +if (condition) +return ; + +clar__fail (file, function, line, error_msg, description, should_abort); +} + +void clar__assert_equal ( +const char *file, +const char *function, +size_t line, +const char *err, +int should_abort, +const char *fmt, + ...) +{ +va_list args; +char buf[4096 ]; +int is_equal =1 ; + +va_start (args, fmt); + +if (!strcmp ("%s" , fmt)) { + const char *s1 = va_arg(args, const char *); + const char *s2 = va_arg(args, const char *); + is_equal = (!s1 || !s2) ? (s1 == s2) : !strcmp (s1, s2); + +if (!is_equal) { +if (s1 && s2) { +int pos; +for (pos =0 ; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos) +/* find differing byte offset */ ; +p_snprintf (buf,sizeof (buf),"'%s' != '%s' (at byte %d)" , + s1, s2, pos); + }else { +p_snprintf (buf,sizeof (buf),"'%s' != '%s'" , s1, s2); + } + } + } +else if (!strcmp ("%.*s" , fmt)) { + const char *s1 = va_arg(args, const char *); + const char *s2 = va_arg(args, const char *); +int len =va_arg (args, int); + is_equal = (!s1 || !s2) ? (s1 == s2) : !strncmp (s1, s2, len); + +if (!is_equal) { +if (s1 && s2) { +int pos; +for (pos =0 ; s1[pos] == s2[pos] && pos < len; ++pos) +/* find differing byte offset */ ; +p_snprintf (buf,sizeof (buf),"'%.*s' != '%.*s' (at byte %d)" , + len, s1, len, s2, pos); + }else { +p_snprintf (buf,sizeof (buf),"'%.*s' != '%.*s'" , len, s1, len, s2); + } + } + } +else if (!strcmp ("%ls" , fmt)) { + const wchar_t *wcs1 = va_arg(args, const wchar_t *); + const wchar_t *wcs2 = va_arg(args, const wchar_t *); + is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcscmp (wcs1, wcs2); + +if (!is_equal) { +if (wcs1 && wcs2) { +int pos; +for (pos =0 ; wcs1[pos] == wcs2[pos] && wcs1[pos] && wcs2[pos]; ++pos) +/* find differing byte offset */ ; +p_snprintf (buf,sizeof (buf),"'%ls' != '%ls' (at byte %d)" , + wcs1, wcs2, pos); + }else { +p_snprintf (buf,sizeof (buf),"'%ls' != '%ls'" , wcs1, wcs2); + } + } + } +else if (!strcmp ("%.*ls" , fmt)) { + const wchar_t *wcs1 = va_arg(args, const wchar_t *); + const wchar_t *wcs2 = va_arg(args, const wchar_t *); +int len =va_arg (args, int); + is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcsncmp (wcs1, wcs2, len); + +if (!is_equal) { +if (wcs1 && wcs2) { +int pos; +for (pos =0 ; wcs1[pos] == wcs2[pos] && pos < len; ++pos) +/* find differing byte offset */ ; +p_snprintf (buf,sizeof (buf),"'%.*ls' != '%.*ls' (at byte %d)" , + len, wcs1, len, wcs2, pos); + }else { +p_snprintf (buf,sizeof (buf),"'%.*ls' != '%.*ls'" , len, wcs1, len, wcs2); + } + } + } +else if (!strcmp ("%" PRIuZ, fmt) || !strcmp ("%" PRIxZ, fmt)) { +size_t sz1 =va_arg (args, size_t), sz2 =va_arg (args, size_t); + is_equal = (sz1 == sz2); +if (!is_equal) { +int offset =p_snprintf (buf,sizeof (buf), fmt, sz1); +strncat (buf," != " ,sizeof (buf) - offset); +p_snprintf (buf + offset +4 ,sizeof (buf) - offset -4 , fmt, sz2); + } + } +else if (!strcmp ("%p" , fmt)) { + void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *); + is_equal = (p1 == p2); +if (!is_equal) +p_snprintf (buf,sizeof (buf),"%p != %p" , p1, p2); + } +else { +int i1 =va_arg (args, int), i2 =va_arg (args, int); + is_equal = (i1 == i2); +if (!is_equal) { +int offset =p_snprintf (buf,sizeof (buf), fmt, i1); +strncat (buf," != " ,sizeof (buf) - offset); +p_snprintf (buf + offset +4 ,sizeof (buf) - offset -4 , fmt, i2); + } + } + +va_end (args); + +if (!is_equal) +clar__fail (file, function, line, err, buf, should_abort); +} + +void cl_set_cleanup (void (*cleanup)(void *),void *opaque) +{ + _clar.local_cleanup = cleanup; + _clar.local_cleanup_payload = opaque; +} + +#include "clar/sandbox.h" +#include "clar/fixtures.h" +#include "clar/fs.h" +#include "clar/print.h" +#include "clar/summary.h" t/unit-tests/clar/clar.h new file +173
-0@@ -0,0 +1,173 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ +#ifndef __CLAR_TEST_H__ +#define __CLAR_TEST_H__ + +#include <stdlib.h> + +enum cl_test_status { +CL_TEST_OK , +CL_TEST_FAILURE , +CL_TEST_SKIP , +CL_TEST_NOTRUN , +}; + +enum cl_output_format { +CL_OUTPUT_CLAP , +CL_OUTPUT_TAP , +}; + +/** Setup clar environment */ +void clar_test_init (int argc,char *argv[]); +int clar_test_run (void ); +void clar_test_shutdown (void ); + +/** One shot setup & run */ +int clar_test (int argc,char *argv[]); + +const char *clar_sandbox_path (void ); + +void cl_set_cleanup (void (*cleanup)(void *),void *opaque); +void cl_fs_cleanup (void ); + +/** +* cl_trace_* is a hook to provide a simple global tracing +* mechanism. +* +* The goal here is to let main() provide clar-proper +* with a callback to optionally write log info for +* test operations into the same stream used by their +* actual tests. This would let them print test names +* and maybe performance data as they choose. +* +* The goal is NOT to alter the flow of control or to +* override test selection/skipping. (So the callback +* does not return a value.) +* +* The goal is NOT to duplicate the existing +* pass/fail/skip reporting. (So the callback +* does not accept a status/errorcode argument.) +* +*/ +typedef enum cl_trace_event { +CL_TRACE__SUITE_BEGIN , +CL_TRACE__SUITE_END , +CL_TRACE__TEST__BEGIN , +CL_TRACE__TEST__END , +CL_TRACE__TEST__RUN_BEGIN , +CL_TRACE__TEST__RUN_END , +CL_TRACE__TEST__LONGJMP , +}cl_trace_event ; + +typedef void (cl_trace_cb )( +cl_trace_event ev, +const char *suite_name, +const char *test_name, +void *payload); + +/** +* Register a callback into CLAR to send global trace events. +* Pass NULL to disable. +*/ +void cl_trace_register (cl_trace_cb *cb,void *payload); + + +#ifdef CLAR_FIXTURE_PATH +const char *cl_fixture (const char *fixture_name); +void cl_fixture_sandbox (const char *fixture_name); +void cl_fixture_cleanup (const char *fixture_name); +const char *cl_fixture_basename (const char *fixture_name); +#endif + +/** +* Assertion macros with explicit error message +*/ +#define cl_must_pass_ (expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 1) +#define cl_must_fail_ (expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 1) +#define cl_assert_ (expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 1) + +/** +* Check macros with explicit error message +*/ +#define cl_check_pass_ (expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 0) +#define cl_check_fail_ (expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 0) +#define cl_check_ (expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 0) + +/** +* Assertion macros with no error message +*/ +#define cl_must_pass (expr) cl_must_pass_(expr, NULL) +#define cl_must_fail (expr) cl_must_fail_(expr, NULL) +#define cl_assert (expr) cl_assert_(expr, NULL) + +/** +* Check macros with no error message +*/ +#define cl_check_pass (expr) cl_check_pass_(expr, NULL) +#define cl_check_fail (expr) cl_check_fail_(expr, NULL) +#define cl_check (expr) cl_check_(expr, NULL) + +/** +* Forced failure/warning +*/ +#define cl_fail (desc) clar__fail(__FILE__, __func__, __LINE__, "Test failed.", desc, 1) +#define cl_warning (desc) clar__fail(__FILE__, __func__, __LINE__, "Warning during test execution:", desc, 0) + +#define cl_skip () clar__skip() + +/** +* Typed assertion macros +*/ +#define cl_assert_equal_s (s1,s2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2)) +#define cl_assert_equal_s_ (s1,s2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2)) + +#define cl_assert_equal_wcs (wcs1,wcs2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2)) +#define cl_assert_equal_wcs_ (wcs1,wcs2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2)) + +#define cl_assert_equal_strn (s1,s2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len)) +#define cl_assert_equal_strn_ (s1,s2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len)) + +#define cl_assert_equal_wcsn (wcs1,wcs2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len)) +#define cl_assert_equal_wcsn_ (wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len)) + +#define cl_assert_equal_i (i1,i2) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) +#define cl_assert_equal_i_ (i1,i2,note) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2)) +#define cl_assert_equal_i_fmt (i1,i2,fmt) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2)) + +#define cl_assert_equal_b (b1,b2) clar__assert_equal(__FILE__,__func__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0)) + +#define cl_assert_equal_p (p1,p2) clar__assert_equal(__FILE__,__func__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2)) + +void clar__skip (void ); + +void clar__fail ( +const char *file, +const char *func, +size_t line, +const char *error, +const char *description, +int should_abort); + +void clar__assert ( +int condition, +const char *file, +const char *func, +size_t line, +const char *error, +const char *description, +int should_abort); + +void clar__assert_equal ( +const char *file, +const char *func, +size_t line, +const char *err, +int should_abort, +const char *fmt, + ...); + +#endif t/unit-tests/clar/clar/fixtures.h new file +50
-0@@ -0,0 +1,50 @@ +#ifdef CLAR_FIXTURE_PATH +static const char * +fixture_path (const char *base,const char *fixture_name) +{ +static char _path[4096 ]; +size_t root_len; + + root_len =strlen (base); +strncpy (_path, base,sizeof (_path)); + +if (_path[root_len -1 ] !='/' ) + _path[root_len++] ='/' ; + +if (fixture_name[0 ] =='/' ) + fixture_name++; + +strncpy (_path + root_len, + fixture_name, +sizeof (_path) - root_len); + +return _path; +} + +const char *cl_fixture (const char *fixture_name) +{ +return fixture_path (CLAR_FIXTURE_PATH , fixture_name); +} + +void cl_fixture_sandbox (const char *fixture_name) +{ +fs_copy (cl_fixture (fixture_name), _clar_path); +} + +const char *cl_fixture_basename (const char *fixture_name) +{ +const char *p; + +for (p = fixture_name; *p; p++) { +if (p[0 ] =='/' && p[1 ] && p[1 ] !='/' ) + fixture_name = p+1 ; + } + +return fixture_name; +} + +void cl_fixture_cleanup (const char *fixture_name) +{ +fs_rm (fixture_path (_clar_path,cl_fixture_basename (fixture_name))); +} +#endif t/unit-tests/clar/clar/fs.h new file +522
-0@@ -0,0 +1,522 @@ +/* +* By default, use a read/write loop to copy files on POSIX systems. +* On Linux, use sendfile by default as it's slightly faster. On +* macOS, we avoid fcopyfile by default because it's slightly slower. +*/ +#undef USE_FCOPYFILE +#define USE_SENDFILE 1 + +#ifdef _WIN32 + +#ifdef CLAR_WIN32_LONGPATHS +# define CLAR_MAX_PATH 4096 +#else +# define CLAR_MAX_PATH MAX_PATH +#endif + +#define RM_RETRY_COUNT 5 +#define RM_RETRY_DELAY 10 + +#ifdef __MINGW32__ + +/* These security-enhanced functions are not available +* in MinGW, so just use the vanilla ones */ +#define wcscpy_s (a, b, c) wcscpy((a), (c)) +#define wcscat_s (a, b, c) wcscat((a), (c)) + +#endif /* __MINGW32__ */ + +static int +fs__dotordotdot (WCHAR *_tocheck) +{ +return _tocheck[0 ] =='.' && + (_tocheck[1 ] =='\0' || + (_tocheck[1 ] =='.' && _tocheck[2 ] =='\0' )); +} + +static int +fs_rmdir_rmdir (WCHAR *_wpath) +{ +unsigned retries =1 ; + +while (!RemoveDirectoryW (_wpath)) { +/* Only retry when we have retries remaining, and the +* error was ERROR_DIR_NOT_EMPTY. */ +if (retries++ >RM_RETRY_COUNT || +ERROR_DIR_NOT_EMPTY !=GetLastError ()) +return -1 ; + +/* Give whatever has a handle to a child item some time +* to release it before trying again */ +Sleep (RM_RETRY_DELAY * retries * retries); + } + +return 0 ; +} + +static void translate_path (WCHAR *path,size_t path_size) +{ +size_t path_len, i; + +if (wcsncmp (path,L"\\\\?\\" ,4 ) ==0 ) +return ; + + path_len =wcslen (path); +cl_assert (path_size > path_len +4 ); + +for (i = path_len; i >0 ; i--) { +WCHAR c = path[i -1 ]; + +if (c ==L'/' ) + path[i +3 ] =L'\\' ; +else + path[i +3 ] = path[i -1 ]; + } + + path[0 ] =L'\\' ; + path[1 ] =L'\\' ; + path[2 ] =L'?' ; + path[3 ] =L'\\' ; + path[path_len +4 ] =L'\0' ; +} + +static void +fs_rmdir_helper (WCHAR *_wsource) +{ +WCHAR buffer[CLAR_MAX_PATH ]; +HANDLE find_handle; +WIN32_FIND_DATAW find_data; +size_t buffer_prefix_len; + +/* Set up the buffer and capture the length */ +wcscpy_s (buffer,CLAR_MAX_PATH , _wsource); +translate_path (buffer,CLAR_MAX_PATH ); +wcscat_s (buffer,CLAR_MAX_PATH ,L"\\" ); + buffer_prefix_len =wcslen (buffer); + +/* FindFirstFile needs a wildcard to match multiple items */ +wcscat_s (buffer,CLAR_MAX_PATH ,L"*" ); + find_handle =FindFirstFileW (buffer, &find_data); +cl_assert (INVALID_HANDLE_VALUE != find_handle); + +do { +/* FindFirstFile/FindNextFile gives back . and .. +* entries at the beginning */ +if (fs__dotordotdot (find_data.cFileName)) +continue ; + +wcscpy_s (buffer + buffer_prefix_len,CLAR_MAX_PATH - buffer_prefix_len, find_data.cFileName); + +if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) +fs_rmdir_helper (buffer); +else { +/* If set, the +R bit must be cleared before deleting */ +if (FILE_ATTRIBUTE_READONLY & find_data.dwFileAttributes) +cl_assert (SetFileAttributesW (buffer, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY )); + +cl_assert (DeleteFileW (buffer)); + } + } +while (FindNextFileW (find_handle, &find_data)); + +/* Ensure that we successfully completed the enumeration */ +cl_assert (ERROR_NO_MORE_FILES ==GetLastError ()); + +/* Close the find handle */ +FindClose (find_handle); + +/* Now that the directory is empty, remove it */ +cl_assert (0 ==fs_rmdir_rmdir (_wsource)); +} + +static int +fs_rm_wait (WCHAR *_wpath) +{ +unsigned retries =1 ; +DWORD last_error; + +do { +if (INVALID_FILE_ATTRIBUTES ==GetFileAttributesW (_wpath)) + last_error =GetLastError (); +else + last_error =ERROR_SUCCESS ; + +/* Is the item gone? */ +if (ERROR_FILE_NOT_FOUND == last_error || +ERROR_PATH_NOT_FOUND == last_error) +return 0 ; + +Sleep (RM_RETRY_DELAY * retries * retries); + } +while (retries++ <=RM_RETRY_COUNT ); + +return -1 ; +} + +static void +fs_rm (const char *_source) +{ +WCHAR wsource[CLAR_MAX_PATH ]; +DWORD attrs; + +/* The input path is UTF-8. Convert it to wide characters +* for use with the Windows API */ +cl_assert (MultiByteToWideChar (CP_UTF8 , +MB_ERR_INVALID_CHARS , + _source, +-1 ,/* Indicates NULL termination */ + wsource, +CLAR_MAX_PATH )); + +translate_path (wsource,CLAR_MAX_PATH ); + +/* Does the item exist? If not, we have no work to do */ + attrs =GetFileAttributesW (wsource); + +if (INVALID_FILE_ATTRIBUTES == attrs) +return ; + +if (FILE_ATTRIBUTE_DIRECTORY & attrs) +fs_rmdir_helper (wsource); +else { +/* The item is a file. Strip the +R bit */ +if (FILE_ATTRIBUTE_READONLY & attrs) +cl_assert (SetFileAttributesW (wsource, attrs & ~FILE_ATTRIBUTE_READONLY )); + +cl_assert (DeleteFileW (wsource)); + } + +/* Wait for the DeleteFile or RemoveDirectory call to complete */ +cl_assert (0 ==fs_rm_wait (wsource)); +} + +static void +fs_copydir_helper (WCHAR *_wsource,WCHAR *_wdest) +{ +WCHAR buf_source[CLAR_MAX_PATH ], buf_dest[CLAR_MAX_PATH ]; +HANDLE find_handle; +WIN32_FIND_DATAW find_data; +size_t buf_source_prefix_len, buf_dest_prefix_len; + +wcscpy_s (buf_source,CLAR_MAX_PATH , _wsource); +wcscat_s (buf_source,CLAR_MAX_PATH ,L"\\" ); +translate_path (buf_source,CLAR_MAX_PATH ); + buf_source_prefix_len =wcslen (buf_source); + +wcscpy_s (buf_dest,CLAR_MAX_PATH , _wdest); +wcscat_s (buf_dest,CLAR_MAX_PATH ,L"\\" ); +translate_path (buf_dest,CLAR_MAX_PATH ); + buf_dest_prefix_len =wcslen (buf_dest); + +/* Get an enumerator for the items in the source. */ +wcscat_s (buf_source,CLAR_MAX_PATH ,L"*" ); + find_handle =FindFirstFileW (buf_source, &find_data); +cl_assert (INVALID_HANDLE_VALUE != find_handle); + +/* Create the target directory. */ +cl_assert (CreateDirectoryW (_wdest,NULL )); + +do { +/* FindFirstFile/FindNextFile gives back . and .. +* entries at the beginning */ +if (fs__dotordotdot (find_data.cFileName)) +continue ; + +wcscpy_s (buf_source + buf_source_prefix_len,CLAR_MAX_PATH - buf_source_prefix_len, find_data.cFileName); +wcscpy_s (buf_dest + buf_dest_prefix_len,CLAR_MAX_PATH - buf_dest_prefix_len, find_data.cFileName); + +if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) +fs_copydir_helper (buf_source, buf_dest); +else +cl_assert (CopyFileW (buf_source, buf_dest, TRUE)); + } +while (FindNextFileW (find_handle, &find_data)); + +/* Ensure that we successfully completed the enumeration */ +cl_assert (ERROR_NO_MORE_FILES ==GetLastError ()); + +/* Close the find handle */ +FindClose (find_handle); +} + +static void +fs_copy (const char *_source,const char *_dest) +{ +WCHAR wsource[CLAR_MAX_PATH ], wdest[CLAR_MAX_PATH ]; +DWORD source_attrs, dest_attrs; +HANDLE find_handle; +WIN32_FIND_DATAW find_data; + +/* The input paths are UTF-8. Convert them to wide characters +* for use with the Windows API. */ +cl_assert (MultiByteToWideChar (CP_UTF8 , +MB_ERR_INVALID_CHARS , + _source, +-1 , + wsource, +CLAR_MAX_PATH )); + +cl_assert (MultiByteToWideChar (CP_UTF8 , +MB_ERR_INVALID_CHARS , + _dest, +-1 , + wdest, +CLAR_MAX_PATH )); + +translate_path (wsource,CLAR_MAX_PATH ); +translate_path (wdest,CLAR_MAX_PATH ); + +/* Check the source for existence */ + source_attrs =GetFileAttributesW (wsource); +cl_assert (INVALID_FILE_ATTRIBUTES != source_attrs); + +/* Check the target for existence */ + dest_attrs =GetFileAttributesW (wdest); + +if (INVALID_FILE_ATTRIBUTES != dest_attrs) { +/* Target exists; append last path part of source to target. +* Use FindFirstFile to parse the path */ + find_handle =FindFirstFileW (wsource, &find_data); +cl_assert (INVALID_HANDLE_VALUE != find_handle); +wcscat_s (wdest,CLAR_MAX_PATH ,L"\\" ); +wcscat_s (wdest,CLAR_MAX_PATH , find_data.cFileName); +FindClose (find_handle); + +/* Check the new target for existence */ +cl_assert (INVALID_FILE_ATTRIBUTES ==GetFileAttributesW (wdest)); + } + +if (FILE_ATTRIBUTE_DIRECTORY & source_attrs) +fs_copydir_helper (wsource, wdest); +else +cl_assert (CopyFileW (wsource, wdest, TRUE)); +} + +void +cl_fs_cleanup (void ) +{ +#ifdef CLAR_FIXTURE_PATH +fs_rm (fixture_path (_clar_path,"*" )); +#endif +} + +#else + +#include <errno.h> +#include <string.h> +#include <limits.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#if defined(__linux__) +# include <sys/sendfile.h> +#endif + +#if defined(__APPLE__) +# include <copyfile.h> +#endif + +static void basename_r (const char **out,int *out_len,const char *in) +{ +size_t in_len =strlen (in), start_pos; + +for (in_len =strlen (in); in_len; in_len--) { +if (in[in_len -1 ] !='/' ) +break ; + } + +for (start_pos = in_len; start_pos; start_pos--) { +if (in[start_pos -1 ] =='/' ) +break ; + } + +cl_assert (in_len - start_pos <INT_MAX ); + +if (in_len - start_pos >0 ) { + *out = &in[start_pos]; + *out_len = (in_len - start_pos); + }else { + *out ="/" ; + *out_len =1 ; + } +} + +static char *joinpath (const char *dir,const char *base,int base_len) +{ +char *out; +int len; + +if (base_len ==-1 ) { +size_t bl =strlen (base); + +cl_assert (bl <INT_MAX ); + base_len = (int )bl; + } + + len =strlen (dir) + base_len +2 ; +cl_assert (len >0 ); + +cl_assert (out =malloc (len)); +cl_assert (snprintf (out, len,"%s/%.*s" , dir, base_len, base) < len); + +return out; +} + +static void +fs_copydir_helper (const char *source,const char *dest,int dest_mode) +{ +DIR *source_dir; +struct dirent *d; + +mkdir (dest, dest_mode); + +cl_assert_ (source_dir =opendir (source),"Could not open source dir" ); +while ((d = (errno =0 ,readdir (source_dir))) !=NULL ) { +char *child; + +if (!strcmp (d->d_name,"." ) || !strcmp (d->d_name,".." )) +continue ; + + child =joinpath (source, d->d_name,-1 ); +fs_copy (child, dest); +free (child); + } + +cl_assert_ (errno ==0 ,"Failed to iterate source dir" ); + +closedir (source_dir); +} + +static void +fs_copyfile_helper (const char *source,size_t source_len,const char *dest,int dest_mode) +{ +int in, out; + +cl_must_pass ((in =open (source,O_RDONLY ))); +cl_must_pass ((out =open (dest,O_WRONLY |O_CREAT |O_TRUNC , dest_mode))); + +#if USE_FCOPYFILE && defined(__APPLE__) + ((void )(source_len));/* unused */ +cl_must_pass (fcopyfile (in, out,0 ,COPYFILE_DATA )); +#elif USE_SENDFILE && defined(__linux__) + { +ssize_t ret =0 ; + +while (source_len && (ret =sendfile (out, in,NULL , source_len)) >0 ) { + source_len -= (size_t )ret; + } +cl_assert (ret >=0 ); + } +#else + { +char buf[131072 ]; +ssize_t ret; + + ((void )(source_len));/* unused */ + +while ((ret =read (in, buf,sizeof (buf))) >0 ) { +size_t len = (size_t )ret; + +while (len && (ret =write (out, buf, len)) >0 ) { +cl_assert (ret <= (ssize_t )len); + len -= ret; + } +cl_assert (ret >=0 ); + } +cl_assert (ret ==0 ); + } +#endif + +close (in); +close (out); +} + +static void +fs_copy (const char *source,const char *_dest) +{ +char *dbuf =NULL ; +const char *dest =NULL ; +struct stat source_st, dest_st; + +cl_must_pass_ (lstat (source, &source_st),"Failed to stat copy source" ); + +if (lstat (_dest, &dest_st) ==0 ) { +const char *base; +int base_len; + +/* Target exists and is directory; append basename */ +cl_assert (S_ISDIR (dest_st.st_mode)); + +basename_r (&base, &base_len, source); +cl_assert (base_len <INT_MAX ); + + dbuf =joinpath (_dest, base, base_len); + dest = dbuf; + }else if (errno !=ENOENT ) { +cl_fail ("Cannot copy; cannot stat destination" ); + }else { + dest = _dest; + } + +if (S_ISDIR (source_st.st_mode)) { +fs_copydir_helper (source, dest, source_st.st_mode); + }else { +fs_copyfile_helper (source, source_st.st_size, dest, source_st.st_mode); + } + +free (dbuf); +} + +static void +fs_rmdir_helper (const char *path) +{ +DIR *dir; +struct dirent *d; + +cl_assert_ (dir =opendir (path),"Could not open dir" ); +while ((d = (errno =0 ,readdir (dir))) !=NULL ) { +char *child; + +if (!strcmp (d->d_name,"." ) || !strcmp (d->d_name,".." )) +continue ; + + child =joinpath (path, d->d_name,-1 ); +fs_rm (child); +free (child); + } + +cl_assert_ (errno ==0 ,"Failed to iterate source dir" ); +closedir (dir); + +cl_must_pass_ (rmdir (path),"Could not remove directory" ); +} + +static void +fs_rm (const char *path) +{ +struct stat st; + +if (lstat (path, &st)) { +if (errno ==ENOENT ) +return ; + +cl_fail ("Cannot copy; cannot stat destination" ); + } + +if (S_ISDIR (st.st_mode)) { +fs_rmdir_helper (path); + }else { +cl_must_pass (unlink (path)); + } +} + +void +cl_fs_cleanup (void ) +{ +clar_unsandbox (); +clar_sandbox (); +} +#endif t/unit-tests/clar/clar/print.h new file +211
-0@@ -0,0 +1,211 @@ +/* clap: clar protocol, the traditional clar output format */ + +static void clar_print_clap_init (int test_count,int suite_count,const char *suite_names) +{ + (void )test_count; +printf ("Loaded %d suites: %s\n" , (int )suite_count, suite_names); +printf ("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n" ); +} + +static void clar_print_clap_shutdown (int test_count,int suite_count,int error_count) +{ + (void )test_count; + (void )suite_count; + (void )error_count; + +printf ("\n\n" ); +clar_report_all (); +} + +static void clar_print_clap_error (int num,const struct clar_report *report,const struct clar_error *error) +{ +printf (" %d) Failure:\n" , num); + +printf ("%s::%s [%s:%" PRIuZ"]\n" , + report->suite, + report->test, + error->file, + error->line_number); + +printf (" %s\n" , error->error_msg); + +if (error->description !=NULL ) +printf (" %s\n" , error->description); + +printf ("\n" ); +fflush (stdout); +} + +static void clar_print_clap_ontest (const char *suite_name,const char *test_name,int test_number,enum cl_test_status status) +{ + (void )test_name; + (void )test_number; + +if (_clar.verbosity >1 ) { +printf ("%s::%s: " , suite_name, test_name); + +switch (status) { +case CL_TEST_OK :printf ("ok\n" );break ; +case CL_TEST_FAILURE :printf ("fail\n" );break ; +case CL_TEST_SKIP :printf ("skipped" );break ; +case CL_TEST_NOTRUN :printf ("notrun" );break ; + } + }else { +switch (status) { +case CL_TEST_OK :printf ("." );break ; +case CL_TEST_FAILURE :printf ("F" );break ; +case CL_TEST_SKIP :printf ("S" );break ; +case CL_TEST_NOTRUN :printf ("N" );break ; + } + +fflush (stdout); + } +} + +static void clar_print_clap_onsuite (const char *suite_name,int suite_index) +{ +if (_clar.verbosity ==1 ) +printf ("\n%s" , suite_name); + + (void )suite_index; +} + +static void clar_print_clap_onabort (const char *fmt,va_list arg) +{ +vfprintf (stderr, fmt, arg); +} + +/* tap: test anywhere protocol format */ + +static void clar_print_tap_init (int test_count,int suite_count,const char *suite_names) +{ + (void )test_count; + (void )suite_count; + (void )suite_names; +printf ("TAP version 13\n" ); +} + +static void clar_print_tap_shutdown (int test_count,int suite_count,int error_count) +{ + (void )suite_count; + (void )error_count; + +printf ("1..%d\n" , test_count); +} + +static void clar_print_tap_error (int num,const struct clar_report *report,const struct clar_error *error) +{ + (void )num; + (void )report; + (void )error; +} + +static void print_escaped (const char *str) +{ +char *c; + +while ((c =strchr (str,'\'' )) !=NULL ) { +printf ("%.*s" , (int )(c - str), str); +printf ("''" ); + str = c +1 ; + } + +printf ("%s" , str); +} + +static void clar_print_tap_ontest (const char *suite_name,const char *test_name,int test_number,enum cl_test_status status) +{ +const struct clar_error *error = _clar.last_report->errors; + + (void )test_name; + (void )test_number; + +switch (status) { +case CL_TEST_OK : +printf ("ok %d - %s::%s\n" , test_number, suite_name, test_name); +break ; +case CL_TEST_FAILURE : +printf ("not ok %d - %s::%s\n" , test_number, suite_name, test_name); + +printf (" ---\n" ); +printf (" reason: |\n" ); +printf (" %s\n" , error->error_msg); + +if (error->description) +printf (" %s\n" , error->description); + +printf (" at:\n" ); +printf (" file: '" );print_escaped (error->file);printf ("'\n" ); +printf (" line: %" PRIuZ"\n" , error->line_number); +printf (" function: '%s'\n" , error->function); +printf (" ---\n" ); + +break ; +case CL_TEST_SKIP : +case CL_TEST_NOTRUN : +printf ("ok %d - # SKIP %s::%s\n" , test_number, suite_name, test_name); +break ; + } + +fflush (stdout); +} + +static void clar_print_tap_onsuite (const char *suite_name,int suite_index) +{ +printf ("# start of suite %d: %s\n" , suite_index, suite_name); +} + +static void clar_print_tap_onabort (const char *fmt,va_list arg) +{ +printf ("Bail out! " ); +vprintf (fmt, arg); +fflush (stdout); +} + +/* indirection between protocol output selection */ + +#define FN , ...) do { \ + switch (_clar.output_format) { \ + case CL_OUTPUT_CLAP: \ + clar_print_clap_##FN (__VA_ARGS__); \ + break; \ + case CL_OUTPUT_TAP: \ + clar_print_tap_##FN (__VA_ARGS__); \ + break; \ + default: \ + abort(); \ + } \ + } while (0) + +static void clar_print_init (int test_count,int suite_count,const char *suite_names) +{ +static void clar_print_shutdown (int test_count,int suite_count,int error_count) +{ +static void clar_print_error (int num,const struct clar_report *report,const struct clar_error *error) +{ +static void clar_print_ontest (const char *suite_name,const char *test_name,int test_number,enum cl_test_status status) +{ +static void clar_print_onsuite (const char *suite_name,int suite_index) +{ +static void clar_print_onabort (const char *msg, ...) +{ +va_list argp; +va_start (argp, msg); +va_end (argp); +}t/unit-tests/clar/clar/sandbox.h new file +153
-0@@ -0,0 +1,153 @@ +#ifdef __APPLE__ +#include <sys/syslimits.h> +#endif + +static char _clar_path[4096 +1 ]; + +static int +is_valid_tmp_path (const char *path) +{ +STAT_T st; + +if (stat (path, &st) !=0 ) +return 0 ; + +if (!S_ISDIR (st.st_mode)) +return 0 ; + +return (access (path,W_OK ) ==0 ); +} + +static int +find_tmp_path (char *buffer,size_t length) +{ +#ifndef _WIN32 +static const size_t var_count =5 ; +static const char *env_vars[] = { +"CLAR_TMP" ,"TMPDIR" ,"TMP" ,"TEMP" ,"USERPROFILE" + }; + +size_t i; + +for (i =0 ; i < var_count; ++i) { +const char *env =getenv (env_vars[i]); +if (!env) +continue ; + +if (is_valid_tmp_path (env)) { +#ifdef __APPLE__ +if (length >=PATH_MAX &&realpath (env, buffer) !=NULL ) +return 0 ; +#endif +strncpy (buffer, env, length -1 ); + buffer[length -1 ] ='\0' ; +return 0 ; + } + } + +/* If the environment doesn't say anything, try to use /tmp */ +if (is_valid_tmp_path ("/tmp" )) { +#ifdef __APPLE__ +if (length >=PATH_MAX &&realpath ("/tmp" , buffer) !=NULL ) +return 0 ; +#endif +strncpy (buffer,"/tmp" , length -1 ); + buffer[length -1 ] ='\0' ; +return 0 ; + } + +#else +DWORD env_len =GetEnvironmentVariable ("CLAR_TMP" , buffer, (DWORD )length); +if (env_len >0 && env_len < (DWORD )length) +return 0 ; + +if (GetTempPath ((DWORD )length, buffer)) +return 0 ; +#endif + +/* This system doesn't like us, try to use the current directory */ +if (is_valid_tmp_path ("." )) { +strncpy (buffer,"." , length -1 ); + buffer[length -1 ] ='\0' ; +return 0 ; + } + +return -1 ; +} + +static void clar_unsandbox (void ) +{ +if (_clar_path[0 ] =='\0' ) +return ; + +cl_must_pass (chdir (".." )); + +fs_rm (_clar_path); +} + +static int build_sandbox_path (void ) +{ +#ifdef CLAR_TMPDIR +const char path_tail[] =CLAR_TMPDIR "_XXXXXX" ; +#else +const char path_tail[] ="clar_tmp_XXXXXX" ; +#endif + +size_t len; + +if (find_tmp_path (_clar_path,sizeof (_clar_path)) <0 ) +return -1 ; + + len =strlen (_clar_path); + +#ifdef _WIN32 + {/* normalize path to POSIX forward slashes */ +size_t i; +for (i =0 ; i < len; ++i) { +if (_clar_path[i] =='\\' ) + _clar_path[i] ='/' ; + } + } +#endif + +if (_clar_path[len -1 ] !='/' ) { + _clar_path[len++] ='/' ; + } + +strncpy (_clar_path + len, path_tail,sizeof (_clar_path) - len); + +#if defined(__MINGW32__) +if (_mktemp (_clar_path) ==NULL ) +return -1 ; + +if (mkdir (_clar_path,0700 ) !=0 ) +return -1 ; +#elif defined(_WIN32) +if (_mktemp_s (_clar_path,sizeof (_clar_path)) !=0 ) +return -1 ; + +if (mkdir (_clar_path,0700 ) !=0 ) +return -1 ; +#else +if (mkdtemp (_clar_path) ==NULL ) +return -1 ; +#endif + +return 0 ; +} + +static int clar_sandbox (void ) +{ +if (_clar_path[0 ] =='\0' &&build_sandbox_path () <0 ) +return -1 ; + +if (chdir (_clar_path) !=0 ) +return -1 ; + +return 0 ; +} + +const char *clar_sandbox_path (void ) +{ +return _clar_path; +}t/unit-tests/clar/clar/summary.h new file +143
-0@@ -0,0 +1,143 @@ + +#include <stdio.h> +#include <time.h> + +static int clar_summary_close_tag ( +struct clar_summary *summary,const char *tag,int indent) +{ +const char *indt; + +if (indent ==0 ) indt ="" ; +else if (indent ==1 ) indt ="\t" ; +else indt ="\t\t" ; + +return fprintf (summary->fp,"%s</%s>\n" , indt, tag); +} + +static int clar_summary_testsuites (struct clar_summary *summary) +{ +return fprintf (summary->fp,"<testsuites>\n" ); +} + +static int clar_summary_testsuite (struct clar_summary *summary, +int idn,const char *name,time_t timestamp, +int test_count,int fail_count,int error_count) +{ +struct tm *tm =localtime (×tamp); +char iso_dt[20 ]; + +if (strftime (iso_dt,sizeof (iso_dt),"%Y-%m-%dT%H:%M:%S" , tm) ==0 ) +return -1 ; + +return fprintf (summary->fp,"\t<testsuite" +" id=\"%d\"" +" name=\"%s\"" +" hostname=\"localhost\"" +" timestamp=\"%s\"" +" tests=\"%d\"" +" failures=\"%d\"" +" errors=\"%d\">\n" , + idn, name, iso_dt, test_count, fail_count, error_count); +} + +static int clar_summary_testcase (struct clar_summary *summary, +const char *name,const char *classname,double elapsed) +{ +return fprintf (summary->fp, +"\t\t<testcase name=\"%s\" classname=\"%s\" time=\"%.2f\">\n" , + name, classname, elapsed); +} + +static int clar_summary_failure (struct clar_summary *summary, +const char *type,const char *message,const char *desc) +{ +return fprintf (summary->fp, +"\t\t\t<failure type=\"%s\"><![CDATA[%s\n%s]]></failure>\n" , + type, message, desc); +} + +static int clar_summary_skipped (struct clar_summary *summary) +{ +return fprintf (summary->fp,"\t\t\t<skipped />\n" ); +} + +struct clar_summary *clar_summary_init (const char *filename) +{ +struct clar_summary *summary; +FILE *fp; + +if ((fp =fopen (filename,"w" )) ==NULL ) { +perror ("fopen" ); +return NULL ; + } + +if ((summary =malloc (sizeof (struct clar_summary ))) ==NULL ) { +perror ("malloc" ); +fclose (fp); +return NULL ; + } + + summary->filename = filename; + summary->fp = fp; + +return summary; +} + +int clar_summary_shutdown (struct clar_summary *summary) +{ +struct clar_report *report; +const char *last_suite =NULL ; + +if (clar_summary_testsuites (summary) <0 ) + goto on_error; + + report = _clar.reports; +while (report !=NULL ) { +struct clar_error *error = report->errors; + +if (last_suite ==NULL ||strcmp (last_suite, report->suite) !=0 ) { +if (clar_summary_testsuite (summary,0 , report->suite, + report->start, _clar.tests_ran, _clar.total_errors,0 ) <0 ) + goto on_error; + } + + last_suite = report->suite; + +clar_summary_testcase (summary, report->test, report->suite, report->elapsed); + +while (error !=NULL ) { +if (clar_summary_failure (summary,"assert" , + error->error_msg, error->description) <0 ) + goto on_error; + + error = error->next; + } + +if (report->status ==CL_TEST_SKIP ) +clar_summary_skipped (summary); + +if (clar_summary_close_tag (summary,"testcase" ,2 ) <0 ) + goto on_error; + + report = report->next; + +if (!report ||strcmp (last_suite, report->suite) !=0 ) { +if (clar_summary_close_tag (summary,"testsuite" ,1 ) <0 ) + goto on_error; + } + } + +if (clar_summary_close_tag (summary,"testsuites" ,0 ) <0 || +fclose (summary->fp) !=0 ) + goto on_error; + +printf ("written summary file to %s\n" , summary->filename); + +free (summary); +return 0 ; + +on_error: +fclose (summary->fp); +free (summary); +return -1 ; +}t/unit-tests/clar/generate.py new file +266
-0@@ -0,0 +1,266 @@ +#!/usr/bin/env python +# +# Copyright (c) Vicent Marti. All rights reserved. +# +# This file is part of clar, distributed under the ISC license. +# For full terms see the included COPYING file. +# + +from __future__ import with_statement +from string import Template +import re, fnmatch, os, sys, codecs, pickle + +class Module(object): + class Template(object): + def __init__(self, module): + self.module = module + + def _render_callback(self, cb): + if not cb: + return ' { NULL, NULL }' + return ' { "%s", &%s }' % (cb['short_name'], cb['symbol']) + + class DeclarationTemplate(Template): + def render(self): + out = "\n".join("extern %s;" % cb['declaration'] for cb in self.module.callbacks) + "\n" + + for initializer in self.module.initializers: + out += "extern %s;\n" % initializer['declaration'] + + if self.module.cleanup: + out += "extern %s;\n" % self.module.cleanup['declaration'] + + return out + + class CallbacksTemplate(Template): + def render(self): + out = "static const struct clar_func _clar_cb_%s[] = {\n" % self.module.name + out += ",\n".join(self._render_callback(cb) for cb in self.module.callbacks) + out += "\n};\n" + return out + + class InfoTemplate(Template): + def render(self): + templates = [] + + initializers = self.module.initializers + if len(initializers) == 0: + initializers = [ None ] + + for initializer in initializers: + name = self.module.clean_name() + if initializer and initializer['short_name'].startswith('initialize_'): + variant = initializer['short_name'][len('initialize_'):] + name += " (%s)" % variant.replace('_', ' ') + + template = Template( + r""" + { + "${clean_name}", + ${initialize}, + ${cleanup}, + ${cb_ptr}, ${cb_count}, ${enabled} + }""" + ).substitute( + clean_name = name, + initialize = self._render_callback(initializer), + cleanup = self._render_callback(self.module.cleanup), + cb_ptr = "_clar_cb_%s" % self.module.name, + cb_count = len(self.module.callbacks), + enabled = int(self.module.enabled) + ) + templates.append(template) + + return ','.join(templates) + + def __init__(self, name): + self.name = name + + self.mtime = None + self.enabled = True + self.modified = False + + def clean_name(self): + return self.name.replace("_", "::") + + def _skip_comments(self, text): + SKIP_COMMENTS_REGEX = re.compile( + r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', + re.DOTALL | re.MULTILINE) + + def _replacer(match): + s = match.group(0) + return "" if s.startswith('/') else s + + return re.sub(SKIP_COMMENTS_REGEX, _replacer, text) + + def parse(self, contents): + TEST_FUNC_REGEX = r"^(void\s+(test_%s__(\w+))\s*\(\s*void\s*\))\s*\{" + + contents = self._skip_comments(contents) + regex = re.compile(TEST_FUNC_REGEX % self.name, re.MULTILINE) + + self.callbacks = [] + self.initializers = [] + self.cleanup = None + + for (declaration, symbol, short_name) in regex.findall(contents): + data = { + "short_name" : short_name, + "declaration" : declaration, + "symbol" : symbol + } + + if short_name.startswith('initialize'): + self.initializers.append(data) + elif short_name == 'cleanup': + self.cleanup = data + else: + self.callbacks.append(data) + + return self.callbacks != [] + + def refresh(self, path): + self.modified = False + + try: + st = os.stat(path) + + # Not modified + if st.st_mtime == self.mtime: + return True + + self.modified = True + self.mtime = st.st_mtime + + with codecs.open(path, encoding='utf-8') as fp: + raw_content = fp.read() + + except IOError: + return False + + return self.parse(raw_content) + +class TestSuite(object): + + def __init__(self, path, output): + self.path = path + self.output = output + + def should_generate(self, path): + if not os.path.isfile(path): + return True + + if any(module.modified for module in self.modules.values()): + return True + + return False + + def find_modules(self): + modules = [] + for root, _, files in os.walk(self.path): + module_root = root[len(self.path):] + module_root = [c for c in module_root.split(os.sep) if c] + + tests_in_module = fnmatch.filter(files, "*.c") + + for test_file in tests_in_module: + full_path = os.path.join(root, test_file) + module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_") + + modules.append((full_path, module_name)) + + return modules + + def load_cache(self): + path = os.path.join(self.output, '.clarcache') + cache = {} + + try: + fp = open(path, 'rb') + cache = pickle.load(fp) + fp.close() + except (IOError, ValueError): + pass + + return cache + + def save_cache(self): + path = os.path.join(self.output, '.clarcache') + with open(path, 'wb') as cache: + pickle.dump(self.modules, cache) + + def load(self, force = False): + module_data = self.find_modules() + self.modules = {} if force else self.load_cache() + + for path, name in module_data: + if name not in self.modules: + self.modules[name] = Module(name) + + if not self.modules[name].refresh(path): + del self.modules[name] + + def disable(self, excluded): + for exclude in excluded: + for module in self.modules.values(): + name = module.clean_name() + if name.startswith(exclude): + module.enabled = False + module.modified = True + + def suite_count(self): + return sum(max(1, len(m.initializers)) for m in self.modules.values()) + + def callback_count(self): + return sum(len(module.callbacks) for module in self.modules.values()) + + def write(self): + output = os.path.join(self.output, 'clar.suite') + + if not self.should_generate(output): + return False + + with open(output, 'w') as data: + modules = sorted(self.modules.values(), key=lambda module: module.name) + + for module in modules: + t = Module.DeclarationTemplate(module) + data.write(t.render()) + + for module in modules: + t = Module.CallbacksTemplate(module) + data.write(t.render()) + + suites = "static struct clar_suite _clar_suites[] = {" + ','.join( + Module.InfoTemplate(module).render() for module in modules + ) + "\n};\n" + + data.write(suites) + + data.write("static const size_t _clar_suite_count = %d;\n" % self.suite_count()) + data.write("static const size_t _clar_callback_count = %d;\n" % self.callback_count()) + + self.save_cache() + return True + +if __name__ == '__main__': + from optparse import OptionParser + + parser = OptionParser() + parser.add_option('-f', '--force', action="store_true", dest='force', default=False) + parser.add_option('-x', '--exclude', dest='excluded', action='append', default=[]) + parser.add_option('-o', '--output', dest='output') + + options, args = parser.parse_args() + if len(args) > 1: + print("More than one path given") + sys.exit(1) + + path = args.pop() if args else '.' + output = options.output or path + suite = TestSuite(path, output) + suite.load(options.force) + suite.disable(options.excluded) + if suite.write(): + print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))t/unit-tests/clar/test/.gitignore new file +4
-0@@ -0,0 +1,4 @@ +clar.suite +.clarcache +clar_test +*.ot/unit-tests/clar/test/Makefile new file +39
-0@@ -0,0 +1,39 @@ +# +# Copyright (c) Vicent Marti. All rights reserved. +# +# This file is part of clar, distributed under the ISC license. +# For full terms see the included COPYING file. +# + +# +# Set up the path to the clar sources and to the fixtures directory +# +# The fixture path needs to be an absolute path so it can be used +# even after we have chdir'ed into the test directory while testing. +# +CURRENT_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +TEST_DIRECTORY := $(abspath $(dir $(CURRENT_MAKEFILE))) +CLAR_PATH := $(dir $(TEST_DIRECTORY)) +CLAR_FIXTURE_PATH := $(TEST_DIRECTORY)/resources/ + +CFLAGS=-g -I.. -I. -Wall -DCLAR_FIXTURE_PATH=\"$(CLAR_FIXTURE_PATH)\" + +.PHONY: clean + +# list the objects that go into our test +objects = main.o sample.o + +# build the test executable itself +clar_test: $(objects) clar_test.h clar.suite $(CLAR_PATH)clar.c + $(CC) $(CFLAGS) -o $@ "$(CLAR_PATH)clar.c" $(objects) + +# test object files depend on clar macros +$(objects) : $(CLAR_PATH)clar.h + +# build the clar.suite file of test metadata +clar.suite: + python "$(CLAR_PATH)generate.py" . + +# remove all generated files +clean: + $(RM) -rf *.o clar.suite .clarcache clar_test clar_test.dSYMt/unit-tests/clar/test/clar_test.h new file +16
-0@@ -0,0 +1,16 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ +#ifndef __CLAR_TEST__ +#define __CLAR_TEST__ + +/* Import the standard clar helper functions */ +#include "clar.h" + +/* Your custom shared includes / defines here */ +extern int global_test_counter; + +#endif t/unit-tests/clar/test/main.c new file +40
-0@@ -0,0 +1,40 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ + +#include "clar_test.h" + +/* +* Sample main() for clar tests. +* +* You should write your own main routine for clar tests that does specific +* setup and teardown as necessary for your application. The only required +* line is the call to `clar_test(argc, argv)`, which will execute the test +* suite. If you want to check the return value of the test application, +* your main() should return the same value returned by clar_test(). +*/ + +int global_test_counter =0 ; + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ +int ret; + +/* Your custom initialization here */ + global_test_counter =0 ; + +/* Run the test suite */ + ret =clar_test (argc, argv); + +/* Your custom cleanup here */ +cl_assert_equal_i (8 , global_test_counter); + +return ret; +}t/unit-tests/clar/test/main.c.sample new file +27
-0@@ -0,0 +1,27 @@ +/* + * Copyright (c) Vicent Marti. All rights reserved. + * + * This file is part of clar, distributed under the ISC license. + * For full terms see the included COPYING file. + */ + +#include "clar_test.h" + +/* + * Minimal main() for clar tests. + * + * Modify this with any application specific setup or teardown that you need. + * The only required line is the call to `clar_test(argc, argv)`, which will + * execute the test suite. If you want to check the return value of the test + * application, main() should return the same value returned by clar_test(). + */ + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + /* Run the test suite */ + return clar_test(argc, argv); +}t/unit-tests/clar/test/resources/test/file new file +1
-0@@ -0,0 +1 @@ +Filet/unit-tests/clar/test/sample.c new file +84
-0@@ -0,0 +1,84 @@ +#include "clar_test.h" +#include <sys/stat.h> + +static int file_size (const char *filename) +{ +struct stat st; + +if (stat (filename, &st) ==0 ) +return (int )st.st_size; +return -1 ; +} + +void test_sample__initialize (void ) +{ + global_test_counter++; +} + +void test_sample__cleanup (void ) +{ +cl_fixture_cleanup ("test" ); + +cl_assert (file_size ("test/file" ) ==-1 ); +} + +void test_sample__1 (void ) +{ +cl_assert (1 ); +cl_must_pass (0 );/* 0 == success */ +cl_must_fail (-1 );/* <0 == failure */ +cl_must_pass (-1 );/* demonstrate a failing call */ +} + +void test_sample__2 (void ) +{ +cl_fixture_sandbox ("test" ); + +cl_assert (file_size ("test/nonexistent" ) ==-1 ); +cl_assert (file_size ("test/file" ) >0 ); +cl_assert (100 ==101 ); +} + +void test_sample__strings (void ) +{ +const char *actual ="expected" ; +cl_assert_equal_s ("expected" , actual); +cl_assert_equal_s_ ("expected" , actual,"second try with annotation" ); +cl_assert_equal_s_ ("mismatched" , actual,"this one fails" ); +} + +void test_sample__strings_with_length (void ) +{ +const char *actual ="expected" ; +cl_assert_equal_strn ("expected_" , actual,8 ); +cl_assert_equal_strn ("exactly" , actual,2 ); +cl_assert_equal_strn_ ("expected_" , actual,8 ,"with annotation" ); +cl_assert_equal_strn_ ("exactly" , actual,3 ,"this one fails" ); +} + +void test_sample__int (void ) +{ +int value =100 ; +cl_assert_equal_i (100 , value); +cl_assert_equal_i_ (101 , value,"extra note on failing test" ); +} + +void test_sample__int_fmt (void ) +{ +int value =100 ; +cl_assert_equal_i_fmt (022 , value,"%04o" ); +} + +void test_sample__bool (void ) +{ +int value =100 ; +cl_assert_equal_b (1 , value);/* test equality as booleans */ +cl_assert_equal_b (0 , value); +} + +void test_sample__ptr (void ) +{ +const char *actual ="expected" ; +cl_assert_equal_p (actual, actual);/* pointers to same object */ +cl_assert_equal_p (&actual, actual); +}
Re: [PATCH v5 2/9] t: import the clar unit testing framework
Hi Phillip,
thank you for looping me in; I meant to answer earlier.
I am very, very happy to see that there is a chance to adopt a test framework like clar. Therefore, I am very interested in assisting in every which way I can.
Quoted 3 lines · Phillip Wood
Johannes - are these format specifiers supported by the version of MSVCRT that is used by git for windows? c.f.
https://lore.kernel.org/git/[email protected]/
The format specifiers _should_ work well both with GCC and Visual C. What did not work well was using struct stat with _stat() (note that the latter has a leading underscore and the former does not).
There were other issues, top of which was the bit tricky part to adapt the CMakeLists.txt definition, in particular the part where clar.suite is generated. But I managed to squeeze in that work somehow and Patrick was kind enough to already integrate the patches.
Ciao,
Johannes
Quoted 2,756 lines · Patrick Steinhardt
t/unit-tests/clar/clar.c line 69
Quoted 10 lines
t/unit-tests/clar/clar.c +7
-0+#ifdef _WIN32 +# ifndef PRIuZ +# define PRIuZ "Iu" +# endif +# ifndef PRIxZ +# define PRIxZ "Ix" +# endif Thanks
Phillip
Quoted 2,739 lines
t/unit-tests/clar/clar.c +123
-0+# if defined(_MSC_VER) || defined(__MINGW32__) +typedef struct stat STAT_T ; +# else +typedef struct _stat STAT_T ; +# endif +#else +# include <sys/wait.h> /* waitpid(2) */ +# include <unistd.h> +# define _MAIN_CC +# define p_snprintf snprintf +# ifndef PRIuZ +# define PRIuZ "zu" +# endif +# ifndef PRIxZ +# define PRIxZ "zx" +# endif +typedef struct stat STAT_T ; +#endif + +#define MAX (x, y) (((x) > (y)) ? (x) : (y)) + +#include "clar.h" + +static void fs_rm (const char *_source); +static void fs_copy (const char *_source,const char *dest); + +#ifdef CLAR_FIXTURE_PATH +static const char * +fixture_path (const char *base,const char *fixture_name); +#endif + +struct clar_error { +const char *file; +const char *function; +size_t line_number; +const char *error_msg; +char *description; + +struct clar_error *next; +}; + +struct clar_explicit { +size_t suite_idx; +const char *filter; + +struct clar_explicit *next; +}; + +struct clar_report { +const char *test; +int test_number; +const char *suite; + +enum cl_test_status status; +time_t start; +double elapsed; + +struct clar_error *errors; +struct clar_error *last_error; + +struct clar_report *next; +}; + +struct clar_summary { +const char *filename; +FILE *fp; +}; + +static struct { +enum cl_test_status test_status; + +const char *active_test; +const char *active_suite; + +int total_skipped; +int total_errors; + +int tests_ran; +int suites_ran; + +enum cl_output_format output_format; + +int report_errors_only; +int exit_on_error; +int verbosity; + +int write_summary; +char *summary_filename; +struct clar_summary *summary; + +struct clar_explicit *explicit; +struct clar_explicit *last_explicit; + +struct clar_report *reports; +struct clar_report *last_report; + +void (*local_cleanup)(void *); +void *local_cleanup_payload; + +jmp_buf trampoline; +int trampoline_enabled; + +cl_trace_cb *pfn_trace_cb; +void *trace_payload; + +} _clar; + +struct clar_func { +const char *name; +void (*ptr)(void ); +}; + +struct clar_suite { +const char *name; +struct clar_func initialize; +struct clar_func cleanup; +const struct clar_func *tests; +size_t test_count; +int enabled; +}; + +/* From clar_print_*.c */ +static void clar_print_init(int test_count, int suite_count, const char
*suite_names);
+static voidclar_print_shutdown(int test_count, int suite_count, int error_count);
+static voidclar_print_error(int num, const struct clar_report *report, const struct clar_error *error);
+static voidclar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status failed);t/unit-tests/clar/clar.c +16
-0+static void clar_print_onsuite (const char *suite_name,int suite_index); +static void clar_print_onabort (const char *msg, ...); + +/* From clar_sandbox.c */ +static void clar_unsandbox (void ); +static int clar_sandbox (void ); + +/* From summary.h */ +static struct clar_summary *clar_summary_init (const char *filename); +static int clar_summary_shutdown (struct clar_summary *fp); + +/* Load the declarations for the test suite */ +#include "clar.suite" + + +#define CL_TRACE (ev)\
t/unit-tests/clar/clar.c +1
-0+ do {\
t/unit-tests/clar/clar.c +1
-0+if (_clar.pfn_trace_cb)\
t/unit-tests/clar/clar.c +1
-0+ _clar.pfn_trace_cb(ev,\
t/unit-tests/clar/clar.c +1
-0+ _clar.active_suite,\
t/unit-tests/clar/clar.c +1
-0+ _clar.active_test,\
+
_clar.trace_payload); \t/unit-tests/clar/clar.c +119
-0+ }while (0 ) + +void cl_trace_register(cl_trace_cb *cb, void *payload) +{ + _clar.pfn_trace_cb = cb; + _clar.trace_payload = payload; +} + + +/* Core test functions */ +static void +clar_report_errors (struct clar_report *report) +{ +struct clar_error *error; +int i =1 ; + +for (error = report->errors; error; error = error->next) +clar_print_error (i++, _clar.last_report, error); +} + +static void +clar_report_all (void ) +{ +struct clar_report *report; +struct clar_error *error; +int i =1 ; + +for (report = _clar.reports; report; report = report->next) { +if (report->status !=CL_TEST_FAILURE ) +continue ; + +for (error = report->errors; error; error = error->next) +clar_print_error (i++, report, error); + } +} + +#ifdef WIN32 +# define clar_time DWORD + +static void clar_time_now (clar_time *out) +{ + *out =GetTickCount (); +} + +static double clar_time_diff (clar_time *start,clar_time *end) +{ +return ((double )*end - (double )*start) /1000 ; +} +#else +# include <sys/time.h> + +# define clar_time struct timeval + +static void clar_time_now (clar_time *out) +{ +struct timezone tz; + +gettimeofday (out, &tz); +} + +static double clar_time_diff (clar_time *start,clar_time *end) +{ +return ((double )end->tv_sec + (double )end->tv_usec /1.0E6 ) - + ((double )start->tv_sec + (double )start->tv_usec /1.0E6 ); +} +#endif + +static void +clar_run_test ( +const struct clar_suite *suite, +const struct clar_func *test, +const struct clar_func *initialize, +const struct clar_func *cleanup) +{ +clar_time start, end; + + _clar.trampoline_enabled =1 ; + +CL_TRACE (CL_TRACE__TEST__BEGIN ); + + _clar.last_report->start =time (NULL ); +clar_time_now (&start); + +if (setjmp (_clar.trampoline) ==0 ) { +if (initialize->ptr !=NULL ) + initialize->ptr (); + +CL_TRACE (CL_TRACE__TEST__RUN_BEGIN ); + test->ptr (); +CL_TRACE (CL_TRACE__TEST__RUN_END ); + } + +clar_time_now (&end); + + _clar.trampoline_enabled =0 ; + +if (_clar.last_report->status ==CL_TEST_NOTRUN ) + _clar.last_report->status =CL_TEST_OK ; + + _clar.last_report->elapsed =clar_time_diff (&start, &end); + +if (_clar.local_cleanup !=NULL ) + _clar.local_cleanup (_clar.local_cleanup_payload); + +if (cleanup->ptr !=NULL ) + cleanup->ptr (); + +CL_TRACE (CL_TRACE__TEST__END ); + + _clar.tests_ran++; + +/* remove any local-set cleanup methods */ + _clar.local_cleanup =NULL ; + _clar.local_cleanup_payload =NULL ; + +if (_clar.report_errors_only) { +clar_report_errors (_clar.last_report); + }else { + clar_print_ontest(suite->name, test->name, _clar.tests_ran,
_clar.last_report->status);t/unit-tests/clar/clar.c +66
-0+ } +} + +static void +clar_run_suite (const struct clar_suite *suite,const char *filter) +{ +const struct clar_func *test = suite->tests; +size_t i, matchlen; +struct clar_report *report; +int exact =0 ; + +if (!suite->enabled) +return ; + +if (_clar.exit_on_error && _clar.total_errors) +return ; + +if (!_clar.report_errors_only) +clar_print_onsuite (suite->name, ++_clar.suites_ran); + + _clar.active_suite = suite->name; + _clar.active_test =NULL ; +CL_TRACE (CL_TRACE__SUITE_BEGIN ); + +if (filter) { +size_t suitelen =strlen (suite->name); + matchlen =strlen (filter); +if (matchlen <= suitelen) { + filter =NULL ; + }else { + filter += suitelen; +while (*filter ==':' ) + ++filter; + matchlen =strlen (filter); + +if (matchlen && filter[matchlen -1 ] =='$' ) { + exact =1 ; + matchlen--; + } + } + } + +for (i =0 ; i < suite->test_count; ++i) { +if (filter &&strncmp (test[i].name, filter, matchlen)) +continue ; + +if (exact &&strlen (test[i].name) != matchlen) +continue ; + + _clar.active_test = test[i].name; + + report =calloc (1 ,sizeof (struct clar_report )); + report->suite = _clar.active_suite; + report->test = _clar.active_test; + report->test_number = _clar.tests_ran; + report->status =CL_TEST_NOTRUN ; + +if (_clar.reports ==NULL ) + _clar.reports = report; + +if (_clar.last_report !=NULL ) + _clar.last_report->next = report; + + _clar.last_report = report; + + clar_run_test(suite, &test[i], &suite->initialize,
&suite->cleanup);t/unit-tests/clar/clar.c +15
-0+ +if (_clar.exit_on_error && _clar.total_errors) +return ; + } + + _clar.active_test =NULL ; +CL_TRACE (CL_TRACE__SUITE_END ); +} + +static void +clar_usage (const char *arg) +{ +printf ("Usage: %s [options]\n\n" , arg); +printf ("Options:\n" ); + printf(" -sname Run only the suite with `name` (can go toindividual test name)\n");
t/unit-tests/clar/clar.c +8
-0+printf (" -iname Include the suite with `name`\n" ); +printf (" -xname Exclude the suite with `name`\n" ); +printf (" -v Increase verbosity (show suite names)\n" ); +printf (" -q Only report tests that had an error\n" ); +printf (" -Q Quit as soon as a test fails\n" ); +printf (" -t Display results in tap format\n" ); +printf (" -l Print suite names\n" ); + printf(" -r[filename] Write summary file (to the optionalfilename)\n");
t/unit-tests/clar/clar.c +38
-0+exit (-1 ); +} + +static void +clar_parse_args (int argc,char **argv) +{ +int i; + +/* Verify options before execute */ +for (i =1 ; i < argc; ++i) { +char *argument = argv[i]; + +if (argument[0 ] !='-' || argument[1 ] =='\0' + ||strchr ("sixvqQtlr" , argument[1 ]) ==NULL ) { +clar_usage (argv[0 ]); + } + } + +for (i =1 ; i < argc; ++i) { +char *argument = argv[i]; + +switch (argument[1 ]) { +case 's' : +case 'i' : +case 'x' : {/* given suite name */ +int offset = (argument[2 ] =='=' ) ?3 :2 , found =0 ; +char action = argument[1 ]; +size_t j, arglen, suitelen, cmplen; + + argument += offset; + arglen =strlen (argument); + +if (arglen ==0 ) +clar_usage (argv[0 ]); + +for (j =0 ; j < _clar_suite_count; ++j) { + suitelen =strlen (_clar_suites[j].name); + cmplen = (arglen < suitelen) ? arglen :suitelen;
t/unit-tests/clar/clar.c +2
-0+ + if (strncmp(argument, _clar_suites[j].name,cmplen) == 0) {
t/unit-tests/clar/clar.c +3
-0+int exact = (arglen >= suitelen); + +/* Do we have a real suite prefix separated by a
t/unit-tests/clar/clar.c +1
-0+* trailing '::' or just a matching substring? */
t/unit-tests/clar/clar.c +1
-0+ if (arglen > suitelen &&(argument[suitelen] != ':'
t/unit-tests/clar/clar.c +1
-0+ || argument[suitelen + 1]!= ':'))
t/unit-tests/clar/clar.c +6
-0+continue ; + + ++found; + +if (!exact) + _clar.verbosity =
MAX(_clar.verbosity, 1);t/unit-tests/clar/clar.c +4
-0+ +switch (action) { +case 's' : { +struct clar_explicit *explicit=
t/unit-tests/clar/clar.c +1
-0+ calloc(1,
sizeof(struct clar_explicit));t/unit-tests/clar/clar.c +7
-0+assert (explicit); + + explicit->suite_idx = j; + explicit->filter = argument; + +if (_clar.explicit ==NULL ) + _clar.explicit =explicit;
t/unit-tests/clar/clar.c +2
-0+ + if (_clar.last_explicit !=NULL)
+_clar.last_explicit->next= explicit;t/unit-tests/clar/clar.c +3
-0+ + _clar_suites[j].enabled =1 ; + _clar.last_explicit =explicit;
t/unit-tests/clar/clar.c +3
-0+break ; + } +case 'i' : _clar_suites[j].enabled =1 ;break;
t/unit-tests/clar/clar.c +1
-0+case 'x' : _clar_suites[j].enabled =0 ;break;
t/unit-tests/clar/clar.c +9
-0+ } + +if (exact) +break ; + } + } + +if (!found) { + clar_print_onabort("No suite matching '%s'found.\n", argument);
t/unit-tests/clar/clar.c +20
-0+exit (-1 ); + } +break ; + } + +case 'q' : + _clar.report_errors_only =1 ; +break ; + +case 'Q' : + _clar.exit_on_error =1 ; +break ; + +case 't' : + _clar.output_format =CL_OUTPUT_TAP ; +break ; + +case 'l' : { +size_t j; + printf("Test suites (use -s<name> to run justone):\n");
t/unit-tests/clar/clar.c +2
-0+for (j =0 ; j < _clar_suite_count; ++j) + printf(" %3d: %s\n", (int)j,_clar_suites[j].name);
t/unit-tests/clar/clar.c +12
-0+ +exit (0 ); + } + +case 'v' : + _clar.verbosity++; +break ; + +case 'r' : + _clar.write_summary =1 ; +free (_clar.summary_filename); + _clar.summary_filename = *(argument + 2) ?strdup(argument + 2) : NULL;
t/unit-tests/clar/clar.c +51
-0+break ; + +default : +assert (!"Unexpected commandline argument!" ); + } + } +} + +void +clar_test_init (int argc,char **argv) +{ +const char *summary_env; + +if (argc >1 ) +clar_parse_args (argc, argv); + +clar_print_init ( + (int )_clar_callback_count, + (int )_clar_suite_count, +"" + ); + +if (!_clar.summary_filename && + (summary_env =getenv ("CLAR_SUMMARY" )) !=NULL ) { + _clar.write_summary =1 ; + _clar.summary_filename =strdup (summary_env); + } + +if (_clar.write_summary && !_clar.summary_filename) + _clar.summary_filename =strdup ("summary.xml" ); + +if (_clar.write_summary && + !(_clar.summary =clar_summary_init (_clar.summary_filename))) { +clar_print_onabort ("Failed to open the summary file\n" ); +exit (-1 ); + } + +if (clar_sandbox () <0 ) { +clar_print_onabort ("Failed to sandbox the test runner.\n" ); +exit (-1 ); + } +} + +int +clar_test_run(void) +{ + size_t i; + struct clar_explicit *explicit; + + if (_clar.explicit) { + for (explicit = _clar.explicit; explicit; explicit =
explicit->next)t/unit-tests/clar/clar.c +1
-0+ clar_run_suite(&_clar_suites[explicit->suite_idx],
explicit->filter);t/unit-tests/clar/clar.c +57
-0+ }else { +for (i =0 ; i < _clar_suite_count; ++i) +clar_run_suite (&_clar_suites[i],NULL ); + } + +return _clar.total_errors; +} + +void +clar_test_shutdown (void ) +{ +struct clar_explicit *explicit, *explicit_next; +struct clar_report *report, *report_next; + +clar_print_shutdown ( + _clar.tests_ran, + (int )_clar_suite_count, + _clar.total_errors + ); + +clar_unsandbox (); + +if (_clar.write_summary &&clar_summary_shutdown (_clar.summary) <0 ) { +clar_print_onabort ("Failed to write the summary file\n" ); +exit (-1 ); + } + +for (explicit = _clar.explicit; explicit; explicit = explicit_next) { + explicit_next = explicit->next; +free (explicit); + } + +for (report = _clar.reports; report; report = report_next) { + report_next = report->next; +free (report); + } + +free (_clar.summary_filename); +} + +int +clar_test (int argc,char **argv) +{ +int errors; + +clar_test_init (argc, argv); + errors =clar_test_run (); +clar_test_shutdown (); + +return errors; +} + +static void abort_test (void ) +{ +if (!_clar.trampoline_enabled) { + clar_print_onabort( + "Fatal error: a cleanup method raised anexception.");
t/unit-tests/clar/clar.c +61
-0+clar_report_errors (_clar.last_report); +exit (-1 ); + } + +CL_TRACE (CL_TRACE__TEST__LONGJMP ); +longjmp (_clar.trampoline,-1 ); +} + +void clar__skip (void ) +{ + _clar.last_report->status =CL_TEST_SKIP ; + _clar.total_skipped++; +abort_test (); +} + +void clar__fail ( +const char *file, +const char *function, +size_t line, +const char *error_msg, +const char *description, +int should_abort) +{ +struct clar_error *error =calloc (1 ,sizeof (struct clar_error )); + +if (_clar.last_report->errors ==NULL ) + _clar.last_report->errors = error; + +if (_clar.last_report->last_error !=NULL ) + _clar.last_report->last_error->next = error; + + _clar.last_report->last_error = error; + + error->file = file; + error->function = function; + error->line_number = line; + error->error_msg = error_msg; + +if (description !=NULL ) + error->description =strdup (description); + + _clar.total_errors++; + _clar.last_report->status =CL_TEST_FAILURE ; + +if (should_abort) +abort_test (); +} + +void clar__assert ( +int condition, +const char *file, +const char *function, +size_t line, +const char *error_msg, +const char *description, +int should_abort) +{ +if (condition) +return ; + + clar__fail(file, function, line, error_msg, description,
should_abort);t/unit-tests/clar/clar.c +26
-0+} + +void clar__assert_equal ( +const char *file, +const char *function, +size_t line, +const char *err, +int should_abort, +const char *fmt, + ...) +{ +va_list args; +char buf[4096 ]; +int is_equal =1 ; + +va_start (args, fmt); + +if (!strcmp ("%s" , fmt)) { + const char *s1 = va_arg(args, const char *); + const char *s2 = va_arg(args, const char *); + is_equal = (!s1 || !s2) ? (s1 == s2) : !strcmp (s1, s2); + +if (!is_equal) { +if (s1 && s2) { +int pos; + for (pos = 0; s1[pos] == s2[pos] && s1[pos] &&s2[pos]; ++pos)
t/unit-tests/clar/clar.c +2
-0+/* find differing byte offset */ ; + p_snprintf(buf, sizeof(buf), "'%s' != '%s' (atbyte %d)",
t/unit-tests/clar/clar.c +3
-0+ s1, s2, pos); + }else { + p_snprintf(buf, sizeof(buf), "'%s' != '%s'",s1, s2);
t/unit-tests/clar/clar.c +16
-0+ } + } + } +else if (!strcmp ("%.*s" , fmt)) { + const char *s1 = va_arg(args, const char *); + const char *s2 = va_arg(args, const char *); +int len =va_arg (args, int); + is_equal = (!s1 || !s2) ? (s1 == s2) : !strncmp (s1, s2, len); + +if (!is_equal) { +if (s1 && s2) { +int pos; +for (pos =0 ; s1[pos] == s2[pos] && pos < len; ++pos) +/* find differing byte offset */ ; + p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s'(at byte %d)",
t/unit-tests/clar/clar.c +3
-0+ len, s1, len, s2, pos); + }else { + p_snprintf(buf, sizeof(buf), "'%.*s' !='%.*s'", len, s1, len, s2);
t/unit-tests/clar/clar.c +7
-0+ } + } + } +else if (!strcmp ("%ls" , fmt)) { + const wchar_t *wcs1 = va_arg(args, const wchar_t *); + const wchar_t *wcs2 = va_arg(args, const wchar_t *); + is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcscmp(wcs1,wcs2);
t/unit-tests/clar/clar.c +5
-0+ +if (!is_equal) { +if (wcs1 && wcs2) { +int pos; + for (pos = 0; wcs1[pos] == wcs2[pos] &&wcs1[pos] && wcs2[pos]; ++pos)
t/unit-tests/clar/clar.c +2
-0+/* find differing byte offset */ ; + p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'(at byte %d)",
t/unit-tests/clar/clar.c +3
-0+ wcs1, wcs2, pos); + }else { + p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'",wcs1, wcs2);
t/unit-tests/clar/clar.c +8
-0+ } + } + } +else if (!strcmp ("%.*ls" , fmt)) { + const wchar_t *wcs1 = va_arg(args, const wchar_t *); + const wchar_t *wcs2 = va_arg(args, const wchar_t *); +int len =va_arg (args, int); + is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcsncmp(wcs1,wcs2, len);
t/unit-tests/clar/clar.c +5
-0+ + if (!is_equal) { + if (wcs1 && wcs2) { + int pos; + for (pos = 0; wcs1[pos] == wcs2[pos] && pos <len; ++pos)
t/unit-tests/clar/clar.c +2
-0+/* find differing byte offset */ ; + p_snprintf(buf, sizeof(buf), "'%.*ls' !='%.*ls' (at byte %d)",
t/unit-tests/clar/clar.c +3
-0+ len, wcs1, len, wcs2, pos); + }else { + p_snprintf(buf, sizeof(buf), "'%.*ls' !='%.*ls'", len, wcs1, len, wcs2);
t/unit-tests/clar/clar.c +10
-0+ } + } + } +else if (!strcmp ("%" PRIuZ, fmt) || !strcmp ("%" PRIxZ, fmt)) { +size_t sz1 =va_arg (args, size_t), sz2 =va_arg (args, size_t); + is_equal = (sz1 == sz2); +if (!is_equal) { +int offset =p_snprintf (buf,sizeof (buf), fmt, sz1); +strncat (buf," != " ,sizeof (buf) - offset); + p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4,fmt, sz2);
t/unit-tests/clar/clar.c +15
-0+ } + } +else if (!strcmp ("%p" , fmt)) { + void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *); + is_equal = (p1 == p2); +if (!is_equal) +p_snprintf (buf,sizeof (buf),"%p != %p" , p1, p2); + } +else { +int i1 =va_arg (args, int), i2 =va_arg (args, int); + is_equal = (i1 == i2); +if (!is_equal) { +int offset =p_snprintf (buf,sizeof (buf), fmt, i1); +strncat (buf," != " ,sizeof (buf) - offset); + p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4,fmt, i2);
t/unit-tests/clar/clar.c +20
-0+ } + } + +va_end (args); + +if (!is_equal) +clar__fail (file, function, line, err, buf, should_abort); +} + +void cl_set_cleanup (void (*cleanup)(void *),void *opaque) +{ + _clar.local_cleanup = cleanup; + _clar.local_cleanup_payload = opaque; +} + +#include "clar/sandbox.h" +#include "clar/fixtures.h" +#include "clar/fs.h" +#include "clar/print.h" +#include "clar/summary.h" t/unit-tests/clar/clar.h new file +89
-0@@ -0,0 +1,173 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ +#ifndef __CLAR_TEST_H__ +#define __CLAR_TEST_H__ + +#include <stdlib.h> + +enum cl_test_status { +CL_TEST_OK , +CL_TEST_FAILURE , +CL_TEST_SKIP , +CL_TEST_NOTRUN , +}; + +enum cl_output_format { +CL_OUTPUT_CLAP , +CL_OUTPUT_TAP , +}; + +/** Setup clar environment */ +void clar_test_init (int argc,char *argv[]); +int clar_test_run (void ); +void clar_test_shutdown (void ); + +/** One shot setup & run */ +int clar_test (int argc,char *argv[]); + +const char *clar_sandbox_path (void ); + +void cl_set_cleanup (void (*cleanup)(void *),void *opaque); +void cl_fs_cleanup (void ); + +/** +* cl_trace_* is a hook to provide a simple global tracing +* mechanism. +* +* The goal here is to let main() provide clar-proper +* with a callback to optionally write log info for +* test operations into the same stream used by their +* actual tests. This would let them print test names +* and maybe performance data as they choose. +* +* The goal is NOT to alter the flow of control or to +* override test selection/skipping. (So the callback +* does not return a value.) +* +* The goal is NOT to duplicate the existing +* pass/fail/skip reporting. (So the callback +* does not accept a status/errorcode argument.) +* +*/ +typedef enum cl_trace_event { +CL_TRACE__SUITE_BEGIN , +CL_TRACE__SUITE_END , +CL_TRACE__TEST__BEGIN , +CL_TRACE__TEST__END , +CL_TRACE__TEST__RUN_BEGIN , +CL_TRACE__TEST__RUN_END , +CL_TRACE__TEST__LONGJMP , +}cl_trace_event ; + +typedef void (cl_trace_cb )( +cl_trace_event ev, +const char *suite_name, +const char *test_name, +void *payload); + +/** +* Register a callback into CLAR to send global trace events. +* Pass NULL to disable. +*/ +void cl_trace_register (cl_trace_cb *cb,void *payload); + + +#ifdef CLAR_FIXTURE_PATH +const char *cl_fixture (const char *fixture_name); +void cl_fixture_sandbox (const char *fixture_name); +void cl_fixture_cleanup (const char *fixture_name); +const char *cl_fixture_basename (const char *fixture_name); +#endif + +/** +* Assertion macros with explicit error message +*/ +#define cl_must_pass_ (expr, desc) clar__assert((expr) >= 0, __FILE__,
__func__,__LINE__, "Function call failed: " #expr, desc, 1) +#definecl_must_fail_(expr, desc)clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 1)+#definecl_assert_(expr, desc)clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 1)t/unit-tests/clar/clar.h new file +5
-0+ +/** +* Check macros with explicit error message +*/ +#define cl_check_pass_ (expr, desc) clar__assert((expr) >= 0, __FILE__,
__func__,__LINE__, "Function call failed: " #expr, desc, 0) +#definecl_check_fail_(expr, desc)clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 0)+#definecl_check_(expr, desc)clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 0)t/unit-tests/clar/clar.h new file +19
-0+ +/** +* Assertion macros with no error message +*/ +#define cl_must_pass (expr) cl_must_pass_(expr, NULL) +#define cl_must_fail (expr) cl_must_fail_(expr, NULL) +#define cl_assert (expr) cl_assert_(expr, NULL) + +/** +* Check macros with no error message +*/ +#define cl_check_pass (expr) cl_check_pass_(expr, NULL) +#define cl_check_fail (expr) cl_check_fail_(expr, NULL) +#define cl_check (expr) cl_check_(expr, NULL) + +/** +* Forced failure/warning +*/ +#define cl_fail (desc) clar__fail(__FILE__, __func__, __LINE__, "Testfailed.", desc, 1)
+#definecl_warning(desc)clar__fail(__FILE__, __func__, __LINE__, "Warning during test execution:", desc, 0)t/unit-tests/clar/clar.h new file +7
-0+ +#define cl_skip () clar__skip() + +/** +* Typed assertion macros +*/ +#define cl_assert_equal_s (s1,s2)
clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
+#definecl_assert_equal_s_(s1,s2,note)clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))t/unit-tests/clar/clar.h new file +2
-0+ +#define cl_assert_equal_wcs (wcs1,wcs2)
clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
+#definecl_assert_equal_wcs_(wcs1,wcs2,note)clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))t/unit-tests/clar/clar.h new file +2
-0+ +#define cl_assert_equal_strn (s1,s2,len)
clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
+#definecl_assert_equal_strn_(s1,s2,len,note)clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))t/unit-tests/clar/clar.h new file +2
-0+ +#define cl_assert_equal_wcsn (wcs1,wcs2,len)clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
+#definecl_assert_equal_wcsn_(wcs1,wcs2,len,note)clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))t/unit-tests/clar/clar.h new file +2
-0+ +#define cl_assert_equal_i (i1,i2)
clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
+#definecl_assert_equal_i_(i1,i2,note)clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
+#definecl_assert_equal_i_fmt(i1,i2,fmt)clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))t/unit-tests/clar/clar.h new file +2
-0+ +#define cl_assert_equal_b (b1,b2)
clar__assert_equal(__FILE__,__func__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))t/unit-tests/clar/clar.h new file +2
-0+ +#define cl_assert_equal_p (p1,p2)
clar__assert_equal(__FILE__,__func__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))t/unit-tests/clar/clar.h new file +30
-0+ +void clar__skip (void ); + +void clar__fail ( +const char *file, +const char *func, +size_t line, +const char *error, +const char *description, +int should_abort); + +void clar__assert ( +int condition, +const char *file, +const char *func, +size_t line, +const char *error, +const char *description, +int should_abort); + +void clar__assert_equal ( +const char *file, +const char *func, +size_t line, +const char *err, +int should_abort, +const char *fmt, + ...); + +#endif t/unit-tests/clar/clar/fixtures.h new file +50
-0@@ -0,0 +1,50 @@ +#ifdef CLAR_FIXTURE_PATH +static const char * +fixture_path (const char *base,const char *fixture_name) +{ +static char _path[4096 ]; +size_t root_len; + + root_len =strlen (base); +strncpy (_path, base,sizeof (_path)); + +if (_path[root_len -1 ] !='/' ) + _path[root_len++] ='/' ; + +if (fixture_name[0 ] =='/' ) + fixture_name++; + +strncpy (_path + root_len, + fixture_name, +sizeof (_path) - root_len); + +return _path; +} + +const char *cl_fixture (const char *fixture_name) +{ +return fixture_path (CLAR_FIXTURE_PATH , fixture_name); +} + +void cl_fixture_sandbox (const char *fixture_name) +{ +fs_copy (cl_fixture (fixture_name), _clar_path); +} + +const char *cl_fixture_basename (const char *fixture_name) +{ +const char *p; + +for (p = fixture_name; *p; p++) { +if (p[0 ] =='/' && p[1 ] && p[1 ] !='/' ) + fixture_name = p+1 ; + } + +return fixture_name; +} + +void cl_fixture_cleanup (const char *fixture_name) +{ +fs_rm (fixture_path (_clar_path,cl_fixture_basename (fixture_name))); +} +#endif t/unit-tests/clar/clar/fs.h new file +108
-0@@ -0,0 +1,522 @@ +/* +* By default, use a read/write loop to copy files on POSIX systems. +* On Linux, use sendfile by default as it's slightly faster. On +* macOS, we avoid fcopyfile by default because it's slightly slower. +*/ +#undef USE_FCOPYFILE +#define USE_SENDFILE 1 + +#ifdef _WIN32 + +#ifdef CLAR_WIN32_LONGPATHS +# define CLAR_MAX_PATH 4096 +#else +# define CLAR_MAX_PATH MAX_PATH +#endif + +#define RM_RETRY_COUNT 5 +#define RM_RETRY_DELAY 10 + +#ifdef __MINGW32__ + +/* These security-enhanced functions are not available + * in MinGW, so just use the vanilla ones */ +#define wcscpy_s(a, b, c) wcscpy((a), (c)) +#define wcscat_s(a, b, c) wcscat((a), (c)) + +#endif /* __MINGW32__ */ + +static int +fs__dotordotdot(WCHAR *_tocheck) +{ + return _tocheck[0] == '.' && + (_tocheck[1] == '\0' || + (_tocheck[1] == '.' && _tocheck[2] == '\0')); +} + +static int +fs_rmdir_rmdir(WCHAR *_wpath) +{ + unsigned retries = 1; + + while (!RemoveDirectoryW(_wpath)) { + /* Only retry when we have retries remaining, and the + * error was ERROR_DIR_NOT_EMPTY. */ + if (retries++ > RM_RETRY_COUNT || + ERROR_DIR_NOT_EMPTY != GetLastError()) + return -1; + + /* Give whatever has a handle to a child item some time + * to release it before trying again */ + Sleep(RM_RETRY_DELAY * retries * retries); + } + + return 0; +} + +static void translate_path(WCHAR *path, size_t path_size) +{ + size_t path_len, i; + + if (wcsncmp(path, L"\\\\?\\", 4) == 0) + return; + + path_len = wcslen(path); + cl_assert(path_size > path_len + 4); + + for (i = path_len; i > 0; i--) { + WCHAR c = path[i - 1]; + + if (c == L'/') + path[i + 3] = L'\\'; + else + path[i + 3] = path[i - 1]; + } + + path[0] = L'\\'; + path[1] = L'\\'; + path[2] = L'?'; + path[3] = L'\\'; + path[path_len + 4] = L'\0'; +} + +static void +fs_rmdir_helper(WCHAR *_wsource) +{ + WCHAR buffer[CLAR_MAX_PATH]; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + size_t buffer_prefix_len; + + /* Set up the buffer and capture the length */ + wcscpy_s(buffer, CLAR_MAX_PATH, _wsource); + translate_path(buffer, CLAR_MAX_PATH); + wcscat_s(buffer, CLAR_MAX_PATH, L"\\"); + buffer_prefix_len = wcslen(buffer); + + /* FindFirstFile needs a wildcard to match multiple items */ + wcscat_s(buffer, CLAR_MAX_PATH, L"*"); + find_handle = FindFirstFileW(buffer, &find_data); + cl_assert(INVALID_HANDLE_VALUE != find_handle); + + do { + /* FindFirstFile/FindNextFile gives back . and .. + * entries at the beginning */ + if (fs__dotordotdot(find_data.cFileName)) + continue; + + wcscpy_s(buffer + buffer_prefix_len, CLAR_MAX_PATH -
buffer_prefix_len, find_data.cFileName);t/unit-tests/clar/clar/fs.h new file +5
-0+ +if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) +fs_rmdir_helper (buffer); +else { +/* If set, the +R bit must be cleared before deleting */
t/unit-tests/clar/clar/fs.h new file +1
-0+ if (FILE_ATTRIBUTE_READONLY &find_data.dwFileAttributes)
t/unit-tests/clar/clar/fs.h new file +1
-0+ cl_assert(SetFileAttributesW(buffer,find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY));
t/unit-tests/clar/clar/fs.h new file +69
-0+ +cl_assert (DeleteFileW (buffer)); + } + } +while (FindNextFileW (find_handle, &find_data)); + +/* Ensure that we successfully completed the enumeration */ +cl_assert (ERROR_NO_MORE_FILES ==GetLastError ()); + +/* Close the find handle */ +FindClose (find_handle); + +/* Now that the directory is empty, remove it */ +cl_assert (0 ==fs_rmdir_rmdir (_wsource)); +} + +static int +fs_rm_wait (WCHAR *_wpath) +{ +unsigned retries =1 ; +DWORD last_error; + +do { +if (INVALID_FILE_ATTRIBUTES ==GetFileAttributesW (_wpath)) + last_error =GetLastError (); +else + last_error =ERROR_SUCCESS ; + +/* Is the item gone? */ +if (ERROR_FILE_NOT_FOUND == last_error || +ERROR_PATH_NOT_FOUND == last_error) +return 0 ; + +Sleep (RM_RETRY_DELAY * retries * retries); + } +while (retries++ <=RM_RETRY_COUNT ); + +return -1 ; +} + +static void +fs_rm (const char *_source) +{ +WCHAR wsource[CLAR_MAX_PATH ]; +DWORD attrs; + +/* The input path is UTF-8. Convert it to wide characters +* for use with the Windows API */ +cl_assert (MultiByteToWideChar (CP_UTF8 , +MB_ERR_INVALID_CHARS , + _source, +-1 ,/* Indicates NULL termination */ + wsource, +CLAR_MAX_PATH )); + +translate_path (wsource,CLAR_MAX_PATH ); + +/* Does the item exist? If not, we have no work to do */ + attrs =GetFileAttributesW (wsource); + +if (INVALID_FILE_ATTRIBUTES == attrs) +return ; + +if (FILE_ATTRIBUTE_DIRECTORY & attrs) +fs_rmdir_helper (wsource); +else { +/* The item is a file. Strip the +R bit */ +if (FILE_ATTRIBUTE_READONLY & attrs) + cl_assert(SetFileAttributesW(wsource, attrs &~FILE_ATTRIBUTE_READONLY));
t/unit-tests/clar/clar/fs.h new file +41
-0+ +cl_assert (DeleteFileW (wsource)); + } + +/* Wait for the DeleteFile or RemoveDirectory call to complete */ +cl_assert (0 ==fs_rm_wait (wsource)); +} + +static void +fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest) +{ + WCHAR buf_source[CLAR_MAX_PATH], buf_dest[CLAR_MAX_PATH]; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + size_t buf_source_prefix_len, buf_dest_prefix_len; + + wcscpy_s(buf_source, CLAR_MAX_PATH, _wsource); + wcscat_s(buf_source, CLAR_MAX_PATH, L"\\"); + translate_path(buf_source, CLAR_MAX_PATH); + buf_source_prefix_len = wcslen(buf_source); + + wcscpy_s(buf_dest, CLAR_MAX_PATH, _wdest); + wcscat_s(buf_dest, CLAR_MAX_PATH, L"\\"); + translate_path(buf_dest, CLAR_MAX_PATH); + buf_dest_prefix_len = wcslen(buf_dest); + + /* Get an enumerator for the items in the source. */ + wcscat_s(buf_source, CLAR_MAX_PATH, L"*"); + find_handle = FindFirstFileW(buf_source, &find_data); + cl_assert(INVALID_HANDLE_VALUE != find_handle); + + /* Create the target directory. */ + cl_assert(CreateDirectoryW(_wdest, NULL)); + + do { + /* FindFirstFile/FindNextFile gives back . and .. + * entries at the beginning */ + if (fs__dotordotdot(find_data.cFileName)) + continue; + + wcscpy_s(buf_source + buf_source_prefix_len, CLAR_MAX_PATH -
buf_source_prefix_len, find_data.cFileName);t/unit-tests/clar/clar/fs.h new file +1
-0+ wcscpy_s(buf_dest + buf_dest_prefix_len, CLAR_MAX_PATH -
buf_dest_prefix_len, find_data.cFileName);t/unit-tests/clar/clar/fs.h new file +60
-0+ +if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) +fs_copydir_helper (buf_source, buf_dest); +else +cl_assert (CopyFileW (buf_source, buf_dest, TRUE)); + } +while (FindNextFileW (find_handle, &find_data)); + +/* Ensure that we successfully completed the enumeration */ +cl_assert (ERROR_NO_MORE_FILES ==GetLastError ()); + +/* Close the find handle */ +FindClose (find_handle); +} + +static void +fs_copy (const char *_source,const char *_dest) +{ +WCHAR wsource[CLAR_MAX_PATH ], wdest[CLAR_MAX_PATH ]; +DWORD source_attrs, dest_attrs; +HANDLE find_handle; +WIN32_FIND_DATAW find_data; + +/* The input paths are UTF-8. Convert them to wide characters +* for use with the Windows API. */ +cl_assert (MultiByteToWideChar (CP_UTF8 , +MB_ERR_INVALID_CHARS , + _source, +-1 , + wsource, +CLAR_MAX_PATH )); + +cl_assert (MultiByteToWideChar (CP_UTF8 , +MB_ERR_INVALID_CHARS , + _dest, +-1 , + wdest, +CLAR_MAX_PATH )); + +translate_path (wsource,CLAR_MAX_PATH ); +translate_path (wdest,CLAR_MAX_PATH ); + +/* Check the source for existence */ + source_attrs =GetFileAttributesW (wsource); +cl_assert (INVALID_FILE_ATTRIBUTES != source_attrs); + +/* Check the target for existence */ + dest_attrs =GetFileAttributesW (wdest); + +if (INVALID_FILE_ATTRIBUTES != dest_attrs) { +/* Target exists; append last path part of source to target. +* Use FindFirstFile to parse the path */ + find_handle =FindFirstFileW (wsource, &find_data); +cl_assert (INVALID_HANDLE_VALUE != find_handle); +wcscat_s (wdest,CLAR_MAX_PATH ,L"\\" ); +wcscat_s (wdest,CLAR_MAX_PATH , find_data.cFileName); +FindClose (find_handle); + +/* Check the new target for existence */ + cl_assert(INVALID_FILE_ATTRIBUTES ==GetFileAttributesW(wdest));
t/unit-tests/clar/clar/fs.h new file +108
-0+ } + +if (FILE_ATTRIBUTE_DIRECTORY & source_attrs) +fs_copydir_helper (wsource, wdest); +else +cl_assert (CopyFileW (wsource, wdest, TRUE)); +} + +void +cl_fs_cleanup (void ) +{ +#ifdef CLAR_FIXTURE_PATH +fs_rm (fixture_path (_clar_path,"*" )); +#endif +} + +#else + +#include <errno.h> +#include <string.h> +#include <limits.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#if defined(__linux__) +# include <sys/sendfile.h> +#endif + +#if defined(__APPLE__) +# include <copyfile.h> +#endif + +static void basename_r (const char **out,int *out_len,const char *in) +{ +size_t in_len =strlen (in), start_pos; + +for (in_len =strlen (in); in_len; in_len--) { +if (in[in_len -1 ] !='/' ) +break ; + } + +for (start_pos = in_len; start_pos; start_pos--) { +if (in[start_pos -1 ] =='/' ) +break ; + } + +cl_assert (in_len - start_pos <INT_MAX ); + +if (in_len - start_pos >0 ) { + *out = &in[start_pos]; + *out_len = (in_len - start_pos); + }else { + *out ="/" ; + *out_len =1 ; + } +} + +static char *joinpath (const char *dir,const char *base,int base_len) +{ +char *out; +int len; + +if (base_len ==-1 ) { +size_t bl =strlen (base); + +cl_assert (bl <INT_MAX ); + base_len = (int )bl; + } + + len =strlen (dir) + base_len +2 ; +cl_assert (len >0 ); + +cl_assert (out =malloc (len)); +cl_assert (snprintf (out, len,"%s/%.*s" , dir, base_len, base) < len); + +return out; +} + +static void +fs_copydir_helper (const char *source,const char *dest,int dest_mode) +{ +DIR *source_dir; +struct dirent *d; + +mkdir (dest, dest_mode); + +cl_assert_ (source_dir =opendir (source),"Could not open source dir" ); +while ((d = (errno =0 ,readdir (source_dir))) !=NULL ) { +char *child; + +if (!strcmp (d->d_name,"." ) || !strcmp (d->d_name,".." )) +continue ; + + child =joinpath (source, d->d_name,-1 ); +fs_copy (child, dest); +free (child); + } + +cl_assert_ (errno ==0 ,"Failed to iterate source dir" ); + +closedir (source_dir); +} + +static void +fs_copyfile_helper(const char *source, size_t source_len, const char *dest,int
dest_mode)t/unit-tests/clar/clar/fs.h new file +14
-0+{ + int in, out; + + cl_must_pass((in = open(source, O_RDONLY))); + cl_must_pass((out = open(dest, O_WRONLY|O_CREAT|O_TRUNC, dest_mode))); + +#if USE_FCOPYFILE && defined(__APPLE__) + ((void)(source_len)); /* unused */ + cl_must_pass(fcopyfile(in, out, 0, COPYFILE_DATA)); +#elif USE_SENDFILE && defined(__linux__) + { + ssize_t ret = 0; + + while (source_len && (ret = sendfile(out, in, NULL,
source_len)) > 0) {t/unit-tests/clar/clar/fs.h new file +36
-0+ source_len -= (size_t)ret; + } +cl_assert (ret >=0 ); + } +#else + { +char buf[131072 ]; +ssize_t ret; + + ((void )(source_len));/* unused */ + +while ((ret =read (in, buf,sizeof (buf))) >0 ) { +size_t len = (size_t )ret; + +while (len && (ret =write (out, buf, len)) >0 ) { +cl_assert (ret <= (ssize_t )len); + len -= ret; + } +cl_assert (ret >=0 ); + } +cl_assert (ret ==0 ); + } +#endif + +close (in); +close (out); +} + +static void +fs_copy (const char *source,const char *_dest) +{ +char *dbuf =NULL ; +const char *dest =NULL ; +struct stat source_st, dest_st; + + cl_must_pass_(lstat(source, &source_st), "Failed to stat copysource");
t/unit-tests/clar/clar/fs.h new file +23
-0+ +if (lstat (_dest, &dest_st) ==0 ) { +const char *base; +int base_len; + +/* Target exists and is directory; append basename */ +cl_assert (S_ISDIR (dest_st.st_mode)); + +basename_r (&base, &base_len, source); +cl_assert (base_len <INT_MAX ); + + dbuf =joinpath (_dest, base, base_len); + dest = dbuf; + }else if (errno !=ENOENT ) { +cl_fail ("Cannot copy; cannot stat destination" ); + }else { + dest = _dest; + } + +if (S_ISDIR (source_st.st_mode)) { +fs_copydir_helper (source, dest, source_st.st_mode); + }else { + fs_copyfile_helper(source, source_st.st_size, dest,source_st.st_mode);
t/unit-tests/clar/clar/fs.h new file +55
-0+ } + +free (dbuf); +} + +static void +fs_rmdir_helper (const char *path) +{ +DIR *dir; +struct dirent *d; + +cl_assert_ (dir =opendir (path),"Could not open dir" ); +while ((d = (errno =0 ,readdir (dir))) !=NULL ) { +char *child; + +if (!strcmp (d->d_name,"." ) || !strcmp (d->d_name,".." )) +continue ; + + child =joinpath (path, d->d_name,-1 ); +fs_rm (child); +free (child); + } + +cl_assert_ (errno ==0 ,"Failed to iterate source dir" ); +closedir (dir); + +cl_must_pass_ (rmdir (path),"Could not remove directory" ); +} + +static void +fs_rm (const char *path) +{ +struct stat st; + +if (lstat (path, &st)) { +if (errno ==ENOENT ) +return ; + +cl_fail ("Cannot copy; cannot stat destination" ); + } + +if (S_ISDIR (st.st_mode)) { +fs_rmdir_helper (path); + }else { +cl_must_pass (unlink (path)); + } +} + +void +cl_fs_cleanup (void ) +{ +clar_unsandbox (); +clar_sandbox (); +} +#endif t/unit-tests/clar/clar/print.h new file +3
-0@@ -0,0 +1,211 @@ +/* clap: clar protocol, the traditional clar output format */ + +static void clar_print_clap_init(int test_count, int suite_count, constchar
*suite_names)t/unit-tests/clar/clar/print.h new file +4
-0+{ + (void )test_count; +printf ("Loaded %d suites: %s\n" , (int )suite_count, suite_names); + printf("Started (test status codes: OK='.' FAILURE='F'SKIPPED='S')\n");
t/unit-tests/clar/clar/print.h new file +3
-0+} + +static void clar_print_clap_shutdown(int test_count, int suite_count, int
error_count)t/unit-tests/clar/clar/print.h new file +10
-0+{ + (void )test_count; + (void )suite_count; + (void )error_count; + +printf ("\n\n" ); +clar_report_all (); +} + +static void clar_print_clap_error(int num, const struct clar_report*report, const struct
clar_error*error)t/unit-tests/clar/clar/print.h new file +19
-0+{ +printf (" %d) Failure:\n" , num); + +printf ("%s::%s [%s:%" PRIuZ"]\n" , + report->suite, + report->test, + error->file, + error->line_number); + +printf (" %s\n" , error->error_msg); + +if (error->description !=NULL ) +printf (" %s\n" , error->description); + +printf ("\n" ); +fflush (stdout); +} + +static void clar_print_clap_ontest(const char *suite_name, const char
*test_name, inttest_number, enumcl_test_statusstatus)t/unit-tests/clar/clar/print.h new file +26
-0+{ + (void )test_name; + (void )test_number; + +if (_clar.verbosity >1 ) { +printf ("%s::%s: " , suite_name, test_name); + +switch (status) { +case CL_TEST_OK :printf ("ok\n" );break ; +case CL_TEST_FAILURE :printf ("fail\n" );break ; +case CL_TEST_SKIP :printf ("skipped" );break ; +case CL_TEST_NOTRUN :printf ("notrun" );break ; + } + }else { +switch (status) { +case CL_TEST_OK :printf ("." );break ; +case CL_TEST_FAILURE :printf ("F" );break ; +case CL_TEST_SKIP :printf ("S" );break ; +case CL_TEST_NOTRUN :printf ("N" );break ; + } + +fflush (stdout); + } +} + +static void clar_print_clap_onsuite(const char *suite_name, int
suite_index)t/unit-tests/clar/clar/print.h new file +15
-0+{ +if (_clar.verbosity ==1 ) +printf ("\n%s" , suite_name); + + (void )suite_index; +} + +static void clar_print_clap_onabort (const char *fmt,va_list arg) +{ +vfprintf (stderr, fmt, arg); +} + +/* tap: test anywhere protocol format */ + +static void clar_print_tap_init(int test_count, int suite_count, const char
*suite_names)t/unit-tests/clar/clar/print.h new file +8
-0+{ + (void )test_count; + (void )suite_count; + (void )suite_names; +printf ("TAP version 13\n" ); +} + +static void clar_print_tap_shutdown(int test_count, int suite_count, int
error_count)t/unit-tests/clar/clar/print.h new file +8
-0+{ + (void )suite_count; + (void )error_count; + +printf ("1..%d\n" , test_count); +} + +static void clar_print_tap_error(int num, const struct clar_report *report,const struct
clar_error*error)t/unit-tests/clar/clar/print.h new file +20
-0+{ + (void )num; + (void )report; + (void )error; +} + +static void print_escaped (const char *str) +{ +char *c; + +while ((c =strchr (str,'\'' )) !=NULL ) { +printf ("%.*s" , (int )(c - str), str); +printf ("''" ); + str = c +1 ; + } + +printf ("%s" , str); +} + +static void clar_print_tap_ontest(const char *suite_name, const char
*test_name, inttest_number, enumcl_test_statusstatus)t/unit-tests/clar/clar/print.h new file +9
-0+{ +const struct clar_error *error = _clar.last_report->errors; + + (void )test_name; + (void )test_number; + +switch (status) { +case CL_TEST_OK : + printf("ok %d - %s::%s\n", test_number, suite_name,
test_name);t/unit-tests/clar/clar/print.h new file +3
-0+break ; +case CL_TEST_FAILURE : + printf("not ok %d - %s::%s\n", test_number, suite_name,
test_name);t/unit-tests/clar/clar/print.h new file +10
-0+ +printf (" ---\n" ); +printf (" reason: |\n" ); +printf (" %s\n" , error->error_msg); + +if (error->description) +printf (" %s\n" , error->description); + +printf (" at:\n" ); +printf (" file: '" );print_escaped (error->file);
printf("'\n");t/unit-tests/clar/clar/print.h new file +8
-0+printf (" line: %" PRIuZ"\n" , error->line_number); +printf (" function: '%s'\n" , error->function); +printf (" ---\n" ); + +break ; +case CL_TEST_SKIP : +case CL_TEST_NOTRUN : + printf("ok %d - # SKIP %s::%s\n", test_number, suite_name,
test_name);t/unit-tests/clar/clar/print.h new file +34
-0+break ; + } + +fflush (stdout); +} + +static void clar_print_tap_onsuite (const char *suite_name,int suite_index) +{ +printf ("# start of suite %d: %s\n" , suite_index, suite_name); +} + +static void clar_print_tap_onabort (const char *fmt,va_list arg) +{ +printf ("Bail out! " ); +vprintf (fmt, arg); +fflush (stdout); +} + +/* indirection between protocol output selection */ + +#define FN , ...) do { \ + switch (_clar.output_format) { \ + case CL_OUTPUT_CLAP: \ + clar_print_clap_##FN (__VA_ARGS__); \ + break; \ + case CL_OUTPUT_TAP: \ + clar_print_tap_##FN (__VA_ARGS__); \ + break; \ + default: \ + abort(); \ + } \ + } while (0) + +static void clar_print_init(int test_count, int suite_count, const char
*suite_names)t/unit-tests/clar/clar/print.h new file +5
-0+{ +
error_count)t/unit-tests/clar/clar/print.h new file +5
-0+{ +const struct
clar_error*error)t/unit-tests/clar/clar/print.h new file +5
-0+{ +
*test_name, inttest_number, enumcl_test_statusstatus)t/unit-tests/clar/clar/print.h new file +16
-0+{ +static void clar_print_onsuite (const char *suite_name,int suite_index) +{ +static void clar_print_onabort (const char *msg, ...) +{ +va_list argp; +va_start (argp, msg); +va_end (argp); +}t/unit-tests/clar/clar/sandbox.h new file +39
-0@@ -0,0 +1,153 @@ +#ifdef __APPLE__ +#include <sys/syslimits.h> +#endif + +static char _clar_path[4096 + 1]; + +static int +is_valid_tmp_path(const char *path) +{ + STAT_T st; + + if (stat(path, &st) != 0) + return 0; + + if (!S_ISDIR(st.st_mode)) + return 0; + + return (access(path, W_OK) == 0); +} + +static int +find_tmp_path(char *buffer, size_t length) +{ +#ifndef _WIN32 + static const size_t var_count = 5; + static const char *env_vars[] = { + "CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE" + }; + + size_t i; + + for (i = 0; i < var_count; ++i) { + const char *env = getenv(env_vars[i]); + if (!env) + continue; + + if (is_valid_tmp_path(env)) { +#ifdef __APPLE__ + if (length >= PATH_MAX && realpath(env, buffer) !=NULL)
t/unit-tests/clar/clar/sandbox.h new file +21
-0+return 0 ; +#endif +strncpy (buffer, env, length -1 ); + buffer[length -1 ] ='\0' ; +return 0 ; + } + } + +/* If the environment doesn't say anything, try to use /tmp */ +if (is_valid_tmp_path ("/tmp" )) { +#ifdef __APPLE__ +if (length >=PATH_MAX &&realpath ("/tmp" , buffer) !=NULL ) +return 0 ; +#endif +strncpy (buffer,"/tmp" , length -1 ); + buffer[length -1 ] ='\0' ; +return 0 ; + } + +#else + DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer,(DWORD)length);
t/unit-tests/clar/clar/sandbox.h new file +93
-0+if (env_len >0 && env_len < (DWORD )length) +return 0 ; + +if (GetTempPath ((DWORD )length, buffer)) +return 0 ; +#endif + +/* This system doesn't like us, try to use the current directory */ +if (is_valid_tmp_path ("." )) { +strncpy (buffer,"." , length -1 ); + buffer[length -1 ] ='\0' ; +return 0 ; + } + +return -1 ; +} + +static void clar_unsandbox (void ) +{ +if (_clar_path[0 ] =='\0' ) +return ; + +cl_must_pass (chdir (".." )); + +fs_rm (_clar_path); +} + +static int build_sandbox_path (void ) +{ +#ifdef CLAR_TMPDIR +const char path_tail[] =CLAR_TMPDIR "_XXXXXX" ; +#else +const char path_tail[] ="clar_tmp_XXXXXX" ; +#endif + +size_t len; + +if (find_tmp_path (_clar_path,sizeof (_clar_path)) <0 ) +return -1 ; + + len =strlen (_clar_path); + +#ifdef _WIN32 + {/* normalize path to POSIX forward slashes */ +size_t i; +for (i =0 ; i < len; ++i) { +if (_clar_path[i] =='\\' ) + _clar_path[i] ='/' ; + } + } +#endif + +if (_clar_path[len -1 ] !='/' ) { + _clar_path[len++] ='/' ; + } + +strncpy (_clar_path + len, path_tail,sizeof (_clar_path) - len); + +#if defined(__MINGW32__) +if (_mktemp (_clar_path) ==NULL ) +return -1 ; + +if (mkdir (_clar_path,0700 ) !=0 ) +return -1 ; +#elif defined(_WIN32) +if (_mktemp_s (_clar_path,sizeof (_clar_path)) !=0 ) +return -1 ; + +if (mkdir (_clar_path,0700 ) !=0 ) +return -1 ; +#else +if (mkdtemp (_clar_path) ==NULL ) +return -1 ; +#endif + +return 0 ; +} + +static int clar_sandbox (void ) +{ +if (_clar_path[0 ] =='\0' &&build_sandbox_path () <0 ) +return -1 ; + +if (chdir (_clar_path) !=0 ) +return -1 ; + +return 0 ; +} + +const char *clar_sandbox_path (void ) +{ +return _clar_path; +}t/unit-tests/clar/clar/summary.h new file +40
-0@@ -0,0 +1,143 @@ + +#include <stdio.h> +#include <time.h> + +static int clar_summary_close_tag ( +struct clar_summary *summary,const char *tag,int indent) +{ +const char *indt; + +if (indent ==0 ) indt ="" ; +else if (indent ==1 ) indt ="\t" ; +else indt ="\t\t" ; + +return fprintf (summary->fp,"%s</%s>\n" , indt, tag); +} + +static int clar_summary_testsuites (struct clar_summary *summary) +{ +return fprintf (summary->fp,"<testsuites>\n" ); +} + +static int clar_summary_testsuite (struct clar_summary *summary, +int idn,const char *name,time_t timestamp, +int test_count,int fail_count,int error_count) +{ +struct tm *tm =localtime (×tamp); +char iso_dt[20 ]; + +if (strftime (iso_dt,sizeof (iso_dt),"%Y-%m-%dT%H:%M:%S" , tm) ==0 ) +return -1 ; + + return fprintf(summary->fp, "\t<testsuite" + " id=\"%d\"" + " name=\"%s\"" + " hostname=\"localhost\"" + " timestamp=\"%s\"" + " tests=\"%d\"" + " failures=\"%d\"" + " errors=\"%d\">\n", + idn, name, iso_dt, test_count, fail_count,
error_count);t/unit-tests/clar/clar/summary.h new file +58
-0+} + +static int clar_summary_testcase (struct clar_summary *summary, +const char *name,const char *classname,double elapsed) +{ +return fprintf (summary->fp, +"\t\t<testcase name=\"%s\" classname=\"%s\" time=\"%.2f\">\n" , + name, classname, elapsed); +} + +static int clar_summary_failure (struct clar_summary *summary, +const char *type,const char *message,const char *desc) +{ +return fprintf (summary->fp, +"\t\t\t<failure type=\"%s\"><![CDATA[%s\n%s]]></failure>\n" , + type, message, desc); +} + +static int clar_summary_skipped (struct clar_summary *summary) +{ +return fprintf (summary->fp,"\t\t\t<skipped />\n" ); +} + +struct clar_summary *clar_summary_init (const char *filename) +{ +struct clar_summary *summary; +FILE *fp; + +if ((fp =fopen (filename,"w" )) ==NULL ) { +perror ("fopen" ); +return NULL ; + } + +if ((summary =malloc (sizeof (struct clar_summary ))) ==NULL ) { +perror ("malloc" ); +fclose (fp); +return NULL ; + } + + summary->filename = filename; + summary->fp = fp; + +return summary; +} + +int clar_summary_shutdown(struct clar_summary *summary) +{ + struct clar_report *report; + const char *last_suite = NULL; + + if (clar_summary_testsuites(summary) < 0) + goto on_error; + + report = _clar.reports; + while (report != NULL) { + struct clar_error *error = report->errors; + + if (last_suite == NULL || strcmp(last_suite, report->suite) !=0) {
t/unit-tests/clar/clar/summary.h new file +2
-0+ if (clar_summary_testsuite(summary, 0, report->suite, + report->start, _clar.tests_ran,_clar.total_errors, 0) < 0)
t/unit-tests/clar/clar/summary.h new file +6
-0+ goto on_error; + } + + last_suite = report->suite; + + clar_summary_testcase(summary, report->test, report->suite,
report->elapsed);t/unit-tests/clar/clar/summary.h new file +19
-0+ +while (error !=NULL ) { +if (clar_summary_failure (summary,"assert" , + error->error_msg, error->description) <0 ) + goto on_error; + + error = error->next; + } + +if (report->status ==CL_TEST_SKIP ) +clar_summary_skipped (summary); + +if (clar_summary_close_tag (summary,"testcase" ,2 ) <0 ) + goto on_error; + + report = report->next; + +if (!report ||strcmp (last_suite, report->suite) !=0 ) { + if (clar_summary_close_tag(summary, "testsuite", 1) <0)
t/unit-tests/clar/clar/summary.h new file +18
-0+ goto on_error; + } + } + +if (clar_summary_close_tag (summary,"testsuites" ,0 ) <0 || +fclose (summary->fp) !=0 ) + goto on_error; + +printf ("written summary file to %s\n" , summary->filename); + +free (summary); +return 0 ; + +on_error: +fclose (summary->fp); +free (summary); +return -1 ; +}t/unit-tests/clar/generate.py new file +25
-0@@ -0,0 +1,266 @@ +#!/usr/bin/env python +# +# Copyright (c) Vicent Marti. All rights reserved. +# +# This file is part of clar, distributed under the ISC license. +# For full terms see the included COPYING file. +# + +from __future__ import with_statement +from string import Template +import re, fnmatch, os, sys, codecs, pickle + +class Module(object): + class Template(object): + def __init__(self, module): + self.module = module + + def _render_callback(self, cb): + if not cb: + return ' { NULL, NULL }' + return ' { "%s", &%s }' % (cb['short_name'], cb['symbol']) + + class DeclarationTemplate(Template): + def render(self): + out = "\n".join("extern %s;" % cb['declaration'] for cb inself.module.callbacks) + "\n"
t/unit-tests/clar/generate.py new file +12
-0+ + for initializer in self.module.initializers: + out += "extern %s;\n" % initializer['declaration'] + + if self.module.cleanup: + out += "extern %s;\n" % self.module.cleanup['declaration'] + + return out + + class CallbacksTemplate(Template): + def render(self): + out = "static const struct clar_func _clar_cb_%s[] = {\n" %self.module.name
+ out += ",\n".join(self._render_callback(cb) for cb in
self.module.callbacks)t/unit-tests/clar/generate.py new file +14
-0+ out += "\n};\n" + return out + + class InfoTemplate(Template): + def render(self): + templates = [] + + initializers = self.module.initializers + if len(initializers) == 0: + initializers = [ None ] + + for initializer in initializers: + name = self.module.clean_name() + if initializer andinitializer['short_name'].startswith('initialize_'):
+ variant =
initializer['short_name'][len('initialize_'):]t/unit-tests/clar/generate.py new file +45
-0+ name += " (%s)" % variant.replace('_', ' ') + + template = Template( + r""" + { + "${clean_name}", + ${initialize}, + ${cleanup}, + ${cb_ptr}, ${cb_count}, ${enabled} + }""" + ).substitute( + clean_name = name, + initialize = self._render_callback(initializer), + cleanup = self._render_callback(self.module.cleanup), + cb_ptr = "_clar_cb_%s" % self.module.name, + cb_count = len(self.module.callbacks), + enabled = int(self.module.enabled) + ) + templates.append(template) + + return ','.join(templates) + + def __init__(self, name): + self.name = name + + self.mtime = None + self.enabled = True + self.modified = False + + def clean_name(self): + return self.name.replace("_", "::") + + def _skip_comments(self, text): + SKIP_COMMENTS_REGEX = re.compile( + r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', + re.DOTALL | re.MULTILINE) + + def _replacer(match): + s = match.group(0) + return "" if s.startswith('/') else s + + return re.sub(SKIP_COMMENTS_REGEX, _replacer, text) + + def parse(self, contents): + TEST_FUNC_REGEX =r"^(void\s+(test_%s__(\w+))\s*\(\s*void\s*\))\s*\{"
t/unit-tests/clar/generate.py new file +71
-0+ + contents = self._skip_comments(contents) + regex = re.compile(TEST_FUNC_REGEX % self.name, re.MULTILINE) + + self.callbacks = [] + self.initializers = [] + self.cleanup = None + + for (declaration, symbol, short_name) in regex.findall(contents): + data = { + "short_name" : short_name, + "declaration" : declaration, + "symbol" : symbol + } + + if short_name.startswith('initialize'): + self.initializers.append(data) + elif short_name == 'cleanup': + self.cleanup = data + else: + self.callbacks.append(data) + + return self.callbacks != [] + + def refresh(self, path): + self.modified = False + + try: + st = os.stat(path) + + # Not modified + if st.st_mtime == self.mtime: + return True + + self.modified = True + self.mtime = st.st_mtime + + with codecs.open(path, encoding='utf-8') as fp: + raw_content = fp.read() + + except IOError: + return False + + return self.parse(raw_content) + +class TestSuite(object): + + def __init__(self, path, output): + self.path = path + self.output = output + + def should_generate(self, path): + if not os.path.isfile(path): + return True + + if any(module.modified for module in self.modules.values()): + return True + + return False + + def find_modules(self): + modules = [] + for root, _, files in os.walk(self.path): + module_root = root[len(self.path):] + module_root = [c for c in module_root.split(os.sep) if c] + + tests_in_module = fnmatch.filter(files, "*.c") + + for test_file in tests_in_module: + full_path = os.path.join(root, test_file) + module_name = "_".join(module_root +[test_file[:-2]]).replace("-", "_")
t/unit-tests/clar/generate.py new file +44
-0+ + modules.append((full_path, module_name)) + + return modules + + def load_cache(self): + path = os.path.join(self.output, '.clarcache') + cache = {} + + try: + fp = open(path, 'rb') + cache = pickle.load(fp) + fp.close() + except (IOError, ValueError): + pass + + return cache + + def save_cache(self): + path = os.path.join(self.output, '.clarcache') + with open(path, 'wb') as cache: + pickle.dump(self.modules, cache) + + def load(self, force = False): + module_data = self.find_modules() + self.modules = {} if force else self.load_cache() + + for path, name in module_data: + if name not in self.modules: + self.modules[name] = Module(name) + + if not self.modules[name].refresh(path): + del self.modules[name] + + def disable(self, excluded): + for exclude in excluded: + for module in self.modules.values(): + name = module.clean_name() + if name.startswith(exclude): + module.enabled = False + module.modified = True + + def suite_count(self): + return sum(max(1, len(m.initializers)) for m in
self.modules.values())t/unit-tests/clar/generate.py new file +3
-0+ + def callback_count(self): + return sum(len(module.callbacks) for module in
self.modules.values())t/unit-tests/clar/generate.py new file +9
-0+ + def write(self): + output = os.path.join(self.output, 'clar.suite') + + if not self.should_generate(output): + return False + + with open(output, 'w') as data: + modules = sorted(self.modules.values(), key=lambda module:module.name)
t/unit-tests/clar/generate.py new file +10
-0+ + for module in modules: + t = Module.DeclarationTemplate(module) + data.write(t.render()) + + for module in modules: + t = Module.CallbacksTemplate(module) + data.write(t.render()) + + suites = "static struct clar_suite _clar_suites[] = {" +','.join(
t/unit-tests/clar/generate.py new file +6
-0+ Module.InfoTemplate(module).render() for module in modules + ) + "\n};\n" + + data.write(suites) + + data.write("static const size_t _clar_suite_count = %d;\n" %
self.suite_count())
+ data.write("static constsize_t_clar_callback_count= %d;\n" %self.callback_count())t/unit-tests/clar/generate.py new file +9
-0+ + self.save_cache() + return True + +if __name__ == '__main__': + from optparse import OptionParser + + parser = OptionParser() + parser.add_option('-f', '--force', action="store_true", dest='force',default=False)
+ parser.add_option('-x', '--exclude', dest='excluded', action='append',
default=[])t/unit-tests/clar/generate.py new file +14
-0+ parser.add_option('-o', '--output', dest='output') + + options, args = parser.parse_args() + if len(args) > 1: + print("More than one path given") + sys.exit(1) + + path = args.pop() if args else '.' + output = options.output or path + suite = TestSuite(path, output) + suite.load(options.force) + suite.disable(options.excluded) + if suite.write(): + print("Written `clar.suite` (%d tests in %d suites)" %(
suite.callback_count(),suite.suite_count()))t/unit-tests/clar/test/.gitignore new file +4
-0@@ -0,0 +1,4 @@ +clar.suite +.clarcache +clar_test +*.ot/unit-tests/clar/test/Makefile new file +39
-0@@ -0,0 +1,39 @@ +# +# Copyright (c) Vicent Marti. All rights reserved. +# +# This file is part of clar, distributed under the ISC license. +# For full terms see the included COPYING file. +# + +# +# Set up the path to the clar sources and to the fixtures directory +# +# The fixture path needs to be an absolute path so it can be used +# even after we have chdir'ed into the test directory while testing. +# +CURRENT_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +TEST_DIRECTORY := $(abspath $(dir $(CURRENT_MAKEFILE))) +CLAR_PATH := $(dir $(TEST_DIRECTORY)) +CLAR_FIXTURE_PATH := $(TEST_DIRECTORY)/resources/ + +CFLAGS=-g -I.. -I. -Wall -DCLAR_FIXTURE_PATH=\"$(CLAR_FIXTURE_PATH)\" + +.PHONY: clean + +# list the objects that go into our test +objects = main.o sample.o + +# build the test executable itself +clar_test: $(objects) clar_test.h clar.suite $(CLAR_PATH)clar.c + $(CC) $(CFLAGS) -o $@ "$(CLAR_PATH)clar.c" $(objects) + +# test object files depend on clar macros +$(objects) : $(CLAR_PATH)clar.h + +# build the clar.suite file of test metadata +clar.suite: + python "$(CLAR_PATH)generate.py" . + +# remove all generated files +clean: + $(RM) -rf *.o clar.suite .clarcache clar_test clar_test.dSYMt/unit-tests/clar/test/clar_test.h new file +16
-0@@ -0,0 +1,16 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ +#ifndef __CLAR_TEST__ +#define __CLAR_TEST__ + +/* Import the standard clar helper functions */ +#include "clar.h" + +/* Your custom shared includes / defines here */ +extern int global_test_counter; + +#endif t/unit-tests/clar/test/main.c new file +40
-0@@ -0,0 +1,40 @@ +/* +* Copyright (c) Vicent Marti. All rights reserved. +* +* This file is part of clar, distributed under the ISC license. +* For full terms see the included COPYING file. +*/ + +#include "clar_test.h" + +/* +* Sample main() for clar tests. +* +* You should write your own main routine for clar tests that does specific +* setup and teardown as necessary for your application. The only required +* line is the call to `clar_test(argc, argv)`, which will execute the test +* suite. If you want to check the return value of the test application, +* your main() should return the same value returned by clar_test(). +*/ + +int global_test_counter =0 ; + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ +int ret; + +/* Your custom initialization here */ + global_test_counter =0 ; + +/* Run the test suite */ + ret =clar_test (argc, argv); + +/* Your custom cleanup here */ +cl_assert_equal_i (8 , global_test_counter); + +return ret; +}t/unit-tests/clar/test/main.c.sample new file +13
-0@@ -0,0 +1,27 @@ +/* + * Copyright (c) Vicent Marti. All rights reserved. + * + * This file is part of clar, distributed under the ISC license. + * For full terms see the included COPYING file. + */ + +#include "clar_test.h" + +/* + * Minimal main() for clar tests. + * + * Modify this with any application specific setup or teardown that youneed.
+ * The only required line is the call toclar_test(argc, argv), which will
+ * execute the test suite. If you want to check the return value of the test
+ * application,main()should return the same value returned byclar_test().
+ */t/unit-tests/clar/test/main.c.sample new file +10
-0+ +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + /* Run the test suite */ + return clar_test(argc, argv); +}t/unit-tests/clar/test/resources/test/file new file +1
-0@@ -0,0 +1 @@ +Filet/unit-tests/clar/test/sample.c new file +84
-0@@ -0,0 +1,84 @@ +#include "clar_test.h" +#include <sys/stat.h> + +static int file_size (const char *filename) +{ +struct stat st; + +if (stat (filename, &st) ==0 ) +return (int )st.st_size; +return -1 ; +} + +void test_sample__initialize (void ) +{ + global_test_counter++; +} + +void test_sample__cleanup (void ) +{ +cl_fixture_cleanup ("test" ); + +cl_assert (file_size ("test/file" ) ==-1 ); +} + +void test_sample__1 (void ) +{ +cl_assert (1 ); +cl_must_pass (0 );/* 0 == success */ +cl_must_fail (-1 );/* <0 == failure */ +cl_must_pass (-1 );/* demonstrate a failing call */ +} + +void test_sample__2 (void ) +{ +cl_fixture_sandbox ("test" ); + +cl_assert (file_size ("test/nonexistent" ) ==-1 ); +cl_assert (file_size ("test/file" ) >0 ); +cl_assert (100 ==101 ); +} + +void test_sample__strings (void ) +{ +const char *actual ="expected" ; +cl_assert_equal_s ("expected" , actual); +cl_assert_equal_s_ ("expected" , actual,"second try with annotation" ); +cl_assert_equal_s_ ("mismatched" , actual,"this one fails" ); +} + +void test_sample__strings_with_length (void ) +{ +const char *actual ="expected" ; +cl_assert_equal_strn ("expected_" , actual,8 ); +cl_assert_equal_strn ("exactly" , actual,2 ); +cl_assert_equal_strn_ ("expected_" , actual,8 ,"with annotation" ); +cl_assert_equal_strn_ ("exactly" , actual,3 ,"this one fails" ); +} + +void test_sample__int (void ) +{ +int value =100 ; +cl_assert_equal_i (100 , value); +cl_assert_equal_i_ (101 , value,"extra note on failing test" ); +} + +void test_sample__int_fmt (void ) +{ +int value =100 ; +cl_assert_equal_i_fmt (022 , value,"%04o" ); +} + +void test_sample__bool (void ) +{ +int value =100 ; +cl_assert_equal_b (1 , value);/* test equality as booleans */ +cl_assert_equal_b (0 , value); +} + +void test_sample__ptr (void ) +{ +const char *actual ="expected" ; +cl_assert_equal_p (actual, actual);/* pointers to same object */ +cl_assert_equal_p (&actual, actual); +}