Skip to content

Commit 111e22d

Browse files
add UT + coverage
1 parent 26387a4 commit 111e22d

8 files changed

Lines changed: 325 additions & 25 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ dkms.conf
5555
*.dwo
5656

5757
# Build directories
58+
build/
5859
obj/
5960
bin/
6061
_codeql_detected_source_root

Makefile

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@ LDFLAGS =
88
SRC_DIR = src
99
INC_DIR = include
1010
EXAMPLES_DIR = examples
11-
OBJ_DIR = obj
12-
BIN_DIR = bin
11+
TEST_DIR = tests
12+
BUILD_DIR = build
13+
OBJ_DIR = $(BUILD_DIR)/obj
14+
BIN_DIR = $(BUILD_DIR)/bin
1315

1416
SRCS = $(wildcard $(SRC_DIR)/*.c)
1517
OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS))
1618

1719
EXAMPLES = $(wildcard $(EXAMPLES_DIR)/*.c)
1820
EXAMPLE_BINS = $(patsubst $(EXAMPLES_DIR)/%.c,$(BIN_DIR)/%,$(EXAMPLES))
21+
TEST_SRC = $(TEST_DIR)/unit_tests.c
22+
TEST_BIN = $(BIN_DIR)/unit_tests
1923

20-
LIB = libsdlp.a
24+
LIB = $(BUILD_DIR)/libsdlp.a
2125

22-
.PHONY: all clean examples lib
26+
.PHONY: all clean examples lib unit-tests test coverage-html
2327

2428
all: lib examples
2529

@@ -33,22 +37,30 @@ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
3337

3438
examples: $(EXAMPLE_BINS)
3539

40+
unit-tests: $(TEST_BIN)
41+
3642
$(BIN_DIR)/%: $(EXAMPLES_DIR)/%.c $(LIB) | $(BIN_DIR)
3743
$(CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS)
3844

39-
$(OBJ_DIR):
45+
$(TEST_BIN): $(TEST_SRC) $(LIB) | $(BIN_DIR)
46+
$(CC) $(CFLAGS) $< $(LIB) -o $@ $(LDFLAGS)
47+
48+
$(BUILD_DIR):
49+
mkdir -p $(BUILD_DIR)
50+
51+
$(OBJ_DIR): | $(BUILD_DIR)
4052
mkdir -p $(OBJ_DIR)
4153

42-
$(BIN_DIR):
54+
55+
$(BIN_DIR): | $(BUILD_DIR)
4356
mkdir -p $(BIN_DIR)
4457

4558
clean:
46-
rm -rf $(OBJ_DIR) $(BIN_DIR) $(LIB)
47-
48-
.PHONY: test
49-
test: examples
50-
@echo "Running TM example..."
51-
@./$(BIN_DIR)/tm_example
52-
@echo ""
53-
@echo "Running TC example..."
54-
@./$(BIN_DIR)/tc_example
59+
rm -rf $(BUILD_DIR) obj bin libsdlp.a
60+
61+
coverage-html:
62+
bash scripts/coverage_html.sh
63+
64+
test: unit-tests
65+
@echo "Running unit tests..."
66+
@./$(TEST_BIN)

README.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,15 @@ make all
5656
```
5757

5858
This will create:
59-
- `libsdlp.a` - Static library
60-
- `bin/tm_example` - TM frame example
61-
- `bin/tc_example` - TC frame example
59+
- `build/libsdlp.a` - Static library
60+
- `build/bin/tm_example` - TM frame example
61+
- `build/bin/tc_example` - TC frame example
6262

6363
### Build Library Only
6464

6565
```bash
6666
make lib
67-
# Produces: libsdlp.a (static)
67+
# Produces: build/libsdlp.a (static)
6868
```
6969

7070
### Build Examples
@@ -79,6 +79,26 @@ make examples
7979
make test
8080
```
8181

82+
### Coverage (HTML)
83+
84+
Requires `gcovr` installed in your system:
85+
86+
```bash
87+
sudo apt install gcovr
88+
```
89+
90+
Generate coverage report:
91+
92+
```bash
93+
make coverage-html
94+
```
95+
96+
Output report:
97+
98+
```text
99+
build/coverage/index.html
100+
```
101+
82102
### Clean
83103

84104
```bash

include/sdlp_tc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ typedef struct {
3838
uint16_t control_command_flag : 1;
3939
uint16_t reserved : 2;
4040
uint16_t spacecraft_id : 10;
41-
uint8_t virtual_channel_id : 6;
41+
uint16_t virtual_channel_id : 6;
4242
uint16_t frame_length : 10;
4343
uint8_t frame_sequence_number;
4444
} sdlp_tc_header_t;

scripts/coverage_html.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5+
OUT_FILE="${1:-${ROOT_DIR}/build/coverage/index.html}"
6+
7+
if [[ "${OUT_FILE}" != /* ]]; then
8+
OUT_FILE="${ROOT_DIR}/${OUT_FILE}"
9+
fi
10+
11+
COVERAGE_CFLAGS='-O0 -g --coverage -Iinclude -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Wcast-align -Wcast-qual -Wpointer-arith -Wformat=2 -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wundef -std=c11'
12+
13+
cd "${ROOT_DIR}"
14+
15+
if ! command -v gcovr >/dev/null 2>&1; then
16+
echo "Error: gcovr is not installed."
17+
echo "Install with: pip install gcovr"
18+
exit 1
19+
fi
20+
21+
make clean >/dev/null
22+
make unit-tests CFLAGS="${COVERAGE_CFLAGS}" >/dev/null
23+
./build/bin/unit_tests >/dev/null
24+
25+
mkdir -p "$(dirname "${OUT_FILE}")"
26+
gcovr -r "${ROOT_DIR}" \
27+
--filter "${ROOT_DIR}/src" \
28+
--html \
29+
--html-details \
30+
--output "${OUT_FILE}"
31+
32+
echo "Coverage HTML report written to: ${OUT_FILE}"

src/sdlp_tc.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ int sdlp_tc_encode_frame(const sdlp_tc_frame_t *frame, uint8_t *buffer,
5252
((frame->header.reserved & 0x03u) << 2) |
5353
((frame->header.spacecraft_id >> 8) & 0x03u));
5454
buffer[offset++] = (uint8_t)(frame->header.spacecraft_id & 0xffu);
55-
buffer[offset++] = (uint8_t)(((frame->header.virtual_channel_id & 0x3fu) << 2) | 0x00u);
56-
buffer[offset++] = (uint8_t)((frame->header.frame_length >> 8) & 0xffu);
55+
buffer[offset++] = (uint8_t)(((frame->header.virtual_channel_id & 0x3fu) << 2) |
56+
((frame->header.frame_length >> 8) & 0x03u));
5757
buffer[offset++] = (uint8_t)(frame->header.frame_length & 0xffu);
58+
buffer[offset++] = frame->header.frame_sequence_number;
5859

5960
#ifdef TC_SEGMENT_HEADER_ENABLED
6061
if (!frame->header.control_command_flag) {
@@ -93,10 +94,10 @@ int sdlp_tc_decode_frame(const uint8_t *buffer, size_t buffer_size,
9394
offset += 2;
9495

9596
frame->header.virtual_channel_id = (uint8_t)((buffer[offset] >> 2) & 0x3fu);
96-
offset++;
97-
98-
frame->header.frame_length = (uint16_t)(((uint16_t)buffer[offset] << 8) | buffer[offset + 1]);
97+
frame->header.frame_length = (uint16_t)((((uint16_t)buffer[offset] & 0x03u) << 8) |
98+
(uint16_t)buffer[offset + 1]);
9999
offset += 2;
100+
frame->header.frame_sequence_number = buffer[offset++];
100101

101102
#ifdef TC_SEGMENT_HEADER_ENABLED
102103
if (!frame->header.control_command_flag) {

tests/cunit.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* Tiny C unit test helpers. Suitable for embedding in small projects.
2+
* Usage: include this header in a single C test file and implement test
3+
* functions returning 0 on success, non-zero on failure. Use RUN_TEST(fn).
4+
*/
5+
#ifndef CUNIT_H
6+
#define CUNIT_H
7+
8+
#include <stdio.h>
9+
#include <string.h>
10+
11+
static int cunit_overall_failures = 0;
12+
13+
#define ASSERT_EQ_INT(expected, actual) \
14+
do { \
15+
if ((int)(expected) != (int)(actual)) { \
16+
printf("ASSERT_EQ_INT failed: %s:%d: expected %d got %d\n", __FILE__, \
17+
__LINE__, (int)(expected), (int)(actual)); \
18+
return 1; \
19+
} \
20+
} while (0)
21+
22+
#define ASSERT_EQ_MEM(a, b, len) \
23+
do { \
24+
if (memcmp((a), (b), (len)) != 0) { \
25+
printf("ASSERT_EQ_MEM failed: %s:%d\n", __FILE__, __LINE__); \
26+
return 1; \
27+
} \
28+
} while (0)
29+
30+
#define ASSERT_TRUE(cond) \
31+
do { \
32+
if (!(cond)) { \
33+
printf("ASSERT_TRUE failed: %s:%d\n", __FILE__, __LINE__); \
34+
return 1; \
35+
} \
36+
} while (0)
37+
38+
#define RUN_TEST(fn) \
39+
do { \
40+
printf("RUN %s\n", #fn); \
41+
int r = fn(); \
42+
if (r == 0) \
43+
printf("PASS %s\n", #fn); \
44+
else { \
45+
printf("FAIL %s\n", #fn); \
46+
cunit_overall_failures++; \
47+
} \
48+
} while (0)
49+
50+
#endif /* CUNIT_H */

0 commit comments

Comments
 (0)