Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 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
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
pio run -e native -t coverage
- 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/native/coverage_report/coverage.md ]; then
cat .pio/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 }}
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
34 changes: 22 additions & 12 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
build_src_filter =
+<./core>
+<./ports>
+<./adapters>
build_src_flags =
-std=gnu++17
-O0
--coverage
-fprofile-arcs
-ftest-coverage
; Linker flag for coverage
-g
--coverage
-Wall
-Wextra
-Werror
-Wpedantic
-Wshadow
test_lib_deps =
ArduinoFake@^0.4.0
extra_scripts = scripts/test-coverage.py
test_testing_command =
/usr/bin/env
python3
scripts/test-coverage.py
${platformio.build_dir}/${this.__env__}/program
test_ignore =
test_embedded
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
127 changes: 62 additions & 65 deletions scripts/test-coverage.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,65 @@
import os
import subprocess
from pathlib import Path
import sys
from typing import TYPE_CHECKING, Any

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."""
import subprocess
Import("env")

# Ensure the coverage flags are passed to the linker
env.Append(LINKFLAGS=["--coverage"])

def generate_coverage(*args, **kwargs):
print("Running tests to generate coverage data...")
# Run PlatformIO tests natively; exit(1) if any test fails
result = subprocess.run(["pio", "test", "-e", "native", "-vvv"])
if result.returncode != 0:
print("\n❌ Tests failed — aborting coverage generation.")
sys.exit(1)

print("\nGenerating coverage report...")
# macOS Clang uses llvm-cov, Windows/Linux use standard gcov
# gcov_tool = "llvm-cov gcov" if sys.platform == "darwin" else "gcov"
gcov_tool = "gcov"

output_dir = os.path.join(".pio", env["PIOENV"], "coverage_report")

# Create the output directory
os.makedirs(output_dir, exist_ok=True)

# gcovr command to parse data and generate an HTML report
cmd = [
"gcovr",
"--root", ".", # Ensure paths are evaluated relative to the project root
"--gcov-executable", gcov_tool,
"--html-details", os.path.join(output_dir, "index.html"),
"--markdown", os.path.join(output_dir, "coverage.md"),

# Explicitly INCLUDE your actual source code directories
# Add "--filter", r"lib/.*" or others if you have code there too
#"--filter", r".*/core/.*",
#"--filter", r".*/src/.*",

# Exclude the test code itself from the final metrics
"--exclude", r".pio/*",
"--exclude", r"unit_tests/*",

# Ignore Unity testing framework errors
"--gcov-ignore-errors=no_working_dir_found",
"--gcov-ignore-errors=source_not_found",
"--print-summary"
]

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()
subprocess.run(cmd, check=True)
print("\n✅ Coverage report successfully generated: coverage_report/index.html")
except FileNotFoundError:
print("\n❌ Error: 'gcovr' not found. Please install it using 'pip install gcovr'")
except subprocess.CalledProcessError:
print("\n❌ Error: Failed to generate coverage report.")

# Register the custom target in PlatformIO
env.AddCustomTarget(
name="coverage",
dependencies=None,
actions=[generate_coverage],
title="Coverage Report",
description="Run native tests and generate an HTML code coverage report"
)
Loading