Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
57605dc
docs: add README files for core, hal, ports, app, and adapters direct…
andre-stefanov May 15, 2026
cf47b7f
docs: enhance README files with illustrative code snippets and clarif…
andre-stefanov May 15, 2026
d58d43b
Add MeadeParser header and unit tests for command parsing
andre-stefanov May 16, 2026
e4b02e8
Refactor MeadeParser: Introduce meade namespace and add unit tests
andre-stefanov May 16, 2026
5975b9b
test: Enhance coverage report generation: Add markdown output option
andre-stefanov May 16, 2026
48ca079
fix: Refactor code structure for improved readability and maintainabi…
andre-stefanov May 16, 2026
169f3c9
Add MeadeResponse API and unit tests for response shapes and bindings
andre-stefanov May 16, 2026
f3994f9
refactor: Replace std::string with fixed-capacity buffer in MeadePars…
andre-stefanov May 16, 2026
72de49a
refactor: Meade response handling to unify command kind bindings
andre-stefanov May 16, 2026
8594335
refactor: Meade command handling and testing
andre-stefanov May 18, 2026
409a84b
refactor: Meade Set Command Handling
andre-stefanov May 18, 2026
d82c55a
refactor: Implement Meade Quit command handling and dispatcher
andre-stefanov May 18, 2026
05ed3a3
refactor: Add Meade Distance command handling and testing
andre-stefanov May 18, 2026
5bf2101
refactor: Add unit tests for Meade command handlers
andre-stefanov May 18, 2026
d9b2d68
refactor: Update MeadeResponse class with additional methods for payl…
andre-stefanov May 18, 2026
202473a
Refactor unit tests to remove unnecessary inclusion of MeadeResponse.hpp
andre-stefanov May 18, 2026
bb1eb00
refactor: moved meade tests to a single target
andre-stefanov May 18, 2026
1fc6436
refactor: remove legacy per-family tables from MeadeParser
andre-stefanov May 18, 2026
a409ad2
refactor: streamline response-building functions in MeadeParser
andre-stefanov May 18, 2026
10f6f9e
refactor: simplify command processing by replacing handler table with…
andre-stefanov May 18, 2026
8db91d8
refactor: moved meade protocol documentation to an extra file
andre-stefanov May 18, 2026
063ccf4
refactor: unify Meade command handling by introducing dispatch functi…
andre-stefanov May 18, 2026
15ef48d
refactor: introduce Cursor class for single-pass input parsing and si…
andre-stefanov May 19, 2026
f470997
refactor: remove unused isDecimalDigit function and enhance Cursor us…
andre-stefanov May 19, 2026
3d2a2a2
refactor: split MeadeParser into multiple modules (helpers and family…
andre-stefanov May 19, 2026
3de0778
style: apply clang-format fixes
openastrotech-bot May 19, 2026
f2e1450
fix: uncomment build_cache_dir in platformio.ini
andre-stefanov May 19, 2026
a362dfe
refactor: moved meade files in a subfolder
andre-stefanov May 19, 2026
425ba7a
docs: updated plan.md
andre-stefanov May 19, 2026
33b18b8
ci: enhance unit test coverage reporting and update build cache direc…
andre-stefanov May 19, 2026
eaee7bb
fix: handle test failures in coverage generation script
andre-stefanov May 19, 2026
3509c59
fix: remove unnecessary warning suppressions in platformio.ini
andre-stefanov May 19, 2026
d08c832
fix: suppress additional compiler warnings in platformio.ini
andre-stefanov May 19, 2026
9abdfaf
Refactor code structure for improved readability and maintainability
andre-stefanov May 19, 2026
82de091
fix: remove unnecessary warning suppressions in platformio.ini
andre-stefanov May 19, 2026
daf5e20
fix: update build cache directory path in CI workflow
andre-stefanov May 19, 2026
b1b016f
fix: handle missing cache directory in copy_caches_to_executors function
andre-stefanov May 19, 2026
bca3410
fix: update paths for MEADE_CPP and VERSION_FILE in MeadeCommandParser
andre-stefanov May 19, 2026
bddc79e
docs: update plan.md to reflect changes in testing framework and envi…
andre-stefanov May 19, 2026
d343a05
Refactor Meade command handlers to use existing MeadeResponse
andre-stefanov May 25, 2026
67d0485
style: apply clang-format fixes
openastrotech-bot May 25, 2026
43d8e46
Refactor unit tests to use Google Test framework
andre-stefanov May 22, 2026
15b4a9d
refactor: moved core tests into a new folder for better structure
andre-stefanov May 22, 2026
0cb83e6
refactor: update test coverage script and restructure test_main for c…
andre-stefanov May 23, 2026
8a66317
test: add test coverage script and update test_main for coverage repo…
andre-stefanov May 23, 2026
066d417
docs: move plan to repository root
andre-stefanov May 25, 2026
cc1c7a1
test: fixed tests after rebase
andre-stefanov May 25, 2026
eb154fa
fix: update CI workflow to use the correct coverage report path and s…
andre-stefanov May 25, 2026
0ee2969
refactor: improve float writing logic for precision handling in Meade…
andre-stefanov May 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- '**'
push:
branches:
- '**'
- 'develop'

jobs:
unit-tests:
Expand All @@ -17,22 +17,26 @@ jobs:
- uses: ./.github/actions/setup-pio
with:
install-unit-test-deps: 'true'
- name: Run unit tests
run: pio test -e native -v
- name: Run unit tests with coverage
if: always()
run: |
pip install gcovr
./scripts/test-coverage.sh
- name: Publish Coverage Summary
if: always()
run: |
echo "## Native Unit Test Coverage" >> "$GITHUB_STEP_SUMMARY"
echo >> "$GITHUB_STEP_SUMMARY"
if [ -f .pio/coverage.md ]; then
cat .pio/coverage.md >> "$GITHUB_STEP_SUMMARY"
if [ -f .pio/build/native/coverage_report/coverage.md ]; then
cat .pio/build/native/coverage_report/coverage.md >> "$GITHUB_STEP_SUMMARY"
else
echo "Coverage report was not generated." >> "$GITHUB_STEP_SUMMARY"
fi

build:
name: Build (${{ matrix.board }})
runs-on: ubuntu-latest
needs: unit-tests
strategy:
fail-fast: false
matrix:
Expand All @@ -47,5 +51,7 @@ jobs:
- uses: ./.github/actions/setup-pio
with:
install-matrix-deps: 'true'
env:
PLATFORMIO_BUILD_CACHE_DIR: ${{ github.workspace }}/build_cache
- name: Build ${{ matrix.board }}
run: python matrix_build.py -b ${{ matrix.board }}
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ pio run -e ramps -t upload
# Run unit tests (native platform)
pio test -e native

# Run unit tests with coverage report (requires gcovr: pip install gcovr)
./scripts/test-coverage.sh # defaults to -e native
./scripts/test-coverage.sh -e native
# Report: .pio/build/native/coverage_report/index.html

# Run matrix build (tests many configuration combinations across boards)
python matrix_build.py -b ramps # single board
python matrix_build.py # all boards
Expand Down
130 changes: 89 additions & 41 deletions specs/plan.md → PLAN.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions matrix_build_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ def copy_caches_to_executors(src_proj_dir: Path, dst_executors: List[Executor]):
dir_names_to_copy = ['.pio', 'build_cache']
for dir_name_to_copy in dir_names_to_copy:
src_path = Path(src_proj_dir, dir_name_to_copy)
# If the cache dir isn't inside the temp proj dir (e.g. when
# PLATFORMIO_BUILD_CACHE_DIR points at the repo root), try the repo root
if not src_path.exists():
src_path = Path('.', dir_name_to_copy)
if not src_path.exists():
continue
for dst_executor in dst_executors:
dst_path = Path(dst_executor.proj_dir, dir_name_to_copy)
shutil.copytree(src_path, dst_path)
Expand Down
36 changes: 23 additions & 13 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ include_dir = .
src_dir = ./src
lib_dir = ./src/libs
test_dir = ./unit_tests
build_cache_dir = ./build_cache

[common]
lib_deps =
Expand Down Expand Up @@ -123,31 +122,42 @@ platform = espressif32
board = esp32dev
upload_speed = 460800
monitor_filters = esp32_exception_decoder
build_unflags = -std=gnu++11
build_flags =
${env.build_flags}
-std=gnu++17
-D BOARD=BOARD_ESP32_ESP32DEV
lib_deps =
${common.lib_deps}
WiFi

[env:oaeboardv1]
extends = env:esp32
build_unflags = -std=gnu++11
build_flags =
${env.build_flags}
-std=gnu++17
-D BOARD=BOARD_OAE_V1 -D ESP32BOARD

[env:native]
platform = native
test_ignore = test_embedded
build_flags =
test_build_src = true
test_framework = googletest
build_src_filter =
+<./core>
+<./ports>
+<./adapters>
build_src_flags =
-std=gnu++17
-O0
--coverage
-fprofile-arcs
-ftest-coverage
; Linker flag for coverage
extra_scripts = scripts/test-coverage.py
test_testing_command =
/usr/bin/env
python3
scripts/test-coverage.py
${platformio.build_dir}/${this.__env__}/program
-g
--coverage
-Wall
-Wextra
-Werror
-Wpedantic
-Wshadow
test_lib_deps =
ArduinoFake@^0.4.0
extra_scripts = pre:scripts/test-coverage.py
test_filter = test_core
4 changes: 2 additions & 2 deletions scripts/MeadeCommandParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import os
import re

MEADE_CPP = "..\\src\\MeadeCommandProcessor.cpp"
VERSION_FILE = "..\\Version.h"
MEADE_CPP = os.path.join("..", "src", "core", "meade", "MeadeProtocol.hpp")
VERSION_FILE = os.path.join("..", "Version.h")
MODULE_PATH = os.path.dirname(os.path.realpath(__file__))
START_LINE = 0
END_LINE = 0
Expand Down
70 changes: 3 additions & 67 deletions scripts/test-coverage.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,4 @@
import os
import subprocess
from pathlib import Path
import sys
from typing import TYPE_CHECKING, Any
Import("env")

if TYPE_CHECKING:
def Import(*names: str) -> tuple[Any, ...]:
...

def _project_root():
return Path(__file__).resolve().parent.parent


def _native_build_dir():
return _project_root() / ".pio" / "build" / "native"


def _has_coverage_data():
return any(_native_build_dir().rglob("*.gcda"))


def ensure_gcovr_installed(build_env):
"""Checks if gcovr is installed, and installs it via pip if not."""
try:
import gcovr
except ImportError:
print("gcovr not found! Installing it into the PlatformIO environment...")
# $PYTHONEXE ensures we use PlatformIO's isolated Python environment, not the system OS Python
build_env.Execute("$PYTHONEXE -m pip install gcovr")


def generateCoverageInfo():
if not _has_coverage_data():
print("Skipping coverage report generation because no .gcda files were produced.")
return

print("Generating code coverage report...")
gcovr_cmd = ["gcovr"]
report_dir = _project_root()
# Adjust this path if you are testing multiple specific folders
subprocess.run(gcovr_cmd + ["--html-details", ".pio/coverage.html", "--filter", "src/"], check=True, cwd=report_dir)
print(f"Coverage report generated at: .pio/coverage.html")
subprocess.run(gcovr_cmd + ["--markdown", ".pio/coverage.md", "--filter", "src/"], check=True, cwd=report_dir)
print(f"Coverage report generated at: .pio/coverage.md")


def configure_build():
Import("env")
build_env = globals()["env"]
build_env.Append(LINKFLAGS=["--coverage"])
ensure_gcovr_installed(build_env)


def main(argv):
if not argv:
print("Usage: test-coverage.py <test-program> [args...]", file=sys.stderr)
return 2

completed = subprocess.run(argv, cwd=_project_root(), env=os.environ.copy(), check=False)
generateCoverageInfo()
return completed.returncode


if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))

