Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
eee994f
test: add missing coverage for GUI components
Arbaaz123676 Jun 1, 2026
f91be20
test: improve GUI component coverage
Arbaaz123676 Jun 2, 2026
5776d80
test: improve component coverage
Arbaaz123676 Jun 3, 2026
fd9103c
Tests
Arbaaz123676 Jun 3, 2026
106f57f
Merge branch 'main' into add-component-test-coverage-v2
sudip-mondal-2002 Jun 3, 2026
2119bc4
tests
Arbaaz123676 Jun 4, 2026
cc8e0cf
test: add missing coverage for GUI components
Arbaaz123676 Jun 1, 2026
0191967
test: improve GUI component coverage
Arbaaz123676 Jun 2, 2026
0214762
test: improve component coverage
Arbaaz123676 Jun 3, 2026
7ea30c2
Tests
Arbaaz123676 Jun 3, 2026
58dc009
tests
Arbaaz123676 Jun 4, 2026
cdc9f23
Merge origin/main into add-component-test-coverage-v2
sudip-mondal-2002 Jun 5, 2026
446aca8
Fix build and test failures, resolve compilation issues under new ImG…
sudip-mondal-2002 Jun 5, 2026
7f9966a
style: apply pre-commit hooks and formatting checks
sudip-mondal-2002 Jun 5, 2026
945a4c6
Merge branch 'temp-rebase' into add-component-test-coverage-v2
Arbaaz123676 Jun 6, 2026
22020d5
test: improve GUI component coverage
Arbaaz123676 Jun 6, 2026
73ca711
test: resolve merge conflicts and fix ImGui deactivation mock
Arbaaz123676 Jun 6, 2026
7ae4226
test: resolve merge conflicts in command history tests and fix format…
Arbaaz123676 Jun 6, 2026
3e0078e
Merge remote changes into add-component-test-coverage-v2 and resolve …
Arbaaz123676 Jun 6, 2026
209e81a
fix: resolve Windows file dialog build issue
Arbaaz123676 Jun 6, 2026
5958712
fix: clang formatting
Arbaaz123676 Jun 6, 2026
f680f16
Merge branch 'main' into add-component-test-coverage-v2
sudip-mondal-2002 Jun 10, 2026
f1e756a
test: add unit tests for headless file dialog mocks and stubs
sudip-mondal-2002 Jun 10, 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
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ else() # NOT EMSCRIPTEN, ANDROID, or IOS
tests/unit/test_looper.cpp
tests/unit/test_theme.cpp
tests/unit/test_command_history.cpp
tests/unit/test_effect_base.cpp
tests/unit/test_cabinet_sim_ir.cpp
tests/unit/test_wav_loader.cpp
tests/unit/test_convolution_engine.cpp
Expand Down Expand Up @@ -789,6 +790,7 @@ else() # NOT EMSCRIPTEN, ANDROID, or IOS
tests/ui/test_screen.cpp
tests/ui/test_pedal_board_chain.cpp
tests/ui/test_pedal_board_menu.cpp
tests/ui/test_file_dialog.cpp
tests/ui/test_pedal_widget_knobs.cpp
tests/unit/test_session_manager.cpp
tests/unit/test_cli.cpp
Expand Down
Binary file added load_test.wav
Binary file not shown.
Binary file modified short.wav
Binary file not shown.
8 changes: 4 additions & 4 deletions src/gui/components/screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ EMSCRIPTEN_KEEPALIVE void load_ir_callback_screen(uintptr_t cab_ptr, const char*

namespace Amplitron {

// Static variables to track active knob drag states across frames for accurate undo commitment
// Static variables to track active knob drag states across frames for accurate
// undo commitment
static bool s_knob_was_active = false;
static int s_active_param_index = -1;
static float s_param_value_before_drag = 0.0f;
Expand Down Expand Up @@ -153,8 +154,7 @@ void ScreenComponent::render_tuner_display(ImDrawList* dl, ImVec2 p0, float peda

ImGui::SetCursorScreenPos(ImVec2(cx - ml_size.x * 0.5f, display_y));
ImGui::SetNextItemAllowOverlap();
ImGui::InvisibleButton("##tuner_mute_toggle", ml_size);
if (ImGui::IsItemClicked()) {
if (ImGui::InvisibleButton("##tuner_mute_toggle", ml_size)) {
float new_val = mute_on ? 0.0f : 1.0f;
props.effect->params()[0].value = new_val;
if (props.engine) {
Expand Down Expand Up @@ -825,7 +825,7 @@ void ScreenComponent::render_multiband_compressor_display(ImDrawList* dl, ImVec2
Theme::TEXT_DIM, tick_lbl);
}
} else { // High crossover (1000 to 15000 Hz)
float tick_hzs[] = {1000.0f, 4000.0f, 8000.0f, 12000.0f, 15000.0f};
float tick_hzs[] = {500.0f, 1000.0f, 4000.0f, 8000.0f, 12000.0f, 15000.0f};
for (float hz : tick_hzs) {
float norm = (hz - param.min_val) / range;
float ty = track_bottom - norm * (track_bottom - track_top);
Expand Down
14 changes: 10 additions & 4 deletions src/gui/dialogs/file_dialog_native_open.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@

#include "gui/dialogs/file_dialog.h"

#ifdef _WIN32
// clang-format off
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commdlg.h>
// clang-format on
#endif

#ifdef __APPLE__
Expand All @@ -27,15 +26,21 @@
#ifndef _WIN32
#include <sys/wait.h>
#endif
// clang-format on

namespace Amplitron {

#ifdef AMPLITRON_HEADLESS
std::string show_open_dialog(const std::string&, const std::string&, const std::string&) {
return "";
static std::string s_mock_open_path = "";

void set_mock_open_dialog_path(const std::string &path) { s_mock_open_path = path; }

std::string show_open_dialog(const std::string &, const std::string &, const std::string &) {
return s_mock_open_path;
}
#else

// clang-format off
#ifdef _WIN32
std::string show_open_dialog(const std::string& title, const std::string& filter_desc,
const std::string& filter_ext) {
Expand Down Expand Up @@ -195,6 +200,7 @@ std::string show_open_dialog(const std::string& title, const std::string& filter
return result;
}
#endif
// clang-format on

#endif // AMPLITRON_HEADLESS

Expand Down
71 changes: 71 additions & 0 deletions tests/integration/test_snapshot_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -860,3 +860,74 @@ TEST(snapshot_multiple_effects_all_params_preserved) {

engine.shutdown();
}

TEST(SnapshotSlotLabelsCorrect) {

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Rename new test cases to snake_case to match repository C++ naming rules.

The newly added test case identifiers use PascalCase; in this codebase, function/method names are expected to be lowercase snake_case.

Proposed rename diff
-TEST(SnapshotSlotLabelsCorrect) {
+TEST(snapshot_slot_labels_correct) {
@@
-TEST(SnapshotSlotActiveSlotArbitrary) {
+TEST(snapshot_slot_active_slot_arbitrary) {
@@
-TEST(SnapshotApplyMismatchedParamCount) {
+TEST(snapshot_apply_mismatched_param_count) {
@@
-TEST(SnapshotCaptureEmptyEngine) {
+TEST(snapshot_capture_empty_engine) {

As per coding guidelines, "Functions and Methods should use lowercase snake_case (e.g., push_param_change(), remove_mapping())".

Also applies to: 878-878, 887-887, 930-930

🤖 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_snapshot_manager.cpp` at line 870, Rename the new TEST
identifiers from PascalCase to lowercase snake_case (e.g., change
TEST(SnapshotSlotLabelsCorrect) to TEST(snapshot_slot_labels_correct)) and
update any references or registrations accordingly; apply the same renaming
pattern to the other newly added tests flagged in this review so all TEST(...)
identifiers follow the repository rule of lowercase snake_case (ensure function
names inside the test or any CALLS that reference the test name are updated to
match).

ASSERT_EQ(SnapshotManager::NUM_SLOTS, 4);
ASSERT_EQ(std::string(SnapshotManager::SLOT_LABELS[0]), "A");
ASSERT_EQ(std::string(SnapshotManager::SLOT_LABELS[1]), "B");
ASSERT_EQ(std::string(SnapshotManager::SLOT_LABELS[2]), "C");
ASSERT_EQ(std::string(SnapshotManager::SLOT_LABELS[3]), "D");
}

TEST(SnapshotSlotActiveSlotArbitrary) {
SnapshotManager mgr;
ASSERT_EQ(mgr.active_slot(), -1);
mgr.set_active_slot(2);
ASSERT_EQ(mgr.active_slot(), 2);
mgr.set_active_slot(999);
ASSERT_EQ(mgr.active_slot(), 999);
}

TEST(SnapshotApplyMismatchedParamCount) {
AudioEngine engine;
engine.initialize();

auto od = std::make_shared<Overdrive>();
engine.add_effect(od);

size_t param_count = od->params().size();
ASSERT_TRUE(param_count > 0);

float default_val = od->params()[0].value;

// Case 1: Snapshot has fewer params (empty param_values)
{
SnapshotManager::BoardSnapshot snap;
LoadPresetCommand::EffectSnapshot es;
es.effect = od;
es.enabled = true;
es.mix = 1.0f;
snap.effects.push_back(es);

od->params()[0].value = default_val + 5.0f;
SnapshotManager::apply(snap, engine);
ASSERT_NEAR(od->params()[0].value, default_val + 5.0f, 0.001f);
}

// Case 2: Snapshot has more params than the effect actually has
{
SnapshotManager::BoardSnapshot snap;
LoadPresetCommand::EffectSnapshot es;
es.effect = od;
es.enabled = true;
es.mix = 1.0f;
es.param_values.assign(param_count + 5, default_val + 2.0f);
snap.effects.push_back(es);

SnapshotManager::apply(snap, engine);
ASSERT_NEAR(od->params()[0].value, default_val + 2.0f, 0.001f);
}

engine.shutdown();
}

TEST(SnapshotCaptureEmptyEngine) {
AudioEngine engine;
engine.initialize();
clear_engine(engine);

SnapshotManager::BoardSnapshot snap = SnapshotManager::capture(engine);
ASSERT_TRUE(snap.effects.empty());

engine.shutdown();
}
141 changes: 141 additions & 0 deletions tests/ui/test_file_dialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* @file test_file_dialog.cpp
* @brief Tests for file dialog headless mock paths.
*
* Under AMPLITRON_HEADLESS the native dialog functions are replaced by
* controllable mocks. These tests exercise every reachable code path
* in file_dialog_native_open.cpp (the mock implementation), as well as
* the stub implementations in file_dialog_native.cpp (save) and
* file_dialog_native_folder.cpp (folder).
*/

#include <string>

#include "gui/dialogs/file_dialog.h"
#include "test_framework.h"

using namespace Amplitron;
using namespace TestFramework;

// Forward-declare the mock setter (defined in file_dialog_native_open.cpp
// under AMPLITRON_HEADLESS).
namespace Amplitron {
void set_mock_open_dialog_path(const std::string& path);
}

// =============================================================================
// show_open_dialog — headless mock
// =============================================================================

TEST(file_dialog_open_returns_empty_by_default) {
// Without setting a mock path, the dialog must return "".
Amplitron::set_mock_open_dialog_path("");
std::string result = show_open_dialog("Open File", "WAV Audio", "wav");
ASSERT_EQ(result, "");
}

TEST(file_dialog_open_returns_mock_path) {
// After setting a mock path, show_open_dialog must return it verbatim.
Amplitron::set_mock_open_dialog_path("/tmp/test_file.wav");
std::string result = show_open_dialog("Open File", "WAV Audio", "wav");
ASSERT_EQ(result, "/tmp/test_file.wav");
}

TEST(file_dialog_open_ignores_title_and_filter_params) {
// The mock path must be returned regardless of the title/filter arguments.
Amplitron::set_mock_open_dialog_path("my_ir.wav");
ASSERT_EQ(show_open_dialog("Load Impulse Response", "WAV Audio", "wav"), "my_ir.wav");
ASSERT_EQ(show_open_dialog("Select File", "All Files", "*"), "my_ir.wav");
ASSERT_EQ(show_open_dialog("", "", ""), "my_ir.wav");
}

TEST(file_dialog_open_can_be_reset) {
// Set a path, verify it is returned, reset, verify empty.
Amplitron::set_mock_open_dialog_path("first.wav");
ASSERT_EQ(show_open_dialog(), "first.wav");

Amplitron::set_mock_open_dialog_path("");
ASSERT_EQ(show_open_dialog(), "");
}

TEST(file_dialog_open_can_be_overwritten) {
// Setting a new mock path overwrites the previous one.
Amplitron::set_mock_open_dialog_path("a.wav");
ASSERT_EQ(show_open_dialog(), "a.wav");

Amplitron::set_mock_open_dialog_path("b.wav");
ASSERT_EQ(show_open_dialog(), "b.wav");
}

TEST(file_dialog_open_default_params) {
// Call with default arguments (title="Open File", desc="WAV Audio",
// ext="wav").
Amplitron::set_mock_open_dialog_path("default_args.wav");
ASSERT_EQ(show_open_dialog(), "default_args.wav");
}

TEST(file_dialog_open_unicode_path) {
// The mock must handle paths with unicode characters.
const std::string unicode_path = "/tmp/日本語ファイル.wav";
Amplitron::set_mock_open_dialog_path(unicode_path);
ASSERT_EQ(show_open_dialog("Open", "WAV", "wav"), unicode_path);
}

TEST(file_dialog_open_path_with_spaces) {
// The mock must handle paths containing spaces.
const std::string spaced_path = "/my folder/my file.wav";
Amplitron::set_mock_open_dialog_path(spaced_path);
ASSERT_EQ(show_open_dialog("Open", "WAV", "wav"), spaced_path);
}

TEST(file_dialog_open_path_with_special_chars) {
// The mock must handle paths with special characters (quotes,
// backslashes).
const std::string special_path = R"(C:\Users\test\"file".wav)";
Amplitron::set_mock_open_dialog_path(special_path);
ASSERT_EQ(show_open_dialog("Open", "WAV", "wav"), special_path);
}

TEST(file_dialog_open_consecutive_calls_same_result) {
// Multiple consecutive calls without resetting should return the same mock
// path.
Amplitron::set_mock_open_dialog_path("stable.wav");
ASSERT_EQ(show_open_dialog(), "stable.wav");
ASSERT_EQ(show_open_dialog(), "stable.wav");
ASSERT_EQ(show_open_dialog(), "stable.wav");
}

// =============================================================================
// show_save_dialog — headless stub (always returns "")
// =============================================================================

TEST(file_dialog_save_returns_empty_in_headless) {
std::string result = show_save_dialog("recording.wav", "WAV Audio", "wav");
ASSERT_EQ(result, "");
}

TEST(file_dialog_save_default_params) {
// Call with default arguments.
ASSERT_EQ(show_save_dialog(), "");
}

TEST(file_dialog_save_custom_params) {
// Custom arguments should still return "" in headless mode.
ASSERT_EQ(show_save_dialog("my_preset.json", "Preset Files", "json"), "");
}

// =============================================================================
// show_folder_dialog — headless stub (always returns "")
// =============================================================================

TEST(file_dialog_folder_returns_empty_in_headless) {
std::string result = show_folder_dialog("Select Folder");
ASSERT_EQ(result, "");
}

TEST(file_dialog_folder_default_params) {
// Call with default argument.
ASSERT_EQ(show_folder_dialog(), "");
}

TEST(file_dialog_folder_custom_title) { ASSERT_EQ(show_folder_dialog("Choose IR Directory"), ""); }
Loading
Loading