BLE Log Compression Test Suite
Overview
This test suite validates the BLE log compression pipeline — from C source input through tree-sitter AST parsing, log ID assignment, compressed macro generation, to final log_index.h header output.
All tests use Python standard library unittest only. No third-party test frameworks required.
Directory Structure
tests/
├── README.md # This file
├── test_utils.py # Shared utilities: path setup, PipelineContext, helpers
├── test_format_parser.py # FormatToken NamedTuple and parse_format_string
├── test_arg_size_types.py # ARG_SIZE_TYPE determination (U32/U64/STR)
├── test_db_manager.py # LogDBManager state persistence and incremental logic
├── test_log_extraction.py # Tree-sitter AST log extraction
├── test_macro_generation.py # Bluedroid and Mesh module macro generators
├── test_pipeline_e2e.py # End-to-end .c -> .h with golden file comparison
├── test_incremental.py # Incremental compression scenarios
├── update_golden.py # Script to regenerate golden expected files
└── fixtures/
├── c_sources/ # Test input C files
│ ├── simple_logs.c # Basic: no-arg, single-arg, multi-arg, string
│ ├── format_specifiers.c # All format -> size type mappings
│ ├── special_tokens.c # __func__, __LINE__, MAC2STR, bt_hex
│ ├── concatenated_strings.c # PRIu64, PRId64 macro concatenation
│ ├── multi_level.c # ERROR/WARNING/API/DEBUG/EVENT/VERBOSE
│ ├── no_logs.c # Valid C with no matching log tags
│ └── mesh_logs.c # BLE Mesh module log patterns
└── expected/ # Golden output headers for comparison
├── simple_logs_index.h
├── format_specifiers_index.h
├── special_tokens_index.h
├── multi_level_index.h
└── multi_file_index.h
Running Tests
From the tests/ directory:
# Run all tests
python3 -m unittest discover -v
# Run a single test module
python3 -m unittest test_format_parser -v
# Run a single test class
python3 -m unittest test_arg_size_types.TestU64SizeType -v
# Run a single test method
python3 -m unittest test_db_manager.TestAddLog.test_sequential_ids -v
Test Modules
test_format_parser.py — Format String Parsing
Validates FormatToken(NamedTuple) and parse_format_string() in c_format_parse.py.
| Test Class | Coverage |
|---|---|
TestFormatTokenStructure |
NamedTuple field names, named vs index access, tuple subclass |
TestBasicSpecifiers |
18 conversion characters (%d, %s, %f, %p, ...), escaped %%, empty string |
TestPositionTracking |
FormatToken.start / .end positions in source string |
TestLengthModifiers |
h, hh, l, ll, z, j, t |
TestWidthAndPrecision |
Fixed width/precision, dynamic %*d / %.*s, both dynamic |
TestFlags |
-, +, , #, 0 |
TestMixedContent |
Interleaved text and specifiers, quoted format strings |
test_arg_size_types.py — Size Type Determination
Core regression test for the off-by-one bug fix. Verifies that FormatToken.conv_char and FormatToken.length (not the old incorrect indices) are used to determine ARG_SIZE_TYPE.
| Format | Expected Size Type | Field Checked |
|---|---|---|
%d, %u, %x, %X, %o |
U32 (0) |
token.conv_char |
%s |
STR (1) |
token.conv_char |
%f |
U64 (2) |
token.conv_char |
%lld, %llx |
U64 (2) |
token.length |
__func__ / __LINE__ |
skipped | token.conv_char == '@func' / '@line' |
MAC2STR(addr) |
buffer arg | token.conv_char.startswith('@hex_func') |
test_db_manager.py — Database State Management
Tests LogDBManager in LogDBManager.py.
| Test Class | Coverage |
|---|---|
TestAddLog |
SUCCESS / LOG_EXISTS return codes, sequential IDs, unique key fields |
TestPersistence |
save_all() + reload preserves data |
TestFileProcessing |
is_file_processed: new file, unchanged, modified |
TestConfigUpdate |
is_config_updated: fresh DB, same config after reload, changed config |
TestSourceUpdateState |
SOURCE_LOG_UPDATE_FULL / PARTIAL / NONE transitions |
test_log_extraction.py — AST Log Extraction
Tests LogCompressor.extract_log_calls() using tree-sitter.
| Test Class | Coverage |
|---|---|
TestExtractNoLogs |
No matching tags, no functions |
TestExtractBasicLogs |
Single log, no-arg log, multiple tags |
TestExtractFormatTokens |
Token population, hexify=True, dynamic width/precision filtered |
TestExtractSpecialTokens |
__func__ -> @func, __LINE__ -> @line, MAC2STR -> @hex_func |
TestExtractCallerInfo |
Correct enclosing function identification |
TestExtractArgMismatch |
Format/argument count mismatch raises SyntaxError |
test_macro_generation.py — Module-Specific Macro Generators
Tests make_bluedroid_log_macro.py and make_mesh_log_macro.py directly.
| Test Class | Coverage |
|---|---|
TestBluedroidMacroGen |
gen_header_head, zero/single/multi-arg macros, level checks (appl_trace_level >= BT_TRACE_LEVEL_*), buffer args |
TestMeshMacroGen |
Header declarations, BT_ERR/BT_DBG level checks |
test_pipeline_e2e.py — End-to-End Pipeline
Full .c -> .h pipeline tests with golden file comparison.
| Test Class | Coverage |
|---|---|
TestSimpleLogsE2E |
Header generation, tag replacement in source, header structure, sorted IDs, preserve tags |
TestFormatSpecifiersE2E |
All size types (U32/STR/U64) present in output |
TestSpecialTokensE2E |
ble_log_compressed_hex_print_buf generated, __func__ excluded |
TestNoLogsE2E |
Zero macros for file without matching tags |
TestMultiLevelE2E |
Unique IDs per level, different level check prefixes |
TestMultiFileE2E |
Globally unique IDs across multiple files |
TestConcatenatedStringsE2E |
PRIu64 produces U64 size type |
TestGoldenFileComparison |
Exact output comparison against fixtures/expected/*.h |
test_incremental.py — Incremental Compression
Tests caching, re-runs, and config change behavior.
| Test Class | Coverage |
|---|---|
TestFirstRun |
All files compressed, header generated |
TestRerunUnchanged |
File hashes tracked, log IDs stable on re-add |
TestRerunModifiedFile |
Modified file reprocessed, existing IDs stable |
TestAddNewFile |
New file IDs continue from max_id + 1 |
TestConfigChange |
Config change triggers SOURCE_LOG_UPDATE_FULL |
Golden File Workflow
The fixtures/expected/ directory contains known-good header outputs. End-to-end tests compare generated output against these files (with copyright year normalized).
When the compression logic changes intentionally:
# Regenerate all golden files
python3 update_golden.py
# Review the diff
git diff fixtures/expected/
# Commit after verification
Fixture C Source Files
These files are parsed by tree-sitter only — they do not need to compile. Clang warnings about implicit declarations and unknown types are expected and irrelevant.
| File | Purpose |
|---|---|
simple_logs.c |
4 functions: no-arg, single %d, triple %d, %s |
format_specifiers.c |
All ARG_SIZE_TYPE mappings: %d/%u/%x/%X/%o (U32), %f (U64), %lld/%llx (U64), %s (STR), mixed |
special_tokens.c |
__func__, __FUNCTION__, __LINE__, MAC2STR, mixed special + normal args |
concatenated_strings.c |
PRIu64, PRId64, PRIx64 macro concatenation |
multi_level.c |
ERROR, WARNING, API, DEBUG, EVENT, VERBOSE in one function |
no_logs.c |
Only printf, no matching log tags |
mesh_logs.c |
BLE Mesh tags: BT_ERR, BT_WARN, BT_INFO, BT_DBG |
Dependencies
- Python 3.8+
tree-sitter+tree-sitter-c(same versions as inscripts/requirements.txt)pyyaml- No additional test frameworks required (
unittestis in the Python standard library)