Skip to content

Commit

Permalink
tests: add qa_chart test
Browse files Browse the repository at this point in the history
Tests DashboardPage::drawPlot().

Signed-off-by: Sergio Martins <[email protected]>
  • Loading branch information
iamsergio committed Nov 11, 2024
1 parent 3109286 commit b8fd26e
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/ui/DashboardPage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include "GridLayout.hpp"

struct TestApp;

namespace DigitizerUi {

class DashboardPage {
Expand Down Expand Up @@ -41,6 +43,7 @@ class DashboardPage {
void newPlot(Dashboard& dashboard);

private:
friend struct ::TestApp;
void drawPlots(Dashboard& dashboard, DigitizerUi::DashboardPage::Mode mode);
void drawGrid(float w, float h);
void drawLegend(Dashboard& dashboard, const Mode& mode) noexcept;
Expand Down
16 changes: 15 additions & 1 deletion src/ui/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,22 @@ add_test(NAME qa_play_stop_bar COMMAND qa_play_stop_bar)

if(ENABLE_IMGUI_TEST_ENGINE)

cmrc_add_resource_library(
ui_test_assets
NAMESPACE
ui_test_assets
examples/fg_dipole_intensity_ramp.grc
examples/fg_dipole_intensity_ramp.yml)

# Code shared between all tests
add_library(imgui_tests_common STATIC ImGuiTestApp.cpp)
target_link_libraries(imgui_tests_common PUBLIC opendigitizer-uilib raii_wrapper)
target_link_libraries(imgui_tests_common PUBLIC
opendigitizer-uilib
raii_wrapper
ui_assets
ui_test_assets
)

target_compile_definitions(imgui_tests_common PUBLIC IMGUI_DEFINE_MATH_OPERATORS
OPENDIGITIZER_BUILD_DIRECTORY="${CMAKE_BINARY_DIR}")

Expand All @@ -48,5 +61,6 @@ if(ENABLE_IMGUI_TEST_ENGINE)
add_imgui_test(qa_dialog)
add_imgui_test(qa_filtercomboboxes)
add_imgui_test(qa_keypad)
add_imgui_test(qa_chart)

endif()
141 changes: 141 additions & 0 deletions src/ui/test/examples/fg_dipole_intensity_ramp.grc
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
blocks:
- name: ClockSource
id: gr::basic::ClockSource
template_args: "float"
parameters:
chunk_size: 100
do_zero_order_hold: true
n_samples_max: 100000000
repeat_period: 2000000000
sample_rate: 1000.000000
verbose_console: false
tag_times:
- 10000000 # 10 ms in ns
- 100000000 # 100 ms in ns
- 300000000 # 300 ms in ns
- 350000000 # 350 ms in ns
- 550000000 # 550 ms in ns
- 560000000 # 560 ms in ns
- 650000000 # 650 ms in ns
- 800000000 # 800 ms in ns
- 850000000 # 850 ms in ns
tag_values:
- "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=1"
- "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=2"
- "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=3"
- "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=4"
- "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=5"
- "CMD_DIAG_TRIGGER1"
- "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=6"
- "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=7"
- "CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=8"
- name: DipoleCurrentGenerator
id: gr::basic::FunctionGenerator
template_args: "float"
parameters:
duration: 0.000000
final_value: 0.000000
impulse_time0: 0.000000
impulse_time1: 0.000000
round_off_time: 0.000000
sample_rate: 1000.000000
signal_type: Const
start_value: 0.000000
ctx_parameters:
- context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=1
time: 0
parameters:
duration: 0.000000
final_value: 0.000000
impulse_time0: 0.000000
impulse_time1: 0.000000
round_off_time: 0.000000
sample_rate: 1000.000000
signal_type: Const
start_value: 5.000000
- context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=2
time: 0
parameters:
duration: 0.200000
final_value: 30.000000
impulse_time0: 0.000000
impulse_time1: 0.000000
round_off_time: 0.000000
sample_rate: 1000.000000
signal_type: LinearRamp
start_value: 5.000000
- context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=3
time: 0
parameters:
duration: 0.000000
final_value: 0.000000
impulse_time0: 0.000000
impulse_time1: 0.000000
round_off_time: 0.000000
sample_rate: 1000.000000
signal_type: Const
start_value: 30.000000
- context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=4
time: 0
parameters:
duration: 0.100000
final_value: 20.000000
impulse_time0: 0.000000
impulse_time1: 0.000000
round_off_time: 0.020000
sample_rate: 1000.000000
signal_type: ParabolicRamp
start_value: 30.000000
- context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=5
time: 0
parameters:
duration: 0.000000
final_value: 0.000000
impulse_time0: 0.000000
impulse_time1: 0.000000
round_off_time: 0.000000
sample_rate: 1000.000000
signal_type: Const
start_value: 20.000000
- context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=6
time: 0
parameters:
duration: 0.100000
final_value: 10.000000
impulse_time0: 0.000000
impulse_time1: 0.000000
round_off_time: 0.000000
sample_rate: 1000.000000
signal_type: CubicSpline
start_value: 20.000000
- context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=7
time: 0
parameters:
duration: 0.000000
final_value: 0.000000
impulse_time0: 0.000000
impulse_time1: 0.000000
round_off_time: 0.000000
sample_rate: 1000.000000
signal_type: Const
start_value: 10.000000
- context: CMD_BP_START/FAIR.SELECTOR.C=1:S=1:P=8
time: 0
parameters:
duration: 0.000000
final_value: 20.000000
impulse_time0: 0.020000
impulse_time1: 0.060000
round_off_time: 0.000000
sample_rate: 1000.000000
signal_type: ImpulseResponse
start_value: 5.000000
- name: DipoleCurrentSink
id: opendigitizer::ImPlotSink
template_args: float
parameters:
signal_name: dipole current
signal_unit: A
connections:
- [ClockSource, 0, DipoleCurrentGenerator, 0]
- [DipoleCurrentGenerator, 0, DipoleCurrentSink, 0]
21 changes: 21 additions & 0 deletions src/ui/test/examples/fg_dipole_intensity_ramp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
sources:
- name: sink 1
block: DipoleCurrentSink
port: 0
color: 4281816053
plots:
- name: Plot 1
axes:
- axis: X
min: 0.1
max: 3000.0
- axis: Y
min: -1.991981029510498
max: 1.9978399276733398
sources:
- sink 1
rect:
- 8
- 0
- 8
- 8
148 changes: 148 additions & 0 deletions src/ui/test/qa_chart.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include "imgui.h"
#include "imgui_test_engine/imgui_te_context.h"

#include <boost/ut.hpp>

#include "ImGuiTestApp.hpp"

#include <Dashboard.hpp>
#include <DashboardPage.hpp>
#include <Flowgraph.hpp>

// TODO: blocks are locally included/registered for this test -> should become a global feature
#include "blocks/Arithmetic.hpp"
#include "blocks/ImPlotSink.hpp"
#include "blocks/SineSource.hpp"
#include "gnuradio-4.0/basic/FunctionGenerator.hpp"
#include "gnuradio-4.0/basic/clock_source.hpp"
#include "imgui_test_engine/imgui_te_internal.h"
#include <gnuradio-4.0/Scheduler.hpp>
#include <gnuradio-4.0/fourier/fft.hpp>

#include <cmrc/cmrc.hpp>
#include <gnuradio-4.0/Graph_yaml_importer.hpp>
#include <gnuradio-4.0/Profiler.hpp>
#include <gnuradio-4.0/Scheduler.hpp>
#include <memory.h>

CMRC_DECLARE(ui_test_assets);

using namespace boost;
using namespace boost::ut;

// We derive from gr's scheduler just to make stop() public
template<gr::profiling::ProfilerLike TProfiler = gr::profiling::null::Profiler>
class TestScheduler : public gr::scheduler::Simple<gr::scheduler::ExecutionPolicy::singleThreaded, TProfiler> {
public:
explicit TestScheduler(gr::Graph&& graph) : gr::scheduler::Simple<gr::scheduler::ExecutionPolicy::singleThreaded, TProfiler>(std::move(graph)) {}
void stop() { gr::scheduler::Simple<gr::scheduler::ExecutionPolicy::singleThreaded, TProfiler>::stop(); }
};

struct TestState {
std::shared_ptr<DigitizerUi::Dashboard> dashboard;
std::function<void()> stopFunction;
std::thread schedulerThread;
std::unique_ptr<TestScheduler<gr::profiling::null::Profiler>> scheduler;

void startScheduler(gr::Graph&& graph) {
schedulerThread = std::thread([&] {
scheduler = std::make_unique<TestScheduler<gr::profiling::null::Profiler>>(std::move(graph));
scheduler->runAndWait();
});
}

void stopScheduler() {
dashboard = {};
scheduler->stop();
schedulerThread.join();
}
};

TestState g_state;

struct TestApp : public DigitizerUi::test::ImGuiTestApp {
using DigitizerUi::test::ImGuiTestApp::ImGuiTestApp;

void registerTests() override {

ImGuiTest* t = IM_REGISTER_TEST(engine(), "chart_dashboard", "DashboardPage::drawPlot");
t->SetVarsDataType<TestState>();

t->GuiFunc = [](ImGuiTestContext*) {
ImGui::Begin("Test Window", nullptr, ImGuiWindowFlags_NoSavedSettings);

ImGui::SetWindowPos({0, 0});
ImGui::SetWindowSize(ImVec2(500, 500));

if (g_state.dashboard) {
ut::expect(!g_state.dashboard->plots().empty());
auto plot = g_state.dashboard->plots()[0];

if (ImPlot::BeginPlot("Line Plot")) {
DigitizerUi::DashboardPage::drawPlot(*g_state.dashboard, plot);
ImPlot::EndPlot();
}
}

ImGui::End();
};

t->TestFunc = [](ImGuiTestContext* ctx) {
"DashboardPage::drawPlot"_test = [ctx] {
ctx->SetRef("Test Window");

// For our test we stop the graph after a certain amount samples.
// TODO: Once Ivan finishes his new ImPlotSink registry class we can remove these reinterpret_cast.

auto execution = g_state.dashboard->localFlowGraph.createExecutionContext();
auto blockModel = g_state.dashboard->localFlowGraph.findPlotSinkGrBlock("sink 3");
ut::expect(blockModel);
auto plotBlockModel = reinterpret_cast<gr::BlockWrapper<opendigitizer::ImPlotSink<float>>*>(blockModel);
auto implotSink = reinterpret_cast<opendigitizer::ImPlotSink<float>*>(plotBlockModel->raw());

const int maxSamples = 1400;
while (g_state.dashboard && implotSink->data.size() < maxSamples) {
ImGuiTestEngine_Yield(ctx->Engine);
}

captureScreenshot(*ctx);

g_state.stopScheduler();
};
};
}
};

int main(int argc, char* argv[]) {
auto options = DigitizerUi::test::TestOptions::fromArgs(argc, argv);
options.screenshotPrefix = "chart";

options.speedMode = ImGuiTestRunSpeed_Normal;
TestApp app(options);

// init early, as Dashboard invokes ImGui style stuff
app.initImGui();

auto loader = DigitizerUi::test::ImGuiTestApp::createPluginLoader();

// TODO: fg_dipole_intensity_ramp fails to load, somthing about unkown_type when parsing port
// auto fs = cmrc::ui_test_assets::get_filesystem();
// auto grcFile = fs.open("examples/fg_dipole_intensity_ramp.grc");
// auto dashboardFile = fs.open("examples/fg_dipole_intensity_ramp.yml");

auto fs = cmrc::sample_dashboards::get_filesystem();
auto grcFile = fs.open("assets/sampleDashboards/DemoDashboard.grc");
auto dashboardFile = fs.open("assets/sampleDashboards/DemoDashboard.yml");

auto dashBoardDescription = DigitizerUi::DashboardDescription::createEmpty("empty");
g_state.dashboard = DigitizerUi::Dashboard::create(/**fgItem=*/nullptr, dashBoardDescription);
g_state.dashboard->setPluginLoader(loader);
g_state.dashboard->load(std::string(grcFile.begin(), grcFile.end()), std::string(dashboardFile.begin(), dashboardFile.end()));

auto execution = g_state.dashboard->localFlowGraph.createExecutionContext();
g_state.dashboard->localFlowGraph.setPlotSinkGrBlocks(std::move(execution.plotSinkGrBlocks));

g_state.startScheduler(std::move(execution.graph));

return app.runTests() ? 0 : 1;
}

0 comments on commit b8fd26e

Please sign in to comment.