summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Bartell <pbartell@amazon.com>2021-03-15 17:01:29 -0700
committerGitHub <noreply@github.com>2021-03-15 17:01:29 -0700
commitd7e5f4088538f24b8d61a081fc7313e2d297bd2a (patch)
treea9505480e08a1154b76164c962a544f5a68c3a03
parentc8fa483b68c6c1149c2a7a8bc1e901b38860ec9b (diff)
downloadfreertos-git-d7e5f4088538f24b8d61a081fc7313e2d297bd2a.tar.gz
Clean up CMock makefiles and add coverage filtering (#523)
* Cleanup Makefiles * Add lcovrc configuration file * Add CMock test build directory to .gitignore * Add callgraph.py and filtercov.py scripts * Cleanup list Makefile and update list_utest.c with coverage tags * Add information about coverage filtering and running single test cases * Remove -fprofile-exclude-files for compatibility with older versions of gcc. Fix line endings (change to unix style) * Lint callgraph.py and filtercov.py. Print and error when no target functions are defined. * Indent with spaces when possible * Replace tabs with spaces and enable .RECIPEPREFIX * Add fake_port.h and related portmacro.h changes * Fix list makefile when bin directory is not available * Clean up grouped rules * Update makesfile.. Add "two_tests" example dir * Fix memory checker error * Move common makefile items to subdir.mk and testdir.mk includes * Update core_checker.py exclusions * Remove line from portmacro.h that doesn't match core_checker.py
-rwxr-xr-x.github/scripts/core_checker.py4
-rw-r--r--FreeRTOS/Test/CMock/.gitignore1
-rw-r--r--FreeRTOS/Test/CMock/Makefile172
-rw-r--r--FreeRTOS/Test/CMock/Readme.md37
-rw-r--r--FreeRTOS/Test/CMock/config/fake_port.h46
-rw-r--r--FreeRTOS/Test/CMock/config/portmacro.h155
-rw-r--r--FreeRTOS/Test/CMock/doc/Makefile5
-rw-r--r--FreeRTOS/Test/CMock/lcovrc3
-rw-r--r--FreeRTOS/Test/CMock/list/Makefile87
-rw-r--r--FreeRTOS/Test/CMock/list/list_utest.c18
-rw-r--r--FreeRTOS/Test/CMock/makefile.in111
-rw-r--r--FreeRTOS/Test/CMock/queue/Makefile81
-rw-r--r--FreeRTOS/Test/CMock/queue/queue_utest.c17
-rw-r--r--FreeRTOS/Test/CMock/queue/two_tests/Makefile47
l---------FreeRTOS/Test/CMock/queue/two_tests/queue_1_utest.c1
l---------FreeRTOS/Test/CMock/queue/two_tests/queue_2_utest.c1
-rw-r--r--FreeRTOS/Test/CMock/subdir.mk46
-rw-r--r--FreeRTOS/Test/CMock/testdir.mk201
-rwxr-xr-xFreeRTOS/Test/CMock/tools/callgraph.py106
-rwxr-xr-xFreeRTOS/Test/CMock/tools/filtercov.py326
20 files changed, 1123 insertions, 342 deletions
diff --git a/.github/scripts/core_checker.py b/.github/scripts/core_checker.py
index ec835048c..97e9bd590 100755
--- a/.github/scripts/core_checker.py
+++ b/.github/scripts/core_checker.py
@@ -175,6 +175,7 @@ FREERTOS_IGNORED_EXTENSIONS = [
'.properties',
'.ps1',
'.ptf',
+ '.py',
'.r79',
'.rapp',
'.rc',
@@ -263,7 +264,8 @@ FREERTOS_IGNORED_FILES = [
'mbedtls_config.h',
'requirements.txt',
'run-cbmc-proofs.py',
- '.editorconfig'
+ '.editorconfig',
+ 'lcovrc'
]
FREERTOS_HEADER = [
diff --git a/FreeRTOS/Test/CMock/.gitignore b/FreeRTOS/Test/CMock/.gitignore
new file mode 100644
index 000000000..567609b12
--- /dev/null
+++ b/FreeRTOS/Test/CMock/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/FreeRTOS/Test/CMock/Makefile b/FreeRTOS/Test/CMock/Makefile
index d8863dc85..a44dfe4a2 100644
--- a/FreeRTOS/Test/CMock/Makefile
+++ b/FreeRTOS/Test/CMock/Makefile
@@ -1,98 +1,116 @@
+# indent with spaces
+.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX)
+include makefile.in
+
# Change to match installed location
export CC ?= /usr/local/bin/gcc
export LD ?= /usr/local/bin/ld
+
# Add units here when adding a new unit test directory with the same name
-UNITS := queue list timers
+UNITS += list
+#UNITS += queue
+#UNITS += timers
-include makefile.in
+COVINFO := $(BUILD_DIR)/cmock_test.info
+LCOV_LIST := $(foreach unit,$(UNITS),$(GENERATED_DIR)/$(unit).info )
-.PHONY: all doc clean $(UNITS) directories coverage zero_coverage
-.PHONY: run run_formatted run_col_formatted run_col
+.PHONY: all doc clean $(UNITS) directories coverage zero_coverage \
+ run run_formatted run_col_formatted run_col libs execs lcov \
+ help
all: doc coverage
-execs: $(UNITS) | directories
-
-$(UNITS) : ${LIB_DIR}/libcmock.so \
- ${LIB_DIR}/libunity.so \
- ${LIB_DIR}/libunitymemory.so \
- | directories
- $(MAKE) -C $@
-
-doc: | directories
- $(MAKE) -C doc all
-
-directories : $(BUILD_DIR) $(GENERATED_DIR) $(COVERAGE_DIR) $(BIN_DIR) $(DOC_DIR) $(LIB_DIR)
-
-$(BUILD_DIR) :
- -mkdir $(BUILD_DIR)
-$(GENERATED_DIR) :
- -mkdir -p $(GENERATED_DIR)
-$(COVERAGE_DIR) :
- -mkdir -p $(COVERAGE_DIR)
-$(BIN_DIR) :
- -mkdir -p $(BIN_DIR)
-$(DOC_DIR) :
- -mkdir -p $(DOC_DIR)
-$(LIB_DIR) :
- -mkdir -p $(LIB_DIR)
+execs: $(UNITS)
+
+$(UNITS) : libs | directories
+ $(MAKE) -C $@
+
+SHARED_LIBS := $(addprefix $(LIB_DIR)/,$(addsuffix .so,$(LIBS)))
+
+libs : $(SHARED_LIBS)
+
+doc:
+ $(MAKE) -C doc all
clean:
- rm -rf build
+ rm -rf build
help:
- @echo -e 'Usage: $$ make <unit>\n '
- @echo -e ' where <unit> is one of: $(UNITS) doc all run run_formatted run_col run_col_formatted coverage'
+ @echo -e 'Usage: $$ make <unit>\n '
+ @echo -e ' where <unit> is one of: $(UNITS) doc all run run_formatted run_col run_col_formatted coverage'
$(LIB_DIR)/libcmock.so : ${CMOCK_SRC_DIR}/cmock.c \
- ${CMOCK_SRC_DIR}/cmock.h \
- ${LIB_DIR}/libunity.so \
- Makefile | directories
- ${CC} -o $@ -shared -fPIC $< ${INCLUDE_DIR}
+ ${CMOCK_SRC_DIR}/cmock.h \
+ ${LIB_DIR}/libunity.so \
+ Makefile
+ mkdir -p $(LIB_DIR)
+ ${CC} -o $@ -shared -fPIC $< ${INCLUDE_DIR}
$(LIB_DIR)/libunity.so : ${UNITY_SRC_DIR}/unity.c \
- ${UNITY_SRC_DIR}/unity.h \
- Makefile | directories
- ${CC} -o $@ -shared -fPIC $<
-
-$(LIB_DIR)/libunitymemory.so: ${UNITY_EXTRAS_DIR}/memory/src/unity_memory.c \
- ${UNITY_EXTRAS_DIR}/memory/src/unity_memory.h \
- ${LIB_DIR}/libunity.so \
- Makefile | directories
- ${CC} -o $@ -shared -fPIC $< ${INCLUDE_DIR}
-
-run : $(UNITS) directories
- -rm $(BUILD_DIR)/unit_test_report.txt
- for f in $(BIN_DIR)/*; do \
- $${f} | tee -a $(BUILD_DIR)/unit_test_report.txt; \
- done
- cd $(BUILD_DIR) && \
- ruby $(UNITY_BIN_DIR)/parse_output.rb -xml \
- $(BUILD_DIR)/unit_test_report.txt
-
-run_col : $(UNITS) zero_coverage | directories
- for f in $(BIN_DIR)/*; do \
- ruby -r $(UNITY_BIN_DIR)/colour_reporter.rb -e "report('`$${f}`')"; done
-
-run_formatted : $(UNITS) zero_coverage | directories
- for f in $(BIN_DIR)/*; do \
- $${f} > $(BUILD_DIR)/output; \
- ruby $(UNITY_BIN_DIR)/parse_output.rb $(BUILD_DIR)/output ; \
- done
-
-run_col_formatted : $(UNITS) zero_coverage | directories
- for f in $(BIN_DIR)/*; do \
- $${f} > $(BUILD_DIR)/output; \
- ruby -r $(UNITY_BIN_DIR)/colour_reporter.rb \
- -e "report('$$(ruby $(UNITY_BIN_DIR)/parse_output.rb \
- $(BUILD_DIR)/output)')"; \
- done
+ ${UNITY_SRC_DIR}/unity.h \
+ Makefile
+ mkdir -p $(LIB_DIR)
+ ${CC} -o $@ -shared -fPIC $<
+
+$(LIB_DIR)/libunitymemory.so: ${UNITY_MEM_DIR}/unity_memory.c \
+ ${UNITY_MEM_DIR}/unity_memory.h \
+ ${LIB_DIR}/libunity.so \
+ Makefile
+ mkdir -p $(LIB_DIR)
+ ${CC} -o $@ -shared -fPIC $< ${INCLUDE_DIR}
+
+$(LIB_DIR)/libcexception.so: ${C_EXCEPTION_SRC_DIR}/CException.c \
+ ${C_EXCEPTION_SRC_DIR}/CException.h
+ mkdir -p $(LIB_DIR)
+ ${CC} -o $@ -shared -fPIC $< ${INCLUDE_DIR}
+
+run : $(UNITS)
+ mkdir -p $(BUILD_DIR)
+ rm -f $(BUILD_DIR)/unit_test_report.txt
+ for f in $(BIN_DIR)/*; do \
+ $${f} | tee -a $(BUILD_DIR)/unit_test_report.txt; \
+ done
+ cd $(BUILD_DIR) && \
+ ruby $(UNITY_BIN_DIR)/parse_output.rb -xml \
+ $(BUILD_DIR)/unit_test_report.txt
+
+run_col : $(UNITS) zero_coverage
+ for f in $(BIN_DIR)/*; do \
+ ruby -r $(UNITY_BIN_DIR)/colour_reporter.rb -e "report('`$${f}`')"; done
+
+run_formatted : $(UNITS) zero_coverage
+ for f in $(BIN_DIR)/*; do \
+ $${f} > $(BUILD_DIR)/output; \
+ ruby $(UNITY_BIN_DIR)/parse_output.rb $(BUILD_DIR)/output ; \
+ done
+
+run_col_formatted : $(UNITS) zero_coverage
+ for f in $(BIN_DIR)/*; do \
+ $${f} > $(BUILD_DIR)/output; \
+ ruby -r $(UNITY_BIN_DIR)/colour_reporter.rb \
+ -e "report('$$(ruby $(UNITY_BIN_DIR)/parse_output.rb \
+ $(BUILD_DIR)/output)')"; \
+ done
zero_coverage :
- lcov --zerocounters --directory $(BUILD_DIR)
+ lcov --zerocounters --directory $(BUILD_DIR) --config-file $(UT_ROOT_DIR)/lcovrc
+
coverage : run_col
- lcov --base-directory . --directory . -c --rc lcov_branch_coverage=1 \
- --rc genhtml_branch_coverage=1 -o $(BUILD_DIR)/cmock_test.info
- genhtml $(BUILD_DIR)/cmock_test.info --branch-coverage \
- --output-directory $(COVERAGE_DIR)
+ lcov --base-directory $(KERNEL_DIR) --directory $(BUILD_DIR) --capture \
+ --config-file $(UT_ROOT_DIR)/lcovrc -o $(BUILD_DIR)/cmock_test.info
+ genhtml $(BUILD_DIR)/cmock_test.info --branch-coverage \
+ --config-file $(UT_ROOT_DIR)/lcovrc --output-directory $(COVERAGE_DIR)
+
+lcov : $(COVINFO)
+
+# Combine lcov from each test bin into one lcov info file for the suite
+$(COVINFO) : $(LCOV_LIST)
+ lcov $(LCOV_OPTS) -o $@ $(foreach cov,$(LCOV_LIST),--add-tracefile $(cov) )
+# Generate lcov for each suite
+$(LCOV_LIST) : zero_coverage
+ $(foreach unit,$(UNITS),\
+ make -C $(unit) lcov;)
+lcovhtml : $(COVINFO)
+ mkdir -p $(COVERAGE_DIR)
+ genhtml $(COVINFO) $(LCOV_OPTS) --output-directory $(COVERAGE_DIR) --quiet
diff --git a/FreeRTOS/Test/CMock/Readme.md b/FreeRTOS/Test/CMock/Readme.md
index df2c7c105..059f65303 100644
--- a/FreeRTOS/Test/CMock/Readme.md
+++ b/FreeRTOS/Test/CMock/Readme.md
@@ -21,6 +21,14 @@ Doxygen (optional)
```
1.8.5
```
+Python (optional, required for coverage filtering)
+```
+Python 3.8 or later
+```
+Cflow (optional, required for coverage filtering)
+```
+cflow (GNU cflow) 1.6
+```
## How to run
```
$ make help
@@ -41,7 +49,7 @@ $ make doc
Would generate the doxygen documentation in build/doc
```
-$ make run | run_formatted | run_col | run_col_formatted
+$ make run | run_formatted | run_col | run_col_formatted
```
Would build all unit tests and runs them one after the other with different
options between normal and formatted and colored for easily spotting errors
@@ -52,3 +60,30 @@ $ make coverage
Would build all unit tests, runs them one after the other, then generates html code
coverage and places them in build/coverage with initial file index.html
+## Running individual tests
+From each test directory, you can build, run the test, and generate gcov coverage with the default "all" target like so:
+```
+$ make -C list
+```
+For convenience, a "gcov" target is also provided.
+```
+$ make -C list gcov
+```
+Alternatively, you can generate filtered coverage with the "lcov" target:
+```
+$ make -C list lcov
+```
+You can also generate an html coverage report with the lcovhtml target:
+```
+$ make -C list lcovhtml
+```
+
+## Coverage Filtering ##
+Coverage filtering is meant to remove "unintentional" or "incidental" test coverage that is generated by other test cases which call a specific function but are not meant to test that function.
+In order to use coverage filtering and the associated lcov and lcovhtml targets, you must install the "optional" requirements listed above.
+
+
+Additionally, you must also document which functions you are targeting inside each _utest.c file with a tag inside each file similar to the following:
+```
+@coverage vFunctionNameHere vAnotherFunctionNameHere
+```
diff --git a/FreeRTOS/Test/CMock/config/fake_port.h b/FreeRTOS/Test/CMock/config/fake_port.h
new file mode 100644
index 000000000..b65540549
--- /dev/null
+++ b/FreeRTOS/Test/CMock/config/fake_port.h
@@ -0,0 +1,46 @@
+/*
+ * FreeRTOS V202012.00
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * https://www.FreeRTOS.org
+ * https://github.com/FreeRTOS
+ *
+ */
+
+#ifndef FAKE_PORT_H
+#define FAKE_PORT_H
+
+void vFakePortYield( void );
+void vFakePortYieldFromISR( void );
+void vFakePortYieldWithinAPI( void );
+
+void vFakePortDisableInterrupts( void );
+void vFakePortEnableInterrupts( void );
+void vFakePortClearInterruptMaskFromISR( UBaseType_t uxNewMaskValue );
+void vFakePortClearInterruptMask( UBaseType_t uxNewMaskValue );
+UBaseType_t ulFakePortSetInterruptMaskFromISR( void );
+UBaseType_t ulFakePortSetInterruptMask( void );
+
+void vFakePortAssertIfInterruptPriorityInvalid( void );
+
+void vFakePortEnterCriticalSection( void );
+void vFakePortExitCriticalSection( void );
+
+#endif /* FAKE_PORT_H */
diff --git a/FreeRTOS/Test/CMock/config/portmacro.h b/FreeRTOS/Test/CMock/config/portmacro.h
index 060739a69..be2c9d1d1 100644
--- a/FreeRTOS/Test/CMock/config/portmacro.h
+++ b/FreeRTOS/Test/CMock/config/portmacro.h
@@ -22,38 +22,37 @@
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
- * 1 tab == 4 spaces!
*/
/*
- Changes from V3.2.3
-
- + Modified portENTER_SWITCHING_ISR() to allow use with GCC V4.0.1.
-
- Changes from V3.2.4
-
- + Removed the use of the %0 parameter within the assembler macros and
- replaced them with hard coded registers. This will ensure the
- assembler does not select the link register as the temp register as
- was occasionally happening previously.
-
- + The assembler statements are now included in a single asm block rather
- than each line having its own asm block.
-
- Changes from V4.5.0
-
- + Removed the portENTER_SWITCHING_ISR() and portEXIT_SWITCHING_ISR() macros
- and replaced them with portYIELD_FROM_ISR() macro. Application code
- should now make use of the portSAVE_CONTEXT() and portRESTORE_CONTEXT()
- macros as per the V4.5.1 demo code.
-*/
+ * Changes from V3.2.3
+ *
+ + Modified portENTER_SWITCHING_ISR() to allow use with GCC V4.0.1.
+ +
+ + Changes from V3.2.4
+ +
+ + Removed the use of the %0 parameter within the assembler macros and
+ + replaced them with hard coded registers. This will ensure the
+ + assembler does not select the link register as the temp register as
+ + was occasionally happening previously.
+ +
+ + The assembler statements are now included in a single asm block rather
+ + than each line having its own asm block.
+ +
+ + Changes from V4.5.0
+ +
+ + Removed the portENTER_SWITCHING_ISR() and portEXIT_SWITCHING_ISR() macros
+ + and replaced them with portYield_FROM_ISR() macro. Application code
+ + should now make use of the portSAVE_CONTEXT() and portRESTORE_CONTEXT()
+ + macros as per the V4.5.1 demo code.
+ */
#ifndef PORTMACRO_H
-#define PORTMACRO_H
+ #define PORTMACRO_H
-#ifdef __cplusplus
-extern "C" {
-#endif
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
/*-----------------------------------------------------------
* Port specific definitions.
@@ -66,42 +65,44 @@ extern "C" {
*/
/* Type definitions. */
-#define portCHAR char
-#define portFLOAT float
-#define portDOUBLE double
-#define portLONG long
-#define portSHORT short
-#define portSTACK_TYPE uint32_t
-#define portBASE_TYPE long
-
-typedef portSTACK_TYPE StackType_t;
-typedef long BaseType_t;
-typedef unsigned long UBaseType_t;
-
-#if( configUSE_16_BIT_TICKS == 1 )
- typedef uint16_t TickType_t;
- #define portMAX_DELAY ( TickType_t ) 0xffff
-#else
- typedef uint32_t TickType_t;
- #define portMAX_DELAY ( TickType_t ) 0xffffffffUL
-#endif
+ #define portCHAR char
+ #define portFLOAT float
+ #define portDOUBLE double
+ #define portLONG long
+ #define portSHORT short
+ #define portSTACK_TYPE uint32_t
+ #define portBASE_TYPE long
+
+ typedef portSTACK_TYPE StackType_t;
+ typedef long BaseType_t;
+ typedef unsigned long UBaseType_t;
+
+ #if ( configUSE_16_BIT_TICKS == 1 )
+ typedef uint16_t TickType_t;
+ #define portMAX_DELAY ( TickType_t ) 0xffff
+ #else
+ typedef uint32_t TickType_t;
+ #define portMAX_DELAY ( TickType_t ) 0xffffffffUL
+ #endif
/*-----------------------------------------------------------*/
+/* Requires definition of UBaseType_t */
+ #include "fake_port.h"
+
/* Hardware specifics. */
-#define portSTACK_GROWTH ( -1 )
-#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
-#define portBYTE_ALIGNMENT 8
-#define portYIELD()
-#define portNOP()
+ #define portSTACK_GROWTH ( -1 )
+ #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
+ #define portBYTE_ALIGNMENT 8
+ #define portNOP() __asm volatile ( "NOP" )
/*
* These define the timer to use for generating the tick interrupt.
* They are put in this file so they can be shared between "port.c"
* and "portisr.c".
*/
-#define portTIMER_REG_BASE_PTR
-#define portTIMER_CLK_ENABLE_BIT
-#define portTIMER_AIC_CHANNEL
+ #define portTIMER_REG_BASE_PTR
+ #define portTIMER_CLK_ENABLE_BIT
+ #define portTIMER_AIC_CHANNEL
/*-----------------------------------------------------------*/
/* Task utilities. */
@@ -113,37 +114,39 @@ typedef unsigned long UBaseType_t;
* THUMB mode code will result in a compile time error.
*/
-#define portRESTORE_CONTEXT()
+ #define portRESTORE_CONTEXT()
/*-----------------------------------------------------------*/
-#define portSAVE_CONTEXT()
-
-#define portYIELD_FROM_ISR()
+ #define portSAVE_CONTEXT()
+ #define portYIELD() vFakePortYield()
+ #define portYIELD_WITHIN_API() vFakePortYieldWithinAPI()
+ #define portYIELD_FROM_ISR() vFakePortYieldFromISR()
/* Critical section handling. */
-
-#define portDISABLE_INTERRUPTS()
-
-#define portENABLE_INTERRUPTS()
-
-
-extern void vPortEnterCritical( void );
-extern void vPortExitCritical( void );
-
-#define portENTER_CRITICAL()
-#define portEXIT_CRITICAL()
-
-#undef portUSING_MPU_WRAPPERS
+ #define portDISABLE_INTERRUPTS() vFakePortDisableInterrupts()
+ #define portENABLE_INTERRUPTS() vFakePortEnableInterrupts()
+ #define portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) \
+ vFakePortClearInterruptMaskFromISR( x )
+ #define portSET_INTERRUPT_MASK_FROM_ISR() \
+ ulFakePortSetInterruptMaskFromISR()
+ #define portSET_INTERRUPT_MASK() ulFakePortSetInterruptMask()
+ #define portCLEAR_INTERRUPT_MASK( x ) vFakePortClearInterruptMask( x )
+ #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() \
+ vFakePortAssertIfInterruptPriorityInvalid()
+
+ #define portENTER_CRITICAL() vFakePortEnterCriticalSection()
+ #define portEXIT_CRITICAL() vFakePortExitCriticalSection()
+
+ #undef portUSING_MPU_WRAPPERS
/*-----------------------------------------------------------*/
/* Task function macros as described on the FreeRTOS.org WEB site. */
-#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters )
-#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters )
+ #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters )
+ #define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters )
-#ifdef __cplusplus
-}
-#endif
+ #ifdef __cplusplus
+ }
+ #endif
#endif /* PORTMACRO_H */
-
diff --git a/FreeRTOS/Test/CMock/doc/Makefile b/FreeRTOS/Test/CMock/doc/Makefile
index 7290d2499..5febfd9b1 100644
--- a/FreeRTOS/Test/CMock/doc/Makefile
+++ b/FreeRTOS/Test/CMock/doc/Makefile
@@ -1,7 +1,10 @@
+include ../makefile.in
DOXY_ARGS := "INPUT=../ \n FILE_PATTERNS=*.c *.h\n RECURSIVE=YES\n"
DOXY_ARGS += "EXCLUDE=../CMock/ ../config/ ../scripts/ ../build/\n"
+DOXY_ARGS += "ALIASES+=\"coverage=\\xrefitem coverage \\\"Coverage Target\\\" \\\"Coverage Targets\\\" \"\n"
DOXY_ARGS += "PROJECT_NAME=FreeRTOS Unit Tests\nGENERATE_LATEX=NO\n"
DOXY_ARGS += "OUTPUT_DIRECTORY=$(DOC_DIR)"
all:
- echo -e $(DOXY_ARGS) | doxygen -
+ mkdir -p $(DOC_DIR)
+ echo -e $(DOXY_ARGS) | doxygen -
diff --git a/FreeRTOS/Test/CMock/lcovrc b/FreeRTOS/Test/CMock/lcovrc
new file mode 100644
index 000000000..93028bce6
--- /dev/null
+++ b/FreeRTOS/Test/CMock/lcovrc
@@ -0,0 +1,3 @@
+lcov_branch_coverage = 1
+genhtml_branch_coverage = 1
+geninfo_intermediate = auto
diff --git a/FreeRTOS/Test/CMock/list/Makefile b/FreeRTOS/Test/CMock/list/Makefile
index 12dcd9413..f309ead9c 100644
--- a/FreeRTOS/Test/CMock/list/Makefile
+++ b/FreeRTOS/Test/CMock/list/Makefile
@@ -1,72 +1,41 @@
-# Change according to what your unit test directory is.
-# For example if testing queue.c your directory should be called queue
-# and the project name should be queue
-# if testing list.c your directory should be called list
-# and the project name should be list
-PROJECT := list
+# indent with spaces
+.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX)
-# List the dependency files you wish to mock
-MOCK_FILES_FP :=
+# Do not move this line below the include
+MAKEFILE_ABSPATH := $(abspath $(lastword $(MAKEFILE_LIST)))
+include ../makefile.in
+# PROJECT_SRC lists the .c files under test
+PROJECT_SRC := list.c
-# List the options the compilation would need
-CPPFLAGS += -DportUSING_MPU_WRAPPERS=0
+# PROJECT_DEPS_SRC list the .c file that are dependencies of PROJECT_SRC files
+# Files in PROJECT_DEPS_SRC are excluded from coverage measurements
+PROJECT_DEPS_SRC :=
-# Try not to edit beyond this line
-MOCK_FILES := $(notdir $(MOCK_FILES_FP))
-MOCK_OBJ := $(addprefix mock_,$(MOCK_FILES:.h=.o))
-MOCK_SRC := $(addprefix mock_,$(MOCK_FILES:.h=.c))
-EXEC := $(PROJECT)_utest
-PROJECT_DIR := $(abspath .)
-SCRATCH_DIR := $(GENERATED_DIR)/$(PROJECT)
-PROJ_LIB_DIR := $(SCRATCH_DIR)/lib
-MOCK_OBJ_LIST := $(addprefix $(PROJ_LIB_DIR)/,$(MOCK_OBJ))
-MOCKS_DIR := $(SCRATCH_DIR)/mocks
-MOCK_SRC_LIST := $(addprefix $(MOCKS_DIR)/,$(MOCK_SRC))
-CFLAGS += -I$(MOCKS_DIR)
-COVERAGE_OPTS := -fprofile-arcs -ftest-coverage -fprofile-generate
+# PROJECT_HEADER_DEPS: headers that should be excluded from coverage measurements.
+PROJECT_HEADER_DEPS := FreeRTOS.h
-ifeq ($(MOCK_FILES_FP),)
- $(shell mkdir -p $(MOCKS_DIR))
- $(shell touch -a $(MOCKS_DIR)/mock_dummy.c)
-endif
+# SUITE_UT_SRC: .c files that contain test cases (must end in _utest.c)
+SUITE_UT_SRC := list_utest.c
-$(MOCKS_DIR)/mock_%.c : Makefile $(PROJECT_DIR)/$(PROJECT).yml | directories
- cd $(SCRATCH_DIR) && \
- ruby $(CMOCK_EXEC_DIR)/cmock.rb -o$(PROJECT_DIR)/$(PROJECT).yml \
- $(MOCK_FILES_FP)
+# SUITE_SUPPORT_SRC: .c files used for testing that do not contain test cases.
+# Paths are relative to PROJECT_DIR
+SUITE_SUPPORT_SRC :=
-$(PROJ_LIB_DIR)/mock_%.o : $(MOCKS_DIR)/mock_%.c
- $(CC) -c $< -fPIC $(CFLAGS) -o $@
+# List the headers used by PROJECT_SRC that you would like to mock
+MOCK_FILES_FP :=
-$(BIN_DIR)/$(EXEC) : $(SCRATCH_DIR)/test_runner.o \
- $(SCRATCH_DIR)/$(PROJECT).o \
- $(SCRATCH_DIR)/$(PROJECT)_utest.o \
- $(MOCK_OBJ_LIST)
- $(CC) $+ $(LDFLAGS) -o $@
+# List any addiitonal flags needed by the preprocessor
+CPPFLAGS += -DportUSING_MPU_WRAPPERS=0
-$(SCRATCH_DIR)/test_runner.o : $(SCRATCH_DIR)/test_runner.c \
- $(SCRATCH_DIR)/$(PROJECT).o
- $(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
+# List any addiitonal flags needed by the compiler
+CFLAGS +=
-$(SCRATCH_DIR)/$(PROJECT)_utest.o : $(PROJECT_DIR)/$(PROJECT)_utest.c \
- $(MOCKS_DIR)/mock_*.c \
- | directories
- $(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
+# Try not to edit beyond this line unless necessary.
-$(SCRATCH_DIR)/$(PROJECT).o : $(KERNEL_DIR)/$(PROJECT).c
- $(CC) -c $< $(CPPFLAGS) $(CFLAGS) $(COVERAGE_OPTS) -o $@
+# Project is determined based on path: $(UT_ROOT_DIR)/$(PROJECT)
+PROJECT := $(lastword $(subst /, ,$(dir $(abspath $(MAKEFILE_ABSPATH)))))
-$(SCRATCH_DIR)/test_runner.c : $(SCRATCH_DIR)/$(PROJECT)_utest.o \
- Makefile | directories
- ruby $(UNITY_BIN_DIR)/generate_test_runner.rb $(EXEC).c \
- $(PROJECT_DIR)/$(PROJECT).yml $@
+export
-.PHONY: directories
-directories :
- -mkdir $(SCRATCH_DIR)
- -mkdir $(MOCKS_DIR)
- -mkdir $(PROJ_LIB_DIR)
-
-# prevent deletion by chain of implicit rules
-NO_DELETE: $(MOCK_SRC_LIST)
+include ../testdir.mk
diff --git a/FreeRTOS/Test/CMock/list/list_utest.c b/FreeRTOS/Test/CMock/list/list_utest.c
index 28e170b90..8a379f2f0 100644
--- a/FreeRTOS/Test/CMock/list/list_utest.c
+++ b/FreeRTOS/Test/CMock/list/list_utest.c
@@ -115,8 +115,9 @@ static void validate_empty_list( const List_t * const pxList )
/*!
* @brief validate the initilization function of a list
+ * @coverage vListInitialise
*/
-void test_vListInitialisee_Success( void )
+void test_vListInitialise_Success( void )
{
List_t pxList;
@@ -126,6 +127,7 @@ void test_vListInitialisee_Success( void )
/*!
* @brief validate the initializatiom function of a list item
+ * @coverage vListInitialiseItem
*/
void test_vListInitialiseItem_Sucess( void )
{
@@ -138,6 +140,7 @@ void test_vListInitialiseItem_Sucess( void )
/*!
* @brief test vListIntertEnd successful case with only 1 item
* @details This test ensures the list is sane when 1 item is inserted
+ * @coverage vListInsertEnd
*/
void test_vListInsertEnd_Success_1_item( void )
{
@@ -166,6 +169,7 @@ void test_vListInsertEnd_Success_1_item( void )
/*!
* @brief test vListIntertEnd successful case with only 2 items
* @details This test ensures the list is sane when 2 items are inserted
+ * @coverage vListInsertEnd
*/
void test_vListInsertEnd_Success_2_items( void )
{
@@ -199,6 +203,7 @@ void test_vListInsertEnd_Success_2_items( void )
/*!
* @brief test vListIntertEnd successful case with only 3 items
* @details This test ensures the list is sane when 3 items are inserted
+ * @coverage vListInsertEnd
*/
void test_vListInsertEnd_Success_3_items( void )
{
@@ -238,6 +243,7 @@ void test_vListInsertEnd_Success_3_items( void )
/*!
* @brief test vListIntertEnd successful case with multiple items (5000)
* @details This test ensures the list is sane when 5000 items are inserted
+ * @coverage vListInsertEnd
*/
void test_vListInsertEnd_success_multiple_items( void )
{
@@ -284,8 +290,9 @@ void test_vListInsertEnd_success_multiple_items( void )
/*!
* @brief test vListIntert successful case with 1 item
* @details This test ensures the list is sane when 1 item is inserted
+ * @coverage vListInsert
*/
-void test_vListInsert_sucess_1_item( void )
+void test_vListInsert_success_1_item( void )
{
List_t pxList;
ListItem_t pxNewListItem;
@@ -311,6 +318,7 @@ void test_vListInsert_sucess_1_item( void )
/*!
* @brief test vListIntert successful case with 2 items
* @details This test ensures the list is sane when 2 items are inserted
+ * @coverage vListInsert
*/
void test_vListInsert_sucess_2_items( void )
{
@@ -346,6 +354,7 @@ void test_vListInsert_sucess_2_items( void )
/*!
* @brief test vListIntert successful case with 3 items
* @details This test ensures the list is sane when 3 items are inserted
+ * @coverage vListInsert
*/
void test_vListInsert_sucess_3_items( void )
{
@@ -386,6 +395,7 @@ void test_vListInsert_sucess_3_items( void )
/*!
* @brief test vListIntert successful case with multiple items (5000)
* @details This test ensures the list is sane when multiple items are inserted
+ * @coverage vListInsert
*/
void test_vListInsert_success_multiple_items( void )
{
@@ -433,6 +443,7 @@ void test_vListInsert_success_multiple_items( void )
/*!
* @brief test uxListRemove successful case with 1 item
* @details This test ensures the list is sane when 1 item is removed
+ * @coverage vListInsert
*/
void test_vListInsert_success_vportMAXDELAY( void )
{
@@ -468,6 +479,7 @@ void test_vListInsert_success_vportMAXDELAY( void )
/*!
* @brief test uxListRemove successful case with 1 item
* @details This test ensures the list is sane when 1 item is removed
+ * @coverage uxListRemove
*/
void test_uxListRemove_sucesss( void )
{
@@ -505,6 +517,7 @@ void test_uxListRemove_sucesss( void )
/*!
* @brief test uxListRemove successful case with 2 items
* @details This test ensures the list is sane when 2 items are removed
+ * @coverage uxListRemove
*/
void test_uxListRemove_multiple( void )
{
@@ -590,6 +603,7 @@ void test_uxListRemove_multiple( void )
* being removed
* @details This test ensures that the function uxListRemove reassigns the
* index to the previous element of the one being removed
+ * @coverage uxListRemove
*/
void test_uxListRemove_index_item( void )
{
diff --git a/FreeRTOS/Test/CMock/makefile.in b/FreeRTOS/Test/CMock/makefile.in
index 41e5ab147..17ab96cb6 100644
--- a/FreeRTOS/Test/CMock/makefile.in
+++ b/FreeRTOS/Test/CMock/makefile.in
@@ -1,65 +1,68 @@
-# Various directory locations
+# indent with spaces
+.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX)
-UT_ROOT_DIR_REL :=.
-UT_ROOT_DIR := $(abspath $(UT_ROOT_DIR_REL))
+# Various directories
+# Note: for this to work in subdirectories, this makefile must be the first one included.
+UT_ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
-BUILD_DIR := $(UT_ROOT_DIR)/build
-DOC_DIR := $(BUILD_DIR)/doc
-COVERAGE_DIR := $(BUILD_DIR)/coverage
-BIN_DIR := $(BUILD_DIR)/bin
-GENERATED_DIR := $(BUILD_DIR)/generated
-LIB_DIR := $(BUILD_DIR)/lib
+BUILD_DIR := $(UT_ROOT_DIR)/build
+DOC_DIR := $(BUILD_DIR)/doc
+COVERAGE_DIR := $(BUILD_DIR)/coverage
+BIN_DIR := $(BUILD_DIR)/bin
+GENERATED_DIR := $(BUILD_DIR)/generated
+LIB_DIR := $(BUILD_DIR)/lib
+TOOLS_DIR := $(UT_ROOT_DIR)/tools
+FREERTOS_DIR := $(abspath $(UT_ROOT_DIR)../../../FreeRTOS)
+KERNEL_DIR := $(abspath $(UT_ROOT_DIR)/../../../FreeRTOS/Source)
-FREERTOS_DIR_REL := ../../../FreeRTOS
-FREERTOS_DIR := $(abspath $(FREERTOS_DIR_REL))
+CMOCK_DIR := $(UT_ROOT_DIR)/CMock
+CMOCK_SRC_DIR := $(CMOCK_DIR)/src
+UNITY_DIR := $(CMOCK_DIR)/vendor/unity
+UNITY_SRC_DIR := $(UNITY_DIR)/src
+UNITY_INC_DIR := $(UNITY_DIR)/src
+UNITY_BIN_DIR := $(UNITY_DIR)/auto
+UNITY_EXTRAS_DIR := $(UNITY_DIR)/extras
+UNITY_MEM_DIR := $(UNITY_EXTRAS_DIR)/memory/src
+C_EXCEPTION_SRC_DIR := $(CMOCK_DIR)/vendor/c_exception/lib
+CMOCK_EXEC_DIR := $(CMOCK_DIR)/lib
-KERNEL_DIR_REL := ../../../FreeRTOS/Source
-KERNEL_DIR := $(abspath $(KERNEL_DIR_REL))
+# Include directories
+INCLUDE_DIR := -I$(KERNEL_DIR)/include
+INCLUDE_DIR += -I.
+INCLUDE_DIR += -I$(UT_ROOT_DIR)/config
+INCLUDE_DIR += -I$(UNITY_INC_DIR)
+INCLUDE_DIR += -I$(CMOCK_SRC_DIR)
+INCLUDE_DIR += -I$(UNITY_MEM_DIR)
+INCLUDE_DIR += -I$(C_EXCEPTION_SRC_DIR)
-CMOCK_DIR := $(UT_ROOT_DIR)/CMock
-CMOCK_SRC_DIR := $(CMOCK_DIR)/src
-UNITY_DIR := $(CMOCK_DIR)/vendor/unity
-UNITY_SRC_DIR := $(UNITY_DIR)/src
-UNITY_INC_DIR := $(UNITY_DIR)/src
-UNITY_BIN_DIR := $(UNITY_DIR)/auto
-UNITY_EXTRAS_DIR := $(UNITY_DIR)/extras
-UNITY_MEM_SRC_DIR := $(UNITY_EXTRAS_DIR)/memory/src
+# Preprocessor flags
+CPPFLAGS :=
-CMOCK_EXEC_DIR := $(CMOCK_DIR)/lib
+# Compiler flags
+CFLAGS := $(INCLUDE_DIR) -O0 -ggdb -pthread --std=c99 -Werror -Wall
+CFLAGS += -fsanitize=address,undefined -fsanitize-recover=address
+CFLAGS += -fstack-protector-all
+CFLAGS += -Wformat -Werror=format-security -Werror=array-bounds
+CFLAGS += -D_FORTIFY_SOURCE=2
-# Include directory location
-INCLUDE_DIR := -I$(KERNEL_DIR)/include -I. -I$(UT_ROOT_DIR)/config
-INCLUDE_DIR += -I$(UNITY_INC_DIR)
-INCLUDE_DIR += -I$(CMOCK_SRC_DIR)
-INCLUDE_DIR += -I$(UNITY_MEM_SRC_DIR)
+# Linker flags
+LDFLAGS := -L$(LIB_DIR) -Wl,-rpath,$(LIB_DIR)
+LDFLAGS += -fsanitize=address,undefined
+LDFLAGS += -pthread
+LDFLAGS += -lunity
+LDFLAGS += -lunitymemory
+LDFLAGS += -lcexception
+LDFLAGS += -lcmock
+LDFLAGS += -lgcov
-CPPFLAGS :=
-CFLAGS := $(INCLUDE_DIR) -O0 -ggdb -pthread --std=c99 -Werror -Wall
-CFLAGS += -fsanitize=address,undefined -fsanitize-recover=address
-CFLAGS += -fstack-protector-all
-CFLAGS += -Wformat -Werror=format-security -Werror=array-bounds
-CFLAGS += -D_FORTIFY_SOURCE=2
+# Shared libraries
+LIBS := libunity
+LIBS += libcmock
+LIBS += libunitymemory
+LIBS += libcexception
-LDFLAGS := -L$(LIB_DIR) -Wl,-rpath,$(LIB_DIR)
-LDFLAGS += -fsanitize=address,undefined
-LDFLAGS += -pthread
-LDFLAGS += -lunity
-LDFLAGS += -lunitymemory
-LDFLAGS += -lcmock
-LDFLAGS += -lgcov
+LCOV_OPTS := --config-file $(UT_ROOT_DIR)/lcovrc
-export BUILD_DIR
-export DOC_DIR
-export GENERATED_DIR
-export COVERAGE_DIR
-export BIN_DIR
-export UNITS
-export CFLAGS
-export LDFLAGS
-export CPPFLAGS
-export CMOCK_EXEC_DIR
-export KERNEL_DIR
-export UNITY_BIN_DIR
-export LIB_DIR
-export UT_ROOT_DIR
+# export everything in this file
+export
diff --git a/FreeRTOS/Test/CMock/queue/Makefile b/FreeRTOS/Test/CMock/queue/Makefile
index d5044cc03..34deff580 100644
--- a/FreeRTOS/Test/CMock/queue/Makefile
+++ b/FreeRTOS/Test/CMock/queue/Makefile
@@ -1,73 +1,14 @@
-# Change according to what your unit test directory is.
-# For example if testing queue.c your directory should be called queue
-# and the project name should be queue
-# if testing list.c your directory should be called list
-# and the project name should be list
-PROJECT := queue
+# Indent with spaces
+.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX)
+# Do not move this line below the include
+MAKEFILE_ABSPATH := $(abspath $(lastword $(MAKEFILE_LIST)))
+include ../makefile.in
-# List the dependency files you wish to mock
-MOCK_FILES_FP := $(KERNEL_DIR)/include/task.h
-MOCK_FILES_FP += $(KERNEL_DIR)/include/list.h
-MOCK_FILES_FP += $(UT_ROOT_DIR)/config/fake_assert.h
+# SUITES lists the suites contained in subdirectories of this directory
+SUITES += two_tests
-# List the options the compilation would need
-CPPFLAGS += -DportUSING_MPU_WRAPPERS=0
+# PROJECT and SUITE variables are determined based on path like so:
+# $(UT_ROOT_DIR)/$(PROJECT)/$(SUITE)
+PROJECT := $(lastword $(subst /, ,$(dir $(abspath $(MAKEFILE_ABSPATH)))))
-# Try not to edit beyond this line
-MOCK_FILES := $(notdir $(MOCK_FILES_FP))
-MOCK_OBJ := $(addprefix mock_,$(MOCK_FILES:.h=.o))
-MOCK_SRC := $(addprefix mock_,$(MOCK_FILES:.h=.c))
-EXEC := $(PROJECT)_utest
-PROJECT_DIR := $(abspath .)
-SCRATCH_DIR := $(GENERATED_DIR)/$(PROJECT)
-PROJ_LIB_DIR := $(SCRATCH_DIR)/lib
-MOCK_OBJ_LIST := $(addprefix $(PROJ_LIB_DIR)/,$(MOCK_OBJ))
-MOCKS_DIR := $(SCRATCH_DIR)/mocks
-MOCK_SRC_LIST := $(addprefix $(MOCKS_DIR)/,$(MOCK_SRC))
-CFLAGS += -I$(MOCKS_DIR)
-COVERAGE_OPTS := -fprofile-arcs -ftest-coverage -fprofile-generate
-
-ifeq ($(MOCK_FILES_FP),)
- $(shell mkdir -p $(MOCKS_DIR))
- $(shell touch -a $(MOCKS_DIR)/mock_dummy.c)
-endif
-
-$(MOCKS_DIR)/mock_%.c : Makefile $(PROJECT_DIR)/$(PROJECT).yml | directories
- cd $(SCRATCH_DIR) && \
- ruby $(CMOCK_EXEC_DIR)/cmock.rb -o$(PROJECT_DIR)/$(PROJECT).yml \
- $(MOCK_FILES_FP)
-
-$(PROJ_LIB_DIR)/mock_%.o : $(MOCKS_DIR)/mock_%.c
- $(CC) -c $< -fPIC $(CFLAGS) -o $@
-
-$(BIN_DIR)/$(EXEC) : $(SCRATCH_DIR)/test_runner.o \
- $(SCRATCH_DIR)/$(PROJECT).o \
- $(SCRATCH_DIR)/$(PROJECT)_utest.o \
- $(MOCK_OBJ_LIST)
- $(CC) $+ $(LDFLAGS) -o $@
-
-$(SCRATCH_DIR)/test_runner.o : $(SCRATCH_DIR)/test_runner.c \
- $(SCRATCH_DIR)/$(PROJECT).o
- $(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
-
-$(SCRATCH_DIR)/$(PROJECT)_utest.o : $(PROJECT_DIR)/$(PROJECT)_utest.c \
- $(MOCKS_DIR)/mock_*.c \
- | directories
- $(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
-
-$(SCRATCH_DIR)/$(PROJECT).o : $(KERNEL_DIR)/$(PROJECT).c
- $(CC) -c $< $(CPPFLAGS) $(CFLAGS) $(COVERAGE_OPTS) -o $@
-
-$(SCRATCH_DIR)/test_runner.c : $(SCRATCH_DIR)/$(PROJECT)_utest.o \
- Makefile | directories
- ruby $(UNITY_BIN_DIR)/generate_test_runner.rb $(EXEC).c \
- $(PROJECT_DIR)/$(PROJECT).yml $@
-
-.PHONY: directories
-directories :
- -mkdir $(SCRATCH_DIR)
- -mkdir $(MOCKS_DIR)
- -mkdir $(PROJ_LIB_DIR)
-
-# prevent deletion by chain of implicit rules
-NO_DELETE: $(MOCK_SRC_LIST)
+include ../subdir.mk
diff --git a/FreeRTOS/Test/CMock/queue/queue_utest.c b/FreeRTOS/Test/CMock/queue/queue_utest.c
index e6f9439a0..9db394f24 100644
--- a/FreeRTOS/Test/CMock/queue/queue_utest.c
+++ b/FreeRTOS/Test/CMock/queue/queue_utest.c
@@ -42,6 +42,7 @@
#include "mock_task.h"
#include "mock_list.h"
#include "mock_fake_assert.h"
+#include "mock_fake_port.h"
/* ============================ GLOBAL VARIABLES =========================== */
@@ -61,7 +62,12 @@ void vPortFree( void * pv )
******************************************************************************/
void setUp( void )
{
+ mock_task_Init();
+ mock_fake_assert_Init();
+ mock_fake_port_Init();
vFakeAssert_Ignore();
+ vFakePortEnterCriticalSection_Ignore();
+ vFakePortExitCriticalSection_Ignore();
/* Track calls to malloc / free */
UnityMalloc_StartTest();
@@ -71,6 +77,15 @@ void setUp( void )
void tearDown( void )
{
UnityMalloc_EndTest();
+
+ mock_task_Verify();
+ mock_task_Destroy();
+
+ mock_fake_assert_Verify();
+ mock_fake_assert_Destroy();
+
+ mock_fake_port_Verify();
+ mock_fake_port_Destroy();
}
/*! called at the beginning of the whole suite */
@@ -86,7 +101,7 @@ int suiteTearDown( int numFailures )
/*!
* @brief xQueueCreate happy path.
- *
+ * @coverage xQueueGenericCreate
*/
void test_xQueueCreate_Success( void )
{
diff --git a/FreeRTOS/Test/CMock/queue/two_tests/Makefile b/FreeRTOS/Test/CMock/queue/two_tests/Makefile
new file mode 100644
index 000000000..1c79a491d
--- /dev/null
+++ b/FreeRTOS/Test/CMock/queue/two_tests/Makefile
@@ -0,0 +1,47 @@
+# Indent with spaces
+.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX)
+
+# Do not move this line below the include
+MAKEFILE_ABSPATH := $(abspath $(lastword $(MAKEFILE_LIST)))
+include ../../makefile.in
+
+# PROJECT_SRC lists the .c files under test
+PROJECT_SRC += queue.c
+
+# PROJECT_DEPS_SRC list the .c file that are dependencies of PROJECT_SRC files
+# Files in PROJECT_DEPS_SRC are excluded from coverage measurements
+PROJECT_DEPS_SRC +=
+
+# PROJECT_HEADER_DEPS: headers that should be excluded from coverage measurements.
+PROJECT_HEADER_DEPS += FreeRTOS.h
+
+# SUITE_UT_SRC: .c files that contain test cases (must end in _utest.c)
+SUITE_UT_SRC += queue_2_utest.c
+SUITE_UT_SRC += queue_1_utest.c
+
+# SUITE_SUPPORT_SRC: .c files used for testing that do not contain test cases.
+# Paths are relative to PROJECT_DIR
+SUITE_SUPPORT_SRC +=
+
+# List the headers used by PROJECT_SRC that you would like to mock
+MOCK_FILES_FP += $(KERNEL_DIR)/include/task.h
+MOCK_FILES_FP += $(KERNEL_DIR)/include/list.h
+MOCK_FILES_FP += $(UT_ROOT_DIR)/config/fake_assert.h
+MOCK_FILES_FP += $(UT_ROOT_DIR)/config/fake_port.h
+
+# List any addiitonal flags needed by the preprocessor
+CPPFLAGS += -DportUSING_MPU_WRAPPERS=0
+
+# List any addiitonal flags needed by the compiler
+CFLAGS +=
+
+# Try not to edit beyond this line unless necessary.
+
+# Project / Suite are determined based on path: $(UT_ROOT_DIR)/$(PROJECT)/$(SUITE)
+PROJECT := $(lastword $(subst /, ,$(dir $(abspath $(MAKEFILE_ABSPATH)/../))))
+SUITE := $(lastword $(subst /, ,$(dir $(MAKEFILE_ABSPATH))))
+
+# Make variables available to included makefile
+export
+
+include ../../testdir.mk
diff --git a/FreeRTOS/Test/CMock/queue/two_tests/queue_1_utest.c b/FreeRTOS/Test/CMock/queue/two_tests/queue_1_utest.c
new file mode 120000
index 000000000..50aba69ca
--- /dev/null
+++ b/FreeRTOS/Test/CMock/queue/two_tests/queue_1_utest.c
@@ -0,0 +1 @@
+../queue_utest.c \ No newline at end of file
diff --git a/FreeRTOS/Test/CMock/queue/two_tests/queue_2_utest.c b/FreeRTOS/Test/CMock/queue/two_tests/queue_2_utest.c
new file mode 120000
index 000000000..50aba69ca
--- /dev/null
+++ b/FreeRTOS/Test/CMock/queue/two_tests/queue_2_utest.c
@@ -0,0 +1 @@
+../queue_utest.c \ No newline at end of file
diff --git a/FreeRTOS/Test/CMock/subdir.mk b/FreeRTOS/Test/CMock/subdir.mk
new file mode 100644
index 000000000..4da2bb800
--- /dev/null
+++ b/FreeRTOS/Test/CMock/subdir.mk
@@ -0,0 +1,46 @@
+# Indent with spaces
+.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX)
+
+# Define directory paths
+SCRATCH_DIR := $(GENERATED_DIR)/$(PROJECT)
+
+LCOV_LIST := $(foreach suite,$(SUITES),$(SCRATCH_DIR)/$(PROJECT)_$(suite).info)
+COVINFO := $(GENERATED_DIR)/$(PROJECT).info
+COV_REPORT_DIR := $(SCRATCH_DIR)/coverage
+
+.PHONY: all clean libs run bin lcov zerocoverage lcovhtml
+
+all: run
+
+clean:
+ rm -rf $(SCRATCH_DIR)
+ rm -f $(BIN_DIR)/$(PROJECT)*_utest
+ rm -f $(COVINFO)
+
+libs:
+ make -C $(UT_ROOT_DIR) libs
+
+lcov : $(COVINFO)
+
+# run each suite and leave gcda / gcov files in place
+run: libs
+ $(foreach suite,$(SUITES),\
+ make -C $(suite) run;)
+
+bin: $(EXEC_LIST)
+
+zerocoverage:
+ $(LCOV_BIN_DIR)/lcov --zerocounters --directory $(SCRATCH_DIR)
+
+# Generate lcov for each suite
+$(LCOV_LIST) :
+ $(foreach suite,$(SUITES),\
+ make -C $(suite) lcov;)
+
+# Combine lcov from each subdirectory into one lcov info file for the project
+$(COVINFO) : $(LCOV_LIST)
+ lcov $(LCOV_OPTS) -o $@ $(foreach cov,$(LCOV_LIST),--add-tracefile $(cov) )
+
+lcovhtml : $(COVINFO)
+ mkdir -p $(COV_REPORT_DIR)
+ genhtml $(COVINFO) $(LCOV_OPTS) --output-directory $(COV_REPORT_DIR)
diff --git a/FreeRTOS/Test/CMock/testdir.mk b/FreeRTOS/Test/CMock/testdir.mk
new file mode 100644
index 000000000..df043a224
--- /dev/null
+++ b/FreeRTOS/Test/CMock/testdir.mk
@@ -0,0 +1,201 @@
+# Indent with spaces
+.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX)
+
+# define the name for this test's output files
+ifdef SUITE
+EXEC_PREFIX := $(PROJECT)_$(SUITE)
+else
+EXEC_PREFIX := $(PROJECT)
+endif
+
+# Define directory paths
+
+ifdef SUITE
+PROJECT_DIR := $(abspath ../)
+else
+PROJECT_DIR := $(abspath .)
+endif
+
+SUITE_DIR := $(abspath .)
+
+ifdef SUITE
+SCRATCH_DIR := $(GENERATED_DIR)/$(PROJECT)/$(SUITE)
+else
+SCRATCH_DIR := $(GENERATED_DIR)/$(PROJECT)
+endif
+
+MOCKS_DIR := $(SCRATCH_DIR)/mocks
+PROJ_DIR := $(SCRATCH_DIR)/proj
+
+# Define mock details
+MOCK_FILES := $(notdir $(MOCK_FILES_FP))
+MOCK_HDR := $(addprefix mock_,$(MOCK_FILES))
+MOCK_SRC := $(addprefix mock_,$(MOCK_FILES:.h=.c))
+MOCK_OBJ := $(addprefix mock_,$(MOCK_FILES:.h=.o))
+MOCK_HDR_LIST := $(addprefix $(MOCKS_DIR)/,$(MOCK_HDR))
+MOCK_SRC_LIST := $(addprefix $(MOCKS_DIR)/,$(MOCK_SRC))
+MOCK_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/,$(MOCK_OBJ))
+CFLAGS += -I$(MOCKS_DIR)
+
+# Kernel files under test
+PROJ_SRC_LIST := $(addprefix $(KERNEL_DIR)/,$(PROJECT_SRC))
+PROJ_PP_LIST := $(addprefix $(PROJ_DIR)/,$(PROJECT_SRC:.c=.i))
+PROJ_OBJ_LIST := $(addprefix $(PROJ_DIR)/,$(PROJECT_SRC:.c=.o))
+PROJ_GCDA_LIST := $(PROJ_OBJ_LIST:.o=.gcda)
+
+# Unit test files
+SUITE_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/,$(SUITE_UT_SRC:.c=.o))
+RUNNER_SRC_LIST := $(addprefix $(SCRATCH_DIR)/,$(SUITE_UT_SRC:_utest.c=_utest_runner.c))
+RUNNER_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/,$(SUITE_UT_SRC:_utest.c=_utest_runner.o))
+
+# Support files
+SF_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/sf_,$(SUITE_SUPPORT_SRC:.c=.o))
+DEPS_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/dep_,$(PROJECT_DEPS_SRC:.c=.o))
+EXECS := $(addprefix $(EXEC_PREFIX)_,$(SUITE_UT_SRC:.c=))
+EXEC_LIST := $(addprefix $(BIN_DIR)/,$(EXECS))
+LCOV_LIST := $(addsuffix .info,$(addprefix $(SCRATCH_DIR)/,$(SUITE_UT_SRC:.c=)))
+COVINFO := $(abspath $(SCRATCH_DIR)/..)/$(EXEC_PREFIX).info
+LIBS_LIST := $(foreach lib, $(LIBS), $(LIB_DIR)/$(lib).so)
+
+# Coverage related options
+GCC_COV_OPTS := -fprofile-arcs -ftest-coverage -fprofile-generate
+GCOV_OPTS := --unconditional-branches --branch-probabilities
+COV_REPORT_DIR := $(SCRATCH_DIR)/coverage
+
+.PHONY: all clean run gcov bin lcov lcovhtml libs
+
+# Prevent deletion of intermediate files
+NO_DELETE : $(MOCK_HDR_LIST) $(MOCK_SRC_LIST) $(MOCK_OBJ_LIST) \
+ $(DEPS_OBJ_LIST) $(SF_OBJ_LIST) $(EXEC_LIST) \
+ $(PROJ_PP_LIST) $(PROJ_OBJ_LIST) $(PROJ_GCDA_LIST) \
+ $(SUITE_OBJ_LIST) $(RUNNER_SRC_LIST) $(RUNNER_OBJ_LIST) \
+ $(COVINFO) $(LCOV_LIST)
+
+# Cases that run test binaries cannot be run in parallel.
+.NOTPARALLEL : $(COVINFO) $(LCOV_LIST) $(PROJ_GCDA_LIST)
+
+.DEFAULT_GOAL := run
+
+# Generate gcov files by default
+run : gcov
+
+gcov : $(PROJ_GCDA_LIST)
+
+clean:
+ rm -rf $(SCRATCH_DIR)
+ rm -rf $(EXEC_LIST)
+ rm -rf $(COVINFO)
+
+$(LIBS_LIST) :
+ make -C $(UT_ROOT_DIR) libs
+
+define run-test
+$(1)
+
+endef
+
+# Run and append to gcov data files
+$(PROJ_GCDA_LIST) : $(EXEC_LIST)
+ rm -f $(PROJ_DIR)/*.gcda
+ mkdir -p $(BIN_DIR)
+ # run each test case
+ $(foreach bin,$^,$(call run-test,$(bin)))
+
+# Run and generate lcov
+lcov: $(COVINFO)
+
+lcovhtml : $(COVINFO)
+ mkdir -p $(COV_REPORT_DIR)
+ genhtml $(COVINFO) $(LCOV_OPTS) --output-directory $(COV_REPORT_DIR)
+
+bin: $(EXEC_LIST)
+
+# Generate _mock.c / .h files
+$(MOCK_HDR_LIST) $(MOCK_SRC_LIST) : $(PROJECT_DIR)/$(PROJECT).yml $(MOCK_FILES_FP)
+ mkdir -p $(SCRATCH_DIR) $(MOCKS_DIR)
+ cd $(SCRATCH_DIR) && \
+ ruby $(CMOCK_EXEC_DIR)/cmock.rb -o$(PROJECT_DIR)/$(PROJECT).yml \
+ $(MOCK_FILES_FP)
+
+# Generate callgraph for coverage filtering
+$(PROJ_DIR)/callgraph.json : $(PROJ_SRC_LIST)
+ mkdir -p $(PROJ_DIR)
+ python3 $(UT_ROOT_DIR)/tools/callgraph.py $^ > $@
+
+# preprocess proj files to expand macros for coverage
+$(PROJ_DIR)/%.i : $(KERNEL_DIR)/%.c
+ mkdir -p $(PROJ_DIR)
+ $(CC) -E $< $(CPPFLAGS) $(CFLAGS) -o $@
+
+# compile the project objects with coverage instrumented
+$(PROJ_DIR)/%.o : $(PROJ_DIR)/%.i
+ $(CC) -c $< $(CPPFLAGS) $(INCLUDE_DIR) $(GCC_COV_OPTS) -o $@
+
+# Build mock objects
+$(SCRATCH_DIR)/mock_%.o : $(MOCKS_DIR)/mock_%.c
+ $(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
+
+# compile unit tests
+$(SCRATCH_DIR)/%_utest.o : $(SUITE_DIR)/%_utest.c $(MOCK_HDR_LIST) $(LIBS_LIST)
+ mkdir -p $(SCRATCH_DIR)
+ $(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
+
+# compile support files
+$(SCRATCH_DIR)/sf_%.o : $(PROJECT_DIR)/%.c $(MOCK_HDR_LIST)
+ mkdir -p $(SCRATCH_DIR)
+ $(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
+
+# compile c files that are needed by PROJ but not mocked
+$(SCRATCH_DIR)/dep_%.o : $(KERNEL_DIR)/%.c
+ mkdir -p $(SCRATCH_DIR)
+ $(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
+
+# generate a test runner for each test file
+$(SCRATCH_DIR)/%_utest_runner.c : $(SUITE_DIR)/%_utest.c
+ mkdir -p $(SCRATCH_DIR)
+ ruby $(UNITY_BIN_DIR)/generate_test_runner.rb\
+ $(PROJECT_DIR)/$(PROJECT).yml $< $@
+
+# compile test runner
+$(SCRATCH_DIR)/%_utest_runner.o : $(SCRATCH_DIR)/%_utest_runner.c
+ mkdir -p $(SCRATCH_DIR)
+ $(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
+
+# Link the _utest binary
+$(EXEC_LIST) : $(BIN_DIR)/$(EXEC_PREFIX)_%_utest : $(SCRATCH_DIR)/%_utest.o \
+ $(SCRATCH_DIR)/%_utest_runner.o \
+ $(SF_OBJ_LIST) $(MOCK_OBJ_LIST) \
+ $(PROJ_OBJ_LIST) $(LIBS_LIST) \
+ $(DEPS_OBJ_LIST)
+ mkdir -p $(BIN_DIR)
+ $(CC) $< $(subst .o,_runner.o,$<) $(SF_OBJ_LIST) $(DEPS_OBJ_LIST) \
+ $(MOCK_OBJ_LIST) $(PROJ_OBJ_LIST) $(LDFLAGS) -o $@
+
+# Run the test runner and genrate a filtered gcov.json.gz file
+$(SCRATCH_DIR)/%_utest.info : $(BIN_DIR)/$(EXEC_PREFIX)_%_utest \
+ $(PROJ_DIR)/callgraph.json
+ # Remove any existing coverage data
+ rm -f $(PROJ_DIR)/*.gcda
+
+ # run the testrunner
+ $<
+
+ # Gather coverage into a json.gz file
+ gcov $(GCOV_OPTS) $(foreach src,$(PROJECT_SRC),$(PROJ_DIR)/$(src:.c=.gcda)) \
+ --json-format --stdout | gzip > $(subst .info,.json.gz,$@)
+
+ # Filter coverage based on tags in unit test file
+ $(TOOLS_DIR)/filtercov.py --in $(subst .info,.json.gz,$@) \
+ --map $(PROJ_DIR)/callgraph.json \
+ --test $(SUITE_DIR)/$*_utest.c \
+ --format lcov \
+ --out $@
+ -lcov $(LCOV_OPTS) --summary $@
+
+ # Remove temporary files
+ rm -f $(subst .info,.json.gz,$@)
+ rm -f $(PROJ_GCDA_LIST)
+
+# Combine lcov from each test bin into one lcov info file for the suite
+$(COVINFO) : $(LCOV_LIST)
+ lcov $(LCOV_OPTS) -q -o $@ $(foreach cov,$(LCOV_LIST),--add-tracefile $(cov) )
diff --git a/FreeRTOS/Test/CMock/tools/callgraph.py b/FreeRTOS/Test/CMock/tools/callgraph.py
new file mode 100755
index 000000000..7c3f57d32
--- /dev/null
+++ b/FreeRTOS/Test/CMock/tools/callgraph.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python3
+###############################################################################
+# FreeRTOS
+# Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# https://www.FreeRTOS.org
+# https://github.com/FreeRTOS
+###############################################################################
+import json
+import os
+import re
+import subprocess
+import sys
+from typing import Dict, List, Set
+
+target_files = sys.argv[1:]
+
+for f in target_files:
+ if not os.path.isfile(f):
+ print("ERROR: Input file {} does not exist.".format(f))
+ exit(1)
+
+ret = subprocess.run(
+ [
+ "cflow",
+ "--print-level",
+ "--no-main",
+ "--omit-arguments",
+ "--omit-symbol-names",
+ "--all",
+ ]
+ + target_files,
+ capture_output=True,
+)
+
+lineregex = (
+ r"^{\s*(?P<level>\d+)} \s*"
+ r"(?P<function>\S*)\(\) \<.* at "
+ r"(?P<filename>.*):\d+\>(:)?"
+ r"(?P<xref> \[see \d+\])?$"
+)
+linepattern = re.compile(lineregex)
+parent_stack = [""]
+last_indent_level = 0
+last_function_name = ""
+callmap: Dict[str, Set[str]] = {}
+callmap[""] = set()
+
+for line in ret.stdout.decode("utf-8").splitlines():
+ match = linepattern.match(line)
+ # Check that the function for this line is in a target file
+ if match and (match.group("filename") in target_files):
+ indent_level = int(match.group("level"))
+ function_name = match.group("function")
+
+ # Add an entry for the current function
+ if function_name not in callmap:
+ callmap[function_name] = set()
+
+ # Indent -> lower in the call stack
+ if indent_level > last_indent_level:
+ # add last function to the stack
+ parent_stack.append(last_function_name)
+
+ # Outdent -> higher in the call stack
+ elif last_indent_level > indent_level:
+ de_indent_steps = last_indent_level - indent_level
+ # De-indent = pop off the stack
+ for _i in range(0, de_indent_steps):
+ parent_stack.pop()
+
+ # Update parent function(s) dependency list
+ for parent in parent_stack:
+ callmap[parent].add(function_name)
+
+ last_function_name = function_name
+ last_indent_level = indent_level
+
+# remove zero-level fake parent
+callmap.pop("")
+
+callmap_list: Dict[str, List[str]] = {}
+# convert sets to lists for json output
+for key in callmap:
+ temp_list = list(callmap[key])
+ callmap_list[key] = temp_list
+
+print(json.dumps(callmap_list))
diff --git a/FreeRTOS/Test/CMock/tools/filtercov.py b/FreeRTOS/Test/CMock/tools/filtercov.py
new file mode 100755
index 000000000..3b1fc120f
--- /dev/null
+++ b/FreeRTOS/Test/CMock/tools/filtercov.py
@@ -0,0 +1,326 @@
+#!/usr/bin/env python3
+###############################################################################
+# FreeRTOS
+# Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# https://www.FreeRTOS.org
+# https://github.com/FreeRTOS
+###############################################################################
+import argparse
+import gzip
+import json
+import os
+import re
+import sys
+
+
+def main():
+ arg_parser = argparse.ArgumentParser(
+ description="Filter the contents of a gov intermediate coverage file"
+ )
+ arg_parser.add_argument(
+ "-i", "--in", required=True, help="input .gcov.json.gz file to be filtered"
+ )
+ arg_parser.add_argument(
+ "-m",
+ "--map",
+ required=True,
+ help="Json file containing a callmap for the files under test",
+ )
+ arg_parser.add_argument(
+ "-t",
+ "--test",
+ required=True,
+ help="The unit test .c file which contains @coverage flags.",
+ )
+ arg_parser.add_argument(
+ "-f", "--format", required=True, help="The output format to use (json|lcov)"
+ )
+ arg_parser.add_argument(
+ "-o",
+ "--out",
+ required=False,
+ help="output file path (otherwise stdout out is used)",
+ )
+
+ args = arg_parser.parse_args()
+ # Validate arguments
+ if not os.path.isfile(vars(args)["in"]):
+ print("The input file path does not exist.", file=sys.stderr)
+ sys.exit(1)
+
+ if vars(args)["out"] and not os.path.isdir(os.path.dirname(vars(args)["out"])):
+ print("The output directory does not exist.", file=sys.stderr)
+ sys.exit(1)
+
+ if not os.path.isfile(args.map):
+ print("The callmap file path does not exist.", file=sys.stderr)
+ sys.exit(1)
+
+ if not os.path.isfile(args.test):
+ print("The test file path does not exist.", file=sys.stderr)
+ sys.exit(1)
+
+ tagged_functions = get_tagged_functions_in_file(args.test)
+ if len(tagged_functions) == 0:
+ print("No target functions found in test file.")
+ sys.exit(1)
+ print("Target functions from UT: " + str(tagged_functions), file=sys.stderr)
+
+ cov_functions_deps = get_function_deps(args.map, tagged_functions)
+ print(
+ "Target functions and dependencies: " + str(cov_functions_deps), file=sys.stderr
+ )
+
+ if vars(args)["in"] and (".gz" in vars(args)["in"]):
+ covfile_handle = gzip.open(vars(args)["in"], "rb")
+ else:
+ covfile_handle = open(vars(args)["in"], "r")
+ covdata_out = filter_coverage_file(covfile_handle, cov_functions_deps)
+ covfile_handle.close()
+
+ filter_excluded_lines(covdata_out)
+
+ # default to lcov output format
+ out_format = "lcov"
+ if vars(args)["format"]:
+ out_format = args.format
+
+ if vars(args)["out"]:
+ if ".gz" in args.out:
+ outfile = gzip.open(args.out, "wb", encoding="ascii")
+ else:
+ outfile = open(args.out, "w")
+ else:
+ outfile = sys.stdout
+ if out_format == "json":
+ json.dump(covdata_out, outfile)
+ elif out_format == "lcov":
+ convert_to_lcov_info(args, covdata_out, outfile)
+
+ if outfile is not sys.stdout:
+ outfile.close()
+
+
+def get_tagged_functions_in_file(filename):
+ """Given a filename, return a set of the target function names tagged with
+ @coverage in that file."""
+ token_regex = r"^.*@coverage(( (\w+))+).*$"
+ token_pattern = re.compile(token_regex, re.IGNORECASE)
+
+ cov_functions = set()
+
+ line = ""
+ with open(filename, "r") as f:
+ while True:
+ line = f.readline()
+ if not line:
+ break
+ match = re.match(token_pattern, line)
+ if match:
+ loc = match.group(1).strip().split()
+ for i in loc:
+ cov_functions.add(i)
+ return cov_functions
+
+
+def get_function_deps(call_map_file, cov_functions):
+ """Return a set of functions and the functions called by those functions.
+ Given a set of function names, return a set containing those functions and
+ any dependent functions defined by the callmap."""
+ cov_functions_deps = set()
+ with open(call_map_file, "r") as callmap_f:
+ callmap = json.load(callmap_f)
+ for function in cov_functions:
+ # Check callgraph and add dependent functions in this project
+ if function in callmap:
+ cov_functions_deps.add(function)
+ for dep in callmap[function]:
+ cov_functions_deps.add(dep)
+ else:
+ print(
+ "WARN: function specified in unit test file ({}) was not "
+ "found in {}.\nCheck that the function name is correct and"
+ " that the specified coverage target is not a macro.".format(
+ function, call_map_file
+ ),
+ file=sys.stderr,
+ )
+ return cov_functions_deps
+
+
+def filter_coverage_file(covfile_handle, cov_functions):
+ """Given an input coverage json file and a set of functions that the test
+ is targeting, filter the coverage file to only include data generated
+ by running the given functions."""
+ covdata_out = dict()
+ covdata = json.load(covfile_handle)
+ # copy basic info
+ assert covdata["format_version"] == "1"
+ covdata_out["format_version"] = covdata["format_version"]
+ covdata_out["current_working_directory"] = covdata["current_working_directory"]
+ covdata_out["data_file"] = covdata["data_file"]
+ covdata_out["gcc_version"] = covdata["gcc_version"]
+ # handle per proj file data
+ covdata_out["files"] = list()
+ for targetfile in covdata["files"]:
+ cur_file = dict()
+ cur_file["file"] = targetfile["file"]
+ cur_functions = list()
+ for function in targetfile["functions"]:
+ if function["name"] in cov_functions:
+ cur_functions.append(function)
+ cur_file["functions"] = cur_functions
+ cur_lines = list()
+ for line in targetfile["lines"]:
+ if line["function_name"] in cov_functions:
+ cur_lines.append(line)
+ cur_file["lines"] = cur_lines
+ covdata_out["files"].append(cur_file)
+ return covdata_out
+
+
+def get_excluded_lines(c_file):
+ """Read a .c file and determine which lines should be excluded based on
+ LCOV_EXCL comments"""
+ excl_lines = set()
+ excl_br_lines = set()
+ excl_line_flag = False
+ excl_br_line_flag = False
+ with open(c_file, "r") as cfile:
+ line_number = 1
+ while True:
+ line = cfile.readline()
+ if not line:
+ break
+ if "LCOV_EXCL" in line:
+ if "LCOV_EXCL_LINE" in line:
+ excl_lines.add(line_number)
+ if "LCOV_EXCL_BR_LINE" in line:
+ excl_br_lines.add(line_number)
+ if "LCOV_EXCL_START" in line:
+ excl_line_flag = True
+ if "LCOV_EXCL_STOP" in line:
+ excl_line_flag = False
+ if "LCOV_EXCL_BR_START" in line:
+ excl_br_line_flag = True
+ if "LCOV_EXCL_BR_STOP" in line:
+ excl_br_line_flag = False
+ if excl_line_flag:
+ excl_lines.add(line_number)
+ if excl_br_line_flag:
+ excl_br_lines.add(line_number)
+ line_number += 1
+ return excl_lines, excl_br_lines
+
+
+def filter_excluded_lines(covdata):
+ """ Remove coverage data for lines excluded with LCOV_EXCL_* comments """
+ for target_file in covdata["files"]:
+ excl_lines, excl_br_lines = get_excluded_lines(target_file["file"])
+ target_lines_excl = list()
+ for target_line in target_file["lines"]:
+ if not ("line_number" in target_line and "count" in target_line):
+ continue
+ if int(target_line["line_number"]) in excl_br_lines:
+ target_line["branches"] = list()
+ if int(target_line["line_number"]) not in excl_lines:
+ target_lines_excl.append(target_line)
+ target_file["lines"] = target_lines_excl
+
+
+# Based on lcov's geninfo perl script
+def convert_to_lcov_info(args, covdata, outfile):
+ """ Convert a given gcov intermediate format json file to lcov .info format """
+ outfile.write("TN:{}\n".format(os.path.basename(args.test.replace(".c", ""))))
+ for target_file in covdata["files"]:
+ functions_found = 0
+ functions_hit = 0
+ outfile.write("SF:{}\n".format(target_file["file"]))
+
+ # Handle function data
+ for target_func in target_file["functions"]:
+ # validate stanza and skip
+ if not (
+ "name" in target_func
+ and "start_line" in target_func
+ and "execution_count" in target_func
+ ):
+ continue
+ outfile.write(
+ "FN:{},{}\n".format(target_func["start_line"], target_func["name"])
+ )
+
+ outfile.write(
+ "FNDA:{},{}\n".format(
+ target_func["execution_count"], target_func["name"]
+ )
+ )
+ functions_found += 1
+ if target_func["execution_count"] > 0:
+ functions_hit += 1
+ if functions_found > 0:
+ outfile.write("FNF:{}\n".format(functions_found))
+ outfile.write("FNH:{}\n".format(functions_hit))
+
+ lines_found = 0
+ lines_hit = 0
+ branches_found = 0
+ branches_hit = 0
+ # Handle line data
+ for target_line in target_file["lines"]:
+ if not ("line_number" in target_line and "count" in target_line):
+ continue
+ outfile.write(
+ "DA:{},{}\n".format(target_line["line_number"], target_line["count"])
+ )
+ lines_found += 1
+ if target_line["count"] > 0:
+ lines_hit += 1
+ # Branch number within each line
+ branch_number = 0
+ # Handle branch data
+ for target_branch in target_line["branches"]:
+ branch_count = "-"
+ if target_line["unexecuted_block"] or target_line["count"] == 0:
+ branch_count = "-"
+ elif "count" in target_branch:
+ branch_count = target_branch["count"]
+ # Note: block number is not needed.
+ outfile.write(
+ "BRDA:{},0,{},{}\n".format(
+ target_line["line_number"], branch_number, branch_count
+ )
+ )
+ branch_number += 1
+ branches_found += 1
+ if branch_count != "-":
+ branches_hit += 1
+ if branches_found > 0:
+ outfile.write("BRF:{}\n".format(branches_found))
+ outfile.write("BRH:{}\n".format(branches_hit))
+ outfile.write("LF:{}\n".format(lines_found))
+ outfile.write("LH:{}\n".format(lines_hit))
+ outfile.write("end_of_record\n")
+
+
+if __name__ == "__main__":
+ main()