diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec63335 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# C compiled objects +*.o + +# Python generated files +*.pyc diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..f10c7d2 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,31 @@ +# Copyright 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +VERB = @ +ifeq ($(VERBOSE),1) + VERB = +endif + +gen: user_guide.md + +%.md: %.md.in doc_gen.py + $(VERB) ./doc_gen.py -w $< + +diff: + $(VERB) ./doc_gen.py -d *.md.in + +test: + $(VERB) python -m unittest discover -p '*_test.py' + +all-tests: diff test diff --git a/docs/doc_gen.py b/docs/doc_gen.py new file mode 100755 index 0000000..c91d6b7 --- /dev/null +++ b/docs/doc_gen.py @@ -0,0 +1,194 @@ +#!/usr/bin/python +# +# Copyright 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Processes a Markdown file with source code include directives and outputs a +Markdown file with source code substitution and syntax highlighting +directives.""" + +import argparse +import json +import os +import re +import sys + + +def ReadFileRaw(filename): + """Reads the entire file, emits each line separately.""" + with open(filename, 'r') as source: + for line in source: + yield line + + +def ReadFileContentsWithMarker(filename, marker): + """Processes given file, returns lines surrounded by marker.""" + begin_comment_c = re.compile(r'^/\* BEGIN: (\w+) \*/$') + end_comment_c = re.compile(r'^/\* END: (\w+) \*/$') + begin_comment_cpp = re.compile(r'^// BEGIN: (\w+)$') + end_comment_cpp = re.compile(r'^// END: (\w+)$') + + def Matches(matcherA, matcherB, content): + return (matcherA.match(content), matcherB.match(content)) + + def Valid(matches): + return matches[0] or matches[1] + + def Group1(matches): + if matches[0]: + return matches[0].group(1) + else: + return matches[1].group(1) + + output = False + for line in ReadFileRaw(filename): + begin_matches = Matches(begin_comment_c, begin_comment_cpp, line) + if Valid(begin_matches): + begin_marker = Group1(begin_matches) + if begin_marker == marker: + yield '~~~c\n' # Markdown C formatting header + output = True + continue # avoid outputting our own begin line + + end_matches = Matches(end_comment_c, end_comment_cpp, line) + if Valid(end_matches): + end_marker = Group1(end_matches) + if end_marker == marker: + yield '~~~\n' # Markdown formatting end block + return # we are done with this region + + if output: + yield line # enables outputting nested region markers + + +def ProcessFile(headerTpl, filename): + """Process a Markdown file for source code includes. + + Returns: + a line-at-a-time generator, replacing markers with source code + """ + for line in headerTpl(filename): + yield line + + pattern = re.compile(r'^{({.*})}$') + for line in ReadFileRaw(filename): + match = pattern.match(line) + if not match: + yield line + continue + + # Handle special include + params = json.loads(match.group(1)) + full_path = params['source'] + base_name = os.path.basename(full_path) + yield '[`%s`](%s)\n' % (base_name, full_path) + yield '\n' + marker = params['marker'] + for item in ReadFileContentsWithMarker(full_path, marker): + yield item + + +def ProcessFilesToStdout(headerTpl, sources): + """Processes file, prints results to stdout.""" + for source in sources: + results = ProcessFile(headerTpl, source) + for line in results: + sys.stdout.write('%s' % line) + + +def ProcessFilesInPlace(headerTpl, sources): + """Processes Markdown file for includes, writes each output to disk. + + Each input file is assumed to have a `.in` suffix; this is stripped and the + resulting name is used as the output filename. + """ + for source in sources: + output = source.rstrip('.in') + with open(output, 'w') as dest: + results = ProcessFile(headerTpl, source) + for line in results: + dest.write('%s' % line) + + +def DiffFiles(headerTpl, sources): + """Compares expected output from processing to existing on-disk file. + + Generates output in-memory; compares the received output with contents of a + file on disk. Compares the two line-by-line, if any diff is found, only the + first diff is returned. + + Returns: + (bool, string): if no diff: (True, None); otherwise, (False, diff error) + """ + for source in sources: + on_disk_file = source.rstrip('.in') + on_disk_contents = list(ReadFileRaw(on_disk_file)) + + results = ProcessFile(headerTpl, source) + results_contents = list(results) + + if len(on_disk_contents) != len(results_contents): + return False, ('Diff: %s (%d lines) vs. %s (%d lines)\n' % + (on_disk_file, len(on_disk_contents), + '', len(results_contents))) + + for index, lines in enumerate(zip(on_disk_contents, results_contents)): + if lines[0] != lines[1]: + return False, ('Diff on line %d:\n' + '- %s' + '+ %s' % (index + 1, lines[0], lines[1])) + + return True, None + + +def main(argv): + parser = argparse.ArgumentParser( + description='Process Markdown files to include code samples.') + parser.add_argument('-d', dest='diff', action='store_true', + default=False, help='diff existing files vs. planned output') + parser.add_argument('-w', dest='overwrite', action='store_true', + default=False, help='overwrite files in-place') + parser.add_argument('files', metavar='filename', type=str, nargs='+', + help='filenames to process; must end with ".in" suffix') + + args = parser.parse_args() + + if args.diff and args.overwrite: + sys.stderr.write('Cannot specify both -d and -w in a single run\n') + sys.exit(1) + + for filename in args.files: + if not filename.endswith('.in'): + sys.stderr.write('Filename must end with ".in": %s\n' % filename) + sys.exit(1) + + def headerTpl(filename): + yield '\n' + yield '\n' + + if args.diff: + ok, error = DiffFiles(headerTpl, args.files) + if not ok: + sys.stderr.write('%s' % error) + sys.exit(1) + elif args.overwrite: + ProcessFilesInPlace(headerTpl, args.files) + else: + ProcessFilesToStdout(headerTpl, args.files) + + +if __name__ == '__main__': + main(sys.argv) diff --git a/docs/doc_gen_test.py b/docs/doc_gen_test.py new file mode 100755 index 0000000..fa4ec0a --- /dev/null +++ b/docs/doc_gen_test.py @@ -0,0 +1,126 @@ +#!/usr/bin/python +# +# Copyright 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for the `doc_gen` module.""" + +import unittest +import doc_gen + + +class ProcessFileTest(unittest.TestCase): + + def setUp(self): + self.docgen_readfile = None + + + def tearDown(self): + if self.docgen_readfile is not None: + doc_gen.ReadFileRaw = self.docgen_readfile + + + def _setCustomReadFile(self, filepath_contents): + self.docgen_readfile = doc_gen.ReadFileRaw + def readFileCustom(filename): + for filepath, contents in filepath_contents: + if filename == filepath: + return contents + else: + raise Exception('Expected file: "%s", received: "%s"' % + (filepath, filename)) + + doc_gen.ReadFileRaw = readFileCustom + + + def testPreC99Comments(self): + source_file = 'file.c' + source_file_fullpath = '/source/code/%s' % source_file + marker = 'docs' + source_file_contents = [ + '/* license header comment */\n', + '\n', + '/* BEGIN: %s */\n' % marker, + '#include \n', + '/* END: %s */\n' % marker, + 'int main(int argc, char** argv) {\n', + ' return 0;\n', + '}\n', + ] + + md_file_path = 'file.md.in' + md_file_contents = [ + 'text before code\n', + '{{"source": "%s", "marker": "%s"}}\n' % (source_file_fullpath, marker), + 'text after code\n', + ] + + expected_output = [ + 'text before code\n', + '[`%s`](%s)\n' % (source_file, source_file_fullpath), + '\n', + '~~~c\n', + '#include \n', + '~~~\n', + 'text after code\n', + ] + + self._setCustomReadFile( + [(source_file_fullpath, source_file_contents), + (md_file_path, md_file_contents)]) + actual_output = list(doc_gen.ProcessFile(md_file_contents)) + self.assertEquals(expected_output, actual_output) + + + def testC99Comments(self): + source_file = 'file.c' + source_file_fullpath = '/source/code/%s' % source_file + marker = 'docs' + source_file_contents = [ + '/* license header comment */\n', + '\n', + '// BEGIN: %s\n' % marker, + '#include \n', + '// END: %s\n' % marker, + 'int main(int argc, char** argv) {\n', + ' return 0;\n', + '}\n', + ] + + md_file_path = 'file.md.in' + md_file_contents = [ + 'text before code\n', + '{{"source": "%s", "marker": "%s"}}\n' % (source_file_fullpath, marker), + 'text after code\n', + ] + + expected_output = [ + 'text before code\n', + '[`%s`](%s)\n' % (source_file, source_file_fullpath), + '\n', + '~~~c\n', + '#include \n', + '~~~\n', + 'text after code\n', + ] + + self._setCustomReadFile( + [(source_file_fullpath, source_file_contents), + (md_file_path, md_file_contents)]) + actual_output = list(doc_gen.ProcessFile(md_file_path)) + self.assertEquals(expected_output, actual_output) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/user_guide.md b/docs/user_guide.md index eee72b1..6109c9f 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -1,3 +1,6 @@ + + # Cmockery User Guide Cmockery is a lightweight library that is used to author C unit tests. @@ -30,7 +33,7 @@ the test succeeded. #### Using `run_tests()` -[`run_tests.c`](src/example/run_tests.c) +[`run_tests.c`](../src/example/run_tests.c) ~~~c #include @@ -45,6 +48,7 @@ void null_test_success(void **state) { int main(int argc, char* argv[]) { const UnitTest tests[] = { unit_test(null_test_success), + unit_test_with_prefix(someprefix_, null_test_success), }; return run_tests(tests); } @@ -90,14 +94,14 @@ calls to `mock_assert()` occur during the function called via #### Using `mock_assert()` -[`assert_module.c`](src/example/assert_module.c) +[`assert_module.c`](../src/example/assert_module.c) ~~~c #include // If unit testing is enabled override assert with mock_assert(). #if UNIT_TESTING -extern void mock_assert(const int result, const char* const expression, +extern void mock_assert(const int result, const char* const expression, const char * const file, const int line); #undef assert #define assert(expression) \ @@ -116,7 +120,7 @@ void decrement_value(int * const value) { } ~~~ -[`assert_module_test.c`](src/example/assert_module_test.c) +[`assert_module_test.c`](../src/example/assert_module_test.c) ~~~c #include @@ -167,7 +171,7 @@ which increases data visibility aiding debugging of failing test cases. #### Using `assert_{type}_equal()` macros -[`assert_macro.c`](src/example/assert_macro.c) +[`assert_macro.c`](../src/example/assert_macro.c) ~~~c #include @@ -194,7 +198,7 @@ unsigned int string_to_status_code(const char* const status_code_string) { } ~~~ -[`assert_macro_test.c`](src/example/assert_macro_test.c) +[`assert_macro_test.c`](../src/example/assert_macro_test.c) ~~~c #include @@ -247,14 +251,20 @@ the test application to exit prematurely. #### Using Cmockery's Allocators -[`allocate_module.c`](src/example/allocate_module.c) +[`allocate_module.c`](../src/example/allocate_module.c) ~~~c +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_MALLOC_H #include +#endif +#include #if UNIT_TESTING extern void* _test_malloc(const size_t size, const char* file, const int line); -extern void* _test_calloc(const size_t number_of_elements, const size_t size, +extern void* _test_calloc(const size_t number_of_elements, const size_t size, const char* file, const int line); extern void _test_free(void* const ptr, const char* file, const int line); @@ -281,7 +291,7 @@ void buffer_underflow() { } ~~~ -[`allocate_module_test.c`](src/example/allocate_module_test.c) +[`allocate_module_test.c`](../src/example/allocate_module_test.c) ~~~c #include @@ -344,7 +354,7 @@ multiple calls to a mock function. #### Using `will_return()` -[`database.h`](src/example/database.h) +[`database.h`](../src/example/database.h) ~~~c typedef struct DatabaseConnection DatabaseConnection; @@ -370,7 +380,7 @@ DatabaseConnection* connect_to_database(const char * const url, const unsigned int port); ~~~ -[`customer_database.c`](src/example/customer_database.c) +[`customer_database.c`](../src/example/customer_database.c) ~~~c #include @@ -404,7 +414,7 @@ unsigned int get_customer_id_by_name( } ~~~ -[`customer_database_test.c`](src/example/customer_database_test.c) +[`customer_database_test.c`](../src/example/customer_database_test.c) ~~~c #include @@ -413,7 +423,6 @@ unsigned int get_customer_id_by_name( #include #include - extern DatabaseConnection* connect_to_customer_database(); extern unsigned int get_customer_id_by_name( DatabaseConnection * const connection, const char * const customer_name); @@ -422,26 +431,24 @@ extern unsigned int get_customer_id_by_name( unsigned int mock_query_database( DatabaseConnection* const connection, const char * const query_string, void *** const results) { - *results = (void**)mock(); + *results = (void**)((unsigned)mock()); return (unsigned int)mock(); } // Mock of the connect to database function. DatabaseConnection* connect_to_database(const char * const database_url, const unsigned int port) { - return (DatabaseConnection*)mock(); + return (DatabaseConnection*)((unsigned)mock()); } void test_connect_to_customer_database(void **state) { will_return(connect_to_database, 0x0DA7ABA53); - assert_true(connect_to_customer_database() == - (DatabaseConnection*)0x0DA7ABA53); + assert_int_equal((int)connect_to_customer_database(), 0x0DA7ABA53); } /* This test fails as the mock function connect_to_database() will have no * value to return. */ void fail_connect_to_customer_database(void **state) { - will_return(connect_to_database, 0x0DA7ABA53); assert_true(connect_to_customer_database() == (DatabaseConnection*)0x0DA7ABA53); } @@ -460,7 +467,6 @@ void test_get_customer_id_by_name(void **state) { int main(int argc, char* argv[]) { const UnitTest tests[] = { unit_test(test_connect_to_customer_database), - unit_test(fail_connect_to_customer_database), unit_test(test_get_customer_id_by_name), }; return run_tests(tests); @@ -483,7 +489,7 @@ no more parameter values are queued a test failure occurs. #### Using `expect_*()` -[`product_database.c`](src/example/product_database.c) +[`product_database.c`](../src/example/product_database.c) ~~~c #include @@ -494,7 +500,7 @@ DatabaseConnection* connect_to_product_database() { } ~~~ -[`product_database_test.c`](src/example/product_database_test.c) +[`product_database_test.c`](../src/example/product_database_test.c) ~~~c #include @@ -512,14 +518,14 @@ DatabaseConnection* connect_to_database(const char * const url, const unsigned int port) { check_expected(url); check_expected(port); - return (DatabaseConnection*)mock(); + return (DatabaseConnection*)((unsigned)mock()); } void test_connect_to_product_database(void **state) { expect_string(connect_to_database, url, "products.abcd.org"); expect_value(connect_to_database, port, 322); will_return(connect_to_database, 0xDA7ABA53); - assert_int_equal(connect_to_product_database(), 0xDA7ABA53); + assert_int_equal((int)connect_to_product_database(), 0xDA7ABA53); } /* This test will fail since the expected URL is different to the URL that is @@ -562,7 +568,7 @@ executed for a test case even when it fails. #### Using `unit_test_setup_teardown()` -[`key_value.c`](src/example/key_value.c) +[`key_value.c`](../src/example/key_value.c) ~~~c #include @@ -606,7 +612,7 @@ void sort_items_by_key() { } ~~~ -[`key_value_test.c`](src/example/key_value_test.c) +[`key_value_test.c`](../src/example/key_value_test.c) ~~~c #include diff --git a/docs/user_guide.md.in b/docs/user_guide.md.in new file mode 100644 index 0000000..8b30572 --- /dev/null +++ b/docs/user_guide.md.in @@ -0,0 +1,194 @@ +# Cmockery User Guide + +Cmockery is a lightweight library that is used to author C unit tests. + +### Contents + + * [Test Execution](#TestExecution) + * [Exception Handling](#ExceptionHandling) + * [Failure Conditions](#FailureConditions) + * [Assertions](#Assertions) + * [AssertMacros](#AssertMacros) + * [Dynamic Memory Allocation](#DynamicMemoryAllocation) + * [Mock functions](#MockFunctions) + * [Return Values](#ReturnValues) + * [Checking Parameters](#CheckingParameters) + * [Test State](#TestState) + * [Example](#Example) + +## Test Execution + +Cmockery unit test cases are functions with the signature +`void function(void **state)`. Cmockery test applications initialize a +table with test case function pointers using `unit_test*()` macros. This +table is then passed to the `run_tests()` macro to execute the tests. + +`run_tests()` sets up the appropriate exception / signal handlers and +other data structures prior to running each test function. When a unit test +is complete, `run_tests()` performs various checks to determine whether +the test succeeded. + +#### Using `run_tests()` + +{{"source": "../src/example/run_tests.c", "marker": "docs"}} + +## Exception Handling + +Before a test function is executed by `run_tests()`, +exception / signal handlers are overridden with a handler that simply +displays an error and exits a test function if an exception occurs. If an +exception occurs outside of a test function, for example, in Cmockery itself, +the application aborts execution and returns an error code. + +## Failure Conditions + +If a failure occurs during a test function that's executed via +`run_tests()`, the test function is aborted and the application's +execution resumes with the next test function. + +Test failures are ultimately signalled via the Cmockery function `fail()`. +The following events will result in the Cmockery library signalling a test +failure: + + * [Assertions](#Assertions) + * [Exceptions](#ExceptionHandling) + * [Memory leaks](#DynamicMemoryAllocation) + * [Mismatched setup and tear down functions](#TestState) + * [Missing mock return values](#ReturnValues) + * [Unused mock return values](#ReturnValues) + * [Expected parameter values](#CheckingParameters) + * [Unused expected parameter values](#CheckingParameters) + +## Assertions + +Runtime assert macros like the standard C library's `assert()` should +be redefined in modules being tested to use Cmockery's `mock_assert()` +function. Normally `mock_assert()` signals a +[test failure](#FailureConditions). If a function is called using +the `expect_assert_failure()` macro, any calls to `mock_assert()` +within the function will result in the execution of the test. If no +calls to `mock_assert()` occur during the function called via +`expect_assert_failure()` a test failure is signalled. + +#### Using `mock_assert()` + +{{"source": "../src/example/assert_module.c", "marker": "docs"}} + +{{"source": "../src/example/assert_module_test.c", "marker": "docs"}} + +### Assert Macros + +Cmockery provides an assortment of assert macros that tests should use use in +preference to the C standard library's `assert()` macro. On an assertion +failure, a Cmockery assert macro will write the failure to the standard error +stream and signal a test failure. Due to limitations of the C language, the +general C standard library `assert()` and Cmockery's `assert_true()` and +`assert_false()` macros can only display the expression that caused the assert +failure. Cmockery's type-specific assert macros, `assert_{type}_equal()` and +`assert_{type}_not_equal()`, display the data that caused the assertion failure +which increases data visibility aiding debugging of failing test cases. + +#### Using `assert_{type}_equal()` macros + +{{"source": "../src/example/assert_macro.c", "marker": "docs"}} + +{{"source": "../src/example/assert_macro_test.c", "marker": "docs"}} + +## Dynamic Memory Allocation + +To test for memory leaks, buffer overflows, and underflows, a module being +tested by Cmockery should replace calls to `malloc()`, `calloc()`, and +`free()` with `test_malloc()`, `test_calloc()`, and +`test_free()`, respectively. Each time a block is deallocated using +`test_free()`, it is checked for corruption. If a corrupt block is found, +a [test failure](#FailureConditions) is signalled. All blocks +allocated using the `test_*()` allocation functions are tracked by the +Cmockery library. When a test completes, if any allocated blocks (memory leaks) +remain, they are reported and a test failure is signalled. + +For simplicity, Cmockery currently executes all tests in one process. +Therefore, all test cases in a test application share a single address space, +which means that memory corruption from a single test case could potentially cause +the test application to exit prematurely. + +#### Using Cmockery's Allocators + +{{"source": "../src/example/allocate_module.c", "marker": "docs"}} + +{{"source": "../src/example/allocate_module_test.c", "marker": "docs"}} + +## Mock Functions + +A unit test should ideally isolate the function or module being tested +from any external dependencies. This can be performed using mock functions +that are either statically or dynamically linked with the module being tested. +Mock functions must be statically linked when the code being tested directly +references external functions. Dynamic linking is simply the process of +setting a function pointer in a table used by the tested module to reference +a mock function defined in the unit test. + +### Return Values + +In order to simplify the implementation of mock functions, Cmockery provides +functionality which stores return values for mock functions in each test +case using `will_return()`. These values are then returned by each mock +function using calls to `mock()`. + +Values passed to `will_return()` are added to a queue for each function +specified. Each successive call to `mock()` from a function removes a +return value from the queue. This makes it possible for a mock function to use +multiple calls to `mock()` to return output parameters in addition to a +return value. In addition, this allows the specification of return values for +multiple calls to a mock function. + +#### Using `will_return()` + +{{"source": "../src/example/database.h", "marker": "docs"}} + +{{"source": "../src/example/customer_database.c", "marker": "docs"}} + +{{"source": "../src/example/customer_database_test.c", "marker": "docs"}} + +### Checking Parameters + +In addition to storing the return values of mock functions, Cmockery +provides functionality to store expected values for mock function parameters +using the `expect_*()` functions provided. A mock function parameter can then +be validated using the `check_expected()` macro. + + +Successive calls to `expect_*()` macros for a parameter queues values to +check the specified parameter. `check_expected()` checks a function parameter +against the next value queued using `expect_*()`, if the parameter check fails a +test failure is signalled. In addition if `check_expected()` is called and +no more parameter values are queued a test failure occurs. + +#### Using `expect_*()` + +{{"source": "../src/example/product_database.c", "marker": "docs"}} + +{{"source": "../src/example/product_database_test.c", "marker": "docs"}} + +## Test State + +Cmockery allows the specification of multiple setup and tear down functions +for each test case. Setup functions, specified by the `unit_test_setup()` +or `unit_test_setup_teardown()` macros allow common initialization to be +shared between multiple test cases. In addition, tear down functions, +specified by the `unit_test_teardown()` or +`unit_test_setup_teardown()` macros provide a code path that is always +executed for a test case even when it fails. + +#### Using `unit_test_setup_teardown()` + +{{"source": "../src/example/key_value.c", "marker": "docs"}} + +{{"source": "../src/example/key_value_test.c", "marker": "docs"}} + +## Example + +A small command line calculator application +([`calculator.c`](src/example/calculator.c)) +and test application that full exercises the calculator application +([`calculator_test.c`](src/example/calculator_test.c)) +are provided as an example of Cmockery's features discussed in this document. diff --git a/src/example/allocate_module.c b/src/example/allocate_module.c index 634932b..1de152a 100644 --- a/src/example/allocate_module.c +++ b/src/example/allocate_module.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -48,3 +50,4 @@ void buffer_underflow() { memory[-1] = '!'; free(memory); } +/* END: docs */ diff --git a/src/example/allocate_module_test.c b/src/example/allocate_module_test.c index b09bec6..a8350b2 100644 --- a/src/example/allocate_module_test.c +++ b/src/example/allocate_module_test.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include #include #include @@ -45,3 +47,4 @@ int main(int argc, char* argv[]) { }; return run_tests(tests); } +/* END: docs */ diff --git a/src/example/assert_macro.c b/src/example/assert_macro.c index b70713b..931f088 100644 --- a/src/example/assert_macro.c +++ b/src/example/assert_macro.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include static const char* status_code_strings[] = { @@ -35,3 +37,4 @@ unsigned int string_to_status_code(const char* const status_code_string) { } return ~0U; } +/* END: docs */ diff --git a/src/example/assert_macro_test.c b/src/example/assert_macro_test.c index 4b8c850..2ce2821 100644 --- a/src/example/assert_macro_test.c +++ b/src/example/assert_macro_test.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include #include #include @@ -42,3 +44,4 @@ int main(int argc, char *argv[]) { }; return run_tests(tests); } +/* END: docs */ diff --git a/src/example/assert_module.c b/src/example/assert_module.c index 949ddde..4e4592b 100644 --- a/src/example/assert_module.c +++ b/src/example/assert_module.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include // If unit testing is enabled override assert with mock_assert(). @@ -31,6 +33,7 @@ void increment_value(int * const value) { void decrement_value(int * const value) { if (value) { - (*value) --; + (*value) --; } } +/* END: docs */ diff --git a/src/example/assert_module_test.c b/src/example/assert_module_test.c index 5e32e2e..f428890 100644 --- a/src/example/assert_module_test.c +++ b/src/example/assert_module_test.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include #include #include @@ -45,3 +47,4 @@ int main(int argc, char *argv[]) { }; return run_tests(tests); } +/* END: docs */ diff --git a/src/example/customer_database.c b/src/example/customer_database.c index fc0e3a2..cb60934 100644 --- a/src/example/customer_database.c +++ b/src/example/customer_database.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include #include #include @@ -42,3 +44,4 @@ unsigned int get_customer_id_by_name( } return (unsigned int)results[0]; } +/* END: docs */ diff --git a/src/example/customer_database_test.c b/src/example/customer_database_test.c index 202037e..9bbed55 100644 --- a/src/example/customer_database_test.c +++ b/src/example/customer_database_test.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include #include #include @@ -67,3 +69,4 @@ int main(int argc, char* argv[]) { }; return run_tests(tests); } +/* END: docs */ diff --git a/src/example/database.h b/src/example/database.h index 26b4009..7a2b740 100644 --- a/src/example/database.h +++ b/src/example/database.h @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ typedef struct DatabaseConnection DatabaseConnection; /* Function that takes an SQL query string and sets results to an array of @@ -34,4 +36,4 @@ struct DatabaseConnection { // Connect to a database. DatabaseConnection* connect_to_database(const char * const url, const unsigned int port); - +/* END: docs */ diff --git a/src/example/key_value.c b/src/example/key_value.c index a368a16..8080e6e 100644 --- a/src/example/key_value.c +++ b/src/example/key_value.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include #include #include @@ -52,3 +54,4 @@ void sort_items_by_key() { qsort(key_values, number_of_key_values, sizeof(*key_values), key_value_compare_keys); } +/* END: docs */ diff --git a/src/example/key_value_test.c b/src/example/key_value_test.c index edd44f6..4c8bb0e 100644 --- a/src/example/key_value_test.c +++ b/src/example/key_value_test.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include #include #include @@ -78,3 +80,4 @@ int main(int argc, char* argv[]) { }; return run_tests(tests); } +/* END: docs */ diff --git a/src/example/product_database.c b/src/example/product_database.c index 1f03cd0..4304d2f 100644 --- a/src/example/product_database.c +++ b/src/example/product_database.c @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include // Connect to the database containing customer information. DatabaseConnection* connect_to_product_database() { return connect_to_database("products.abcd.org", 322); } - +/* END: docs */ diff --git a/src/example/product_database_test.c b/src/example/product_database_test.c index e198516..ef6c473 100644 --- a/src/example/product_database_test.c +++ b/src/example/product_database_test.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include #include #include @@ -64,3 +66,4 @@ int main(int argc, char* argv[]) { }; return run_tests(tests); } +/* END: docs */ diff --git a/src/example/run_tests.c b/src/example/run_tests.c index 361ea5a..0888be5 100644 --- a/src/example/run_tests.c +++ b/src/example/run_tests.c @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* BEGIN: docs */ #include #include #include @@ -29,3 +31,4 @@ int main(int argc, char* argv[]) { }; return run_tests(tests); } +/* END: docs */