configure_build()
# Ensure coverage flags reach the linker so .gcda files are produced.
env.Append(LINKFLAGS=["--coverage"])
75 changes: 75 additions & 0 deletions scripts/test-coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# Run pio test and auto-generate coverage report.
# Usage: ./scripts/test-coverage.sh [-e <env>] [extra pio test args...]
#
# This wrapper is needed because pio test spawns the test binary as a
# subprocess — coverage data (.gcda files) is only flushed when that
# subprocess exits, so we can't generate the report from inside the
# test binary or from PlatformIO's pre-scripts.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$PROJECT_DIR"

# Extract environment name from args (default: native)
ENV="native"
NEXT_IS_ENV=0
for arg in "$@"; do
if [ "$NEXT_IS_ENV" -eq 1 ]; then
ENV="$arg"
break
fi
if [ "$arg" = "-e" ]; then
NEXT_IS_ENV=1
fi
done

# Run tests (default to -e native if no args)
if [ $# -gt 0 ]; then
pio test "$@" 2>&1
else
pio test -e native 2>&1
fi
TEST_EXIT=$?

if [ $TEST_EXIT -ne 0 ]; then
echo ""
echo "⚠️ Tests failed — skipping coverage report."
exit $TEST_EXIT
fi

# Determine gcov executable
if [ "$(uname -s)" = "Darwin" ]; then
GCOV_TOOL="llvm-cov gcov"
else
GCOV_TOOL="gcov"
fi

# Check if gcovr is available
if ! command -v gcovr &>/dev/null; then
echo ""
echo "⚠️ gcovr not found — skipping coverage report (pip install gcovr)"
exit 0
fi

# Generate coverage report
OUTPUT_DIR=".pio/build/$ENV/coverage_report"
mkdir -p "$OUTPUT_DIR"

echo ""
echo "Generating coverage report..."
gcovr \
--root . \
--gcov-executable "$GCOV_TOOL" \
--html-details "$OUTPUT_DIR/index.html" \
--markdown "$OUTPUT_DIR/coverage.md" \
--exclude '.pio/*' \
--exclude 'unit_tests/*' \
--gcov-ignore-errors=no_working_dir_found \
--gcov-ignore-errors=source_not_found \
--print-summary

echo ""
echo "✅ Coverage report: $OUTPUT_DIR/index.html"
Loading