Skip to content

Feat:headless mode daemon with telemetry output and command listening#249

Merged
sudip-mondal-2002 merged 18 commits into
sudip-mondal-2002:mainfrom
kal-sudo:feat/headless-mode
May 28, 2026
Merged

Feat:headless mode daemon with telemetry output and command listening#249
sudip-mondal-2002 merged 18 commits into
sudip-mondal-2002:mainfrom
kal-sudo:feat/headless-mode

Conversation

@kal-sudo

@kal-sudo kal-sudo commented May 27, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Implements a pure headless daemon mode for Amplitron, allowing it to run as a dedicated DSP rig on resource-constrained hardware (like a Linux server or Raspberry Pi) without a display server along with integration tests for the same.

Related Issue

Fixes #143

Type of Change

-[ ] 🐛 Bug fix
-[x] ✨ New feature
-[ ] ♻️ Refactor / code cleanup
-[ ] 📝 Documentation
-[x] ✅ Tests
-[x] 🔧 Build / CI / Configuration

How Was This Tested?

Platform(s) tested on: Linux (Fedora)
Test command run: make build followed by ./build/amplitron-tests (All 611 tests passing)
Manual test steps :
1. Booted with --headless without a preset to verify the failsafe exit.
2. Booted with a valid dummy preset and verified the audio engine instantiated correctly.
3. Sent live chain commands via stdin to verify the background thread communicated with the audio thread safely.
4. Used Ctrl+C to verify the SIGINT signal successfully exited the loop and safely tore down the hardware.
5. Tested telemetry toggling during live execution.

Checklist

-[x] Code compiles and builds successfully on my platform
-[x] All existing tests pass (./amplitron-tests)
-[x] New tests added for new functionality
-[ x] Documentation updated (README, code comments)
-[x] No blocking calls on the audio thread (no std::mutex::lock() on the hot path)
-[x] Tested on: Linux (Fedora)

Screenshots / Demo

manual_tests.md

Summary by CodeRabbit

  • New Features

    • Enhanced headless mode: CLI-driven startup, stdin command queue, device auto-routing, and periodic telemetry.
  • Behavior Changes

    • CLI now returns structured result with early-exit reporting; help/version exit paths and validation (headless requires a preset) occur before full startup; GUI and session restore skipped in headless.
  • Tests

    • New integration tests for headless flows; updated CLI unit tests to match new CLI behavior.
  • Serialization

    • Effect enabled state is now persisted in presets/UI.

Review Change Stack

@github-actions github-actions Bot added High Impact Significant user-facing improvement level:advanced Advanced task · 45 GSSoC points platform: linux Linux-specific issue or feature type:feature New feature or request labels May 27, 2026
@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a CliOptions contract, enforces headless preset validation, implements headless startup/runtime (no GUI, preset load, optional device auto-routing, stdin command thread, telemetry), makes Effect::enabled atomic and serialized, updates CLI tests, adds headless integration tests, and wires the test into CMake.

Changes

Headless Mode Feature

Layer / File(s) Summary
CLI Options Contract & Parsing
src/cli.h, tests/unit/test_cli.cpp
Introduce CliOptions return type and update handle_cli_args to populate exit_early, is_headless, preset_path, input_device, output_device, and exit_reason; update unit tests to read options.exit_early.
Main startup, includes & CLI handling
src/main.cpp
Add conditional GUI include guards, threading/timing/session includes, detach std::cin, parse CliOptions early, enforce headless preset requirement, and return on exit_early before engine initialization.
Headless runtime loop & device auto-routing
src/main.cpp
Branch runtime for headless: load preset with GUI=nullptr, optionally auto-route I/O by substring matches (warn on ambiguous/missing), start audio engine, spawn detached stdin listener thread to enqueue commands, drain/apply commands on the main thread, emit telemetry periodically, and perform headless-aware shutdown.
Effect enabled → atomic and JSON serialization
src/audio/effects/effect.h
Add <atomic>, change enabled_ to std::atomic<bool>, and read/write "enabled" via load()/store() in get_params()/set_params().
Integration tests, unit updates & CMake wiring
tests/integration/test_headless.cpp, tests/unit/test_cli.cpp, CMakeLists.txt
Add integration tests (offline DSP gain staging, command queue execution, CLI parsing success/failure), update CLI unit tests for the new return type, and add the new integration test source to TEST_SOURCES.
sequenceDiagram
  participant User
  participant CLI as handle_cli_args
  participant Main as main.cpp
  participant Audio as AudioEngine
  participant Stdin as StdinListener

  User->>CLI: amplitron --headless --preset live.json
  CLI->>Main: return CliOptions (is_headless,preset_path,input_device,output_device,exit_reason)
  Main->>Main: if exit_early -> print exit_reason & return
  alt is_headless
    Main->>Audio: load preset (GUI=nullptr)
    Main->>Audio: auto-select devices (substring match)
    Main->>Audio: start audio stream
    Main->>Stdin: spawn detached stdin command thread
    Stdin->>Audio: enqueue commands (gain,bypass,enable,chain)
    Main->>Main: print telemetry periodically (if enabled)
  else
    Main->>Main: init GUI and restore session
  end
  Main->>Audio: shutdown engine
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • sudip-mondal-2002/Amplitron#99: Related preset serialization changes for effect "enabled" that align with the atomic-enabled serialization in this PR.

Suggested labels

Audio Processing, type:testing, Quality-of-Life, quality:clean

Suggested reviewers

  • sudip-mondal-2002
  • MohitBareja16

Poem

🐰 Whiskers twitch for code that runs headless,

No GUI fuss, just DSP relentless.
CliOptions parsed, presets set right,
Threads hum softly through the night,
Goodbye printed — the rabbit hops light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature being added: headless mode with telemetry and command listening, which matches the primary changes across multiple files.
Linked Issues check ✅ Passed The PR implements all core objectives from #143: headless flag (--headless/--no-gui), preset requirement at startup, telemetry output (device names, buffer size, latency, CPU load), stdin command protocol, and SIGINT clean shutdown support.
Out of Scope Changes check ✅ Passed All changes directly support headless mode implementation. The Effect.h atomic flag change enables the enable/disable command feature, which is part of the stdin control protocol requirement.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Report 📊

Line Coverage: 80.0%

✅ Coverage meets threshold: 80.0% >= 60%

Full Coverage Summary
Summary coverage rate:
  lines......: 80.0% (6728 of 8415 lines)
  functions..: 91.8% (743 of 809 functions)
  branches...: no data found

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

PR Preview Removed

The GitHub Pages preview for this PR has been removed because the PR was closed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/integration/test_headless.cpp (1)

12-13: ⚡ Quick win

Assert engine.initialize() succeeds before exercising behavior.

Both tests continue even if engine init fails, which can hide root-cause failures behind downstream assertions.

Suggested patch
 TEST(headless_offline_dsp_gain_staging) {
     AudioEngine engine;
-    engine.initialize();
+    ASSERT_TRUE(engine.initialize());
@@
 TEST(headless_command_queue_execution) {
     AudioEngine engine;
-    engine.initialize();
+    ASSERT_TRUE(engine.initialize());

Also applies to: 40-41

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/test_headless.cpp` around lines 12 - 13, Add an assertion
that engine.initialize() succeeded immediately after calling it (e.g., use
ASSERT_TRUE/EXPECT_TRUE or ASSERT_NO_THROW as appropriate for your test
framework) so the test stops if initialization fails; update both occurrences of
engine.initialize() (the one at the top and the one around lines 40–41) to
assert success before proceeding to exercise other behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cli.h`:
- Around line 39-47: The flag handlers for "--preset", "--input", and "--output"
currently skip when a value is missing; update the parsing logic in the CLI loop
that assigns options.preset_path, options.input_device, and
options.output_device so that if i+1 >= argc you set exit_early = true and emit
a clear error (e.g., "missing value for --preset/--input/--output") instead of
silently ignoring the flag; keep the existing assignment path (argv[++i]) when a
value exists but add an explicit else branch for each flag to fail early.

In `@src/main.cpp`:
- Line 229: The GuiManager is always being constructed (Amplitron::GuiManager
gui(engine)), including in headless runs; move its construction into the GUI
branch so it is only instantiated when not headless. Update the code paths
around the headless check (the GUI branch where GUI setup and event loop run) to
create Amplitron::GuiManager gui(engine) there and remove the global/early
construction; ensure any code that needs gui is also inside or guarded by the
same non-headless branch (or use a pointer/optional to lazily construct
GuiManager) so headless runs never depend on GuiManager.
- Around line 349-417: The stdin_listener thread is detached while capturing
engine by reference and the process calls engine.shutdown() then std::_Exit(0),
creating a race; also the "chain" command reads engine.graph().get_nodes() and
node.pedal->is_enabled() without the audio-side synchronization
(Effect::enabled_ is non-atomic). Fix by not detaching stdin_listener: keep the
thread object and join it during shutdown after signalling g_running=false (or
reuse show_telemetry/another atomic) so the listener exits before
engine.shutdown()/std::_Exit(0); for the chain command avoid reading
engine.graph() and Effect internals directly from this thread — either call a
GUI-thread-safe snapshot API (e.g., add Engine::snapshot_graph_nodes() that
returns a copy) or expose a thread-safe accessor for effect state (e.g.,
Effect::is_enabled_atomic() or protect access with the same mutex used by
AudioEngine::drain_commands()); update usages of stdin_listener,
engine.shutdown(), std::_Exit(0), engine.graph().get_nodes(), and
node.pedal->is_enabled() accordingly.

---

Nitpick comments:
In `@tests/integration/test_headless.cpp`:
- Around line 12-13: Add an assertion that engine.initialize() succeeded
immediately after calling it (e.g., use ASSERT_TRUE/EXPECT_TRUE or
ASSERT_NO_THROW as appropriate for your test framework) so the test stops if
initialization fails; update both occurrences of engine.initialize() (the one at
the top and the one around lines 40–41) to assert success before proceeding to
exercise other behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7e6d560-34f4-477a-b724-d599d57be3a7

📥 Commits

Reviewing files that changed from the base of the PR and between bce133b and b8076ac.

📒 Files selected for processing (5)
  • CMakeLists.txt
  • src/cli.h
  • src/main.cpp
  • tests/integration/test_headless.cpp
  • tests/unit/test_cli.cpp

Comment thread src/cli.h Outdated
Comment thread src/main.cpp Outdated
Comment thread src/main.cpp Outdated
@kal-sudo

Copy link
Copy Markdown
Contributor Author

@sudip-mondal-2002 please test it out with an actual instrument to check if the audio thread is stable.

Comment thread src/main.cpp
Comment on lines +209 to 213
//CLI argument parsing
Amplitron::CliOptions cli_opts = Amplitron::handle_cli_args(argc, argv);
if(cli_opts.exit_early){
return 0;
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some logs

@sudip-mondal-2002

Copy link
Copy Markdown
Owner

Check the conflict and AMPLITRON_HEADLESS def

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main.cpp (1)

347-349: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast when the headless audio stream cannot start.

Headless mode still drops into the command loop after engine.start() fails, which leaves a "ready" daemon running without an active audio stream. Make this fatal in the headless path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main.cpp` around lines 347 - 349, If engine.start() returns false, make
the process fatal when running in headless mode so we don't fall through into
the command loop; after the existing engine.start() check, detect the headless
flag used by the program (e.g. headless / isHeadless / headlessMode) and replace
the current nonfatal warning with an error and immediate exit (return
EXIT_FAILURE or std::exit(1)) when headless is true, otherwise keep the existing
warning behavior for interactive runs; update the log message to be an error and
ensure cleanup if necessary before exiting.
♻️ Duplicate comments (2)
src/cli.h (1)

42-70: ⚠️ Potential issue | 🟠 Major

Reject option tokens as missing values for value-taking flags.

i + 1 < argc only proves there is another token. --headless --preset --input mic currently stores "--input" as the preset path, bypasses the required headless-preset validation, and --input/--output at end still fall through because the outer && i + 1 < argc makes the inner else unreachable. Treat a missing next token or a next option token as an error for all three flags.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli.h` around lines 42 - 70, The parsing logic for flags "--preset",
"--input", and "--output" accepts any next token (including other option tokens)
as a value; update the loop that inspects arg/argv/argc so that for
options.preset_path, options.input_device, and options.output_device you first
verify (i + 1 < argc) AND that argv[i+1] does not begin with '-' (or "--")
before consuming it (argv[++i]); if the next token is missing or starts with '-'
treat it as an error, set options.exit_early and options.exit_reason and return
options; remove the redundant outer "&& i + 1 < argc" guards that make inner
else unreachable so the explicit follow-up-token check handles both absence and
option-token cases consistently.
src/main.cpp (1)

367-375: ⚠️ Potential issue | 🔴 Critical

Don't detach a thread that borrows stack state.

stdin_listener captures cli_mutex and cli_commands by reference, then detach() lets it outlive the headless block. Once this block exits those references dangle, so any later stdin wakeup can lock freed storage. Keep the queue ownership outside the stack frame or make the listener joinable with a real shutdown path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main.cpp` around lines 367 - 375, The stdin_listener thread captures
cli_mutex and cli_commands by reference then is detached, risking use-after-free
when the stack frame ends; fix by giving the listener ownership of shared
lifetime objects (e.g., wrap cli_commands and cli_mutex in shared_ptr and
capture those by value in stdin_listener) or change the threading model to keep
stdin_listener joinable by introducing a shutdown flag (atomic<bool> done) and
calling stdin_listener.join() at program shutdown; ensure you update the capture
list of stdin_listener and remove detach() when using the joinable approach so
references never dangle.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main.cpp`:
- Around line 491-492: sessionManager.clearSession() is being called
unconditionally at shutdown which wipes any pending GUI crash-recovery session
even for headless/daemon runs; guard or relocate the clear so it only runs for a
GUI session (e.g. check engine.isHeadless() or an equivalent
Application::isHeadless()/Engine::isGuiMode() flag) or only when SessionManager
indicates a GUI session was created/restored (e.g. SessionManager::wasGuiUsed()
or SessionManager::createdThisRun()); move the clearSession() call out of the
global shutdown path and into the GUI-specific teardown (or wrap it with if
(!engine.isHeadless()) { sessionManager.clearSession(); }) so headless daemons
do not erase recovery state before engine.shutdown().
- Around line 213-219: The CLI headless flag is being applied after validation,
allowing a headless-only build to bypass required CLI checks and still return 0
on invalid args; set cli_opts.is_headless before running
Amplitron::handle_cli_args() or immediately re-validate and adjust behavior:
ensure AMPLITRON_HEADLESS forces cli_opts.is_headless prior to any validation
(or, if keeping current ordering, check cli_opts for missing required fields
after applying AMPLITRON_HEADLESS) and change the early-exit return to a
non-zero status when cli_opts.exit_early is true; update references to
Amplitron::CliOptions, Amplitron::handle_cli_args, cli_opts.is_headless,
cli_opts.exit_early, cli_opts.exit_reason, engine.initialize, and load_preset
accordingly.

---

Outside diff comments:
In `@src/main.cpp`:
- Around line 347-349: If engine.start() returns false, make the process fatal
when running in headless mode so we don't fall through into the command loop;
after the existing engine.start() check, detect the headless flag used by the
program (e.g. headless / isHeadless / headlessMode) and replace the current
nonfatal warning with an error and immediate exit (return EXIT_FAILURE or
std::exit(1)) when headless is true, otherwise keep the existing warning
behavior for interactive runs; update the log message to be an error and ensure
cleanup if necessary before exiting.

---

Duplicate comments:
In `@src/cli.h`:
- Around line 42-70: The parsing logic for flags "--preset", "--input", and
"--output" accepts any next token (including other option tokens) as a value;
update the loop that inspects arg/argv/argc so that for options.preset_path,
options.input_device, and options.output_device you first verify (i + 1 < argc)
AND that argv[i+1] does not begin with '-' (or "--") before consuming it
(argv[++i]); if the next token is missing or starts with '-' treat it as an
error, set options.exit_early and options.exit_reason and return options; remove
the redundant outer "&& i + 1 < argc" guards that make inner else unreachable so
the explicit follow-up-token check handles both absence and option-token cases
consistently.

In `@src/main.cpp`:
- Around line 367-375: The stdin_listener thread captures cli_mutex and
cli_commands by reference then is detached, risking use-after-free when the
stack frame ends; fix by giving the listener ownership of shared lifetime
objects (e.g., wrap cli_commands and cli_mutex in shared_ptr and capture those
by value in stdin_listener) or change the threading model to keep stdin_listener
joinable by introducing a shutdown flag (atomic<bool> done) and calling
stdin_listener.join() at program shutdown; ensure you update the capture list of
stdin_listener and remove detach() when using the joinable approach so
references never dangle.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c934d877-c493-4a8a-a939-c8f1c81be946

📥 Commits

Reviewing files that changed from the base of the PR and between b8076ac and d34455b.

📒 Files selected for processing (5)
  • CMakeLists.txt
  • src/audio/effects/effect.h
  • src/cli.h
  • src/main.cpp
  • tests/integration/test_headless.cpp
✅ Files skipped from review due to trivial changes (1)
  • CMakeLists.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/integration/test_headless.cpp

Comment thread src/main.cpp Outdated
Comment thread src/main.cpp Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cli.h (1)

54-74: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unreachable error handling for --input and --output flags.

The outer conditions at lines 54 and 65 include && i+1 < argc, so the inner if(i + 1 < argc) check is always true when reached. The else branches (lines 57-62 and 68-73) are dead code. When a user provides --input or --output without a value, the flag is silently ignored instead of producing an error.

The --preset handling (lines 43-52) shows the correct pattern: check only the flag name in the outer condition, then validate argument availability inside.

🐛 Proposed fix to match --preset pattern
-        else if(arg == "--input" && i+1 < argc){
+        else if(arg == "--input"){
             if(i + 1 < argc){ 
                 options.input_device = argv[++i];
             } else{
                 std::cerr << "Error: --input requires a device name argument.\n";
                 options.exit_early = true;
                 options.exit_code = 1;
                 options.exit_reason="Input Field Empty.";
                 return options;
             }
         }
-        else if(arg == "--output" && i+1 < argc){
+        else if(arg == "--output"){
             if(i + 1 < argc){ 
                 options.output_device = argv[++i];
             }else{
                 std::cerr << "Error: --output requires a device name argument.\n";
                 options.exit_early = true;
                 options.exit_code = 1;
                 options.exit_reason="Output Field Empty";
                 return options;
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli.h` around lines 54 - 74, The argument-parsing branches for the
--input and --output flags currently include a redundant outer guard "&& i+1 <
argc" which makes the inner availability checks and their error branches
unreachable; remove the "&& i+1 < argc" from the outer else-if conditions for
"--input" and "--output" so the code first matches the flag (arg == "--input" /
arg == "--output") and then performs the inner if(i + 1 < argc) validation; if
the inner check fails, set options.exit_early = true, options.exit_code = 1,
options.exit_reason to a descriptive message and return options (same pattern
used by the --preset handling), otherwise consume argv[++i] into
options.input_device or options.output_device.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/cli.h`:
- Around line 54-74: The argument-parsing branches for the --input and --output
flags currently include a redundant outer guard "&& i+1 < argc" which makes the
inner availability checks and their error branches unreachable; remove the "&&
i+1 < argc" from the outer else-if conditions for "--input" and "--output" so
the code first matches the flag (arg == "--input" / arg == "--output") and then
performs the inner if(i + 1 < argc) validation; if the inner check fails, set
options.exit_early = true, options.exit_code = 1, options.exit_reason to a
descriptive message and return options (same pattern used by the --preset
handling), otherwise consume argv[++i] into options.input_device or
options.output_device.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 319b4fe0-be2c-4160-9bdf-afc4de2cc2a7

📥 Commits

Reviewing files that changed from the base of the PR and between d34455b and b807544.

📒 Files selected for processing (3)
  • CMakeLists.txt
  • src/cli.h
  • src/main.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • CMakeLists.txt
  • src/main.cpp

@sudip-mondal-2002

Copy link
Copy Markdown
Owner

Test coverage is soo less for both cli.cpp and main.cpp

Can you push the coverage to 90%+
Let me know if possible @kal-sudo
image

@kal-sudo

Copy link
Copy Markdown
Contributor Author

I can push cli.h past 90%, but main.cpp is very hard to do without hanging test runner since it has multiple infinite loops(like while(g_running)).It usually is excluded from lcov for this reason.Do tell how you would prefer to handle that.
I have written tests that check the individual components i added in main.cpp.

@sudip-mondal-2002

Copy link
Copy Markdown
Owner

Okay ignore main.cpp for now, please cover cli.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cli.h (1)

43-75: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject option tokens as values for --preset, --input, and --output.

At Line 44, Line 55, and Line 66, the parser only checks bounds. If the next token is another flag, it is consumed as a value (e.g., --preset --headless), which silently misparses arguments and can defeat the intended fail-fast behavior.

Suggested patch
 inline CliOptions handle_cli_args(int argc, char* argv[]) {
     CliOptions options;
+    auto has_value_token = [&](int idx) {
+        return idx < argc && argv[idx] != nullptr && argv[idx][0] != '-';
+    };
+
     for (int i = 1; i < argc; ++i) {
         std::string arg = argv[i];
@@
         else if(arg == "--preset"){
-            if(i + 1 < argc){ 
+            if(has_value_token(i + 1)){
                 options.preset_path = argv[++i];//grabs next word(the preset path)
             } else{
                 std::cerr << "Error: --preset requires a file path argument.\n";
@@
         else if(arg == "--input"){
-            if(i + 1 < argc){ 
+            if(has_value_token(i + 1)){
                 options.input_device = argv[++i];
             } else{
                 std::cerr << "Error: --input requires a device name argument.\n";
@@
         else if(arg == "--output"){
-            if(i + 1 < argc){ 
+            if(has_value_token(i + 1)){
                 options.output_device = argv[++i];
             }else{
                 std::cerr << "Error: --output requires a device name argument.\n";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli.h` around lines 43 - 75, The parser currently accepts the next argv
token for --preset, --input, and --output without rejecting option tokens, so
flags like "--preset --headless" get misparsed; update the handling for the
"--preset", "--input", and "--output" branches to also check that argv[i+1] does
not start with '-' (e.g., argv[i+1][0] != '-') before consuming it, and if it
does start with '-' treat it as a missing argument: set options.exit_early=true,
options.exit_code=1, options.exit_reason to an appropriate message and return
options instead of assigning the flag as the value for options.preset_path,
options.input_device, or options.output_device.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/cli.h`:
- Around line 43-75: The parser currently accepts the next argv token for
--preset, --input, and --output without rejecting option tokens, so flags like
"--preset --headless" get misparsed; update the handling for the "--preset",
"--input", and "--output" branches to also check that argv[i+1] does not start
with '-' (e.g., argv[i+1][0] != '-') before consuming it, and if it does start
with '-' treat it as a missing argument: set options.exit_early=true,
options.exit_code=1, options.exit_reason to an appropriate message and return
options instead of assigning the flag as the value for options.preset_path,
options.input_device, or options.output_device.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a82a511c-b96a-4060-aac8-55603bc093d7

📥 Commits

Reviewing files that changed from the base of the PR and between b807544 and e21a67c.

📒 Files selected for processing (2)
  • CMakeLists.txt
  • src/cli.h
✅ Files skipped from review due to trivial changes (1)
  • CMakeLists.txt

@sudip-mondal-2002 sudip-mondal-2002 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@sudip-mondal-2002 sudip-mondal-2002 merged commit 0bc8426 into sudip-mondal-2002:main May 28, 2026
9 checks passed
github-actions Bot added a commit that referenced this pull request May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved GSSoC 2026 contribution High Impact Significant user-facing improvement level:advanced Advanced task · 45 GSSoC points mentor:sudip-mondal-2002 platform: linux Linux-specific issue or feature quality:exceptional type:feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Headless/CLI mode — run without GUI for live rig/Raspberry Pi deployment

2 participants