diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-01-26 02:18:21 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-01-26 02:21:16 +0100 |
| commit | 69fabac6d2219c07cfdcd9a8fe6e92bf4669be46 (patch) | |
| tree | 1627020b59fb7380f6e7a4d5ba42de8f1955242c | |
| parent | 811e70bdd09a618f8b0a512950a7f3254c591614 (diff) | |
Add a simple cucumber test.
| -rw-r--r-- | DEVELOP.md | 29 | ||||
| -rw-r--r-- | features/add_still_image.feature | 11 | ||||
| -rw-r--r-- | features/step_definitions/cucumber.wire | 3 | ||||
| -rw-r--r-- | features/step_definitions/dcpomatic.cc | 146 | ||||
| -rw-r--r-- | features/support/env.rb | 0 | ||||
| -rw-r--r-- | features/wscript | 40 | ||||
| -rwxr-xr-x | run/cucumber | 11 | ||||
| -rwxr-xr-x | run/cucumber_server | 9 | ||||
| -rw-r--r-- | src/lib/wscript | 8 | ||||
| -rw-r--r-- | src/tools/dcpomatic.cc | 89 | ||||
| -rw-r--r-- | src/tools/wscript | 6 | ||||
| -rw-r--r-- | src/wx/content_panel.cc | 35 | ||||
| -rw-r--r-- | src/wx/content_panel.h | 5 | ||||
| -rw-r--r-- | src/wx/cucumber_bridge.cc | 110 | ||||
| -rw-r--r-- | src/wx/cucumber_bridge.h | 75 | ||||
| -rw-r--r-- | src/wx/cucumber_registry.cc | 71 | ||||
| -rw-r--r-- | src/wx/cucumber_registry.h | 41 | ||||
| -rw-r--r-- | src/wx/film_editor.cc | 15 | ||||
| -rw-r--r-- | src/wx/film_editor.h | 6 | ||||
| -rw-r--r-- | src/wx/wscript | 6 | ||||
| -rw-r--r-- | wscript | 13 |
21 files changed, 721 insertions, 8 deletions
diff --git a/DEVELOP.md b/DEVELOP.md index 75233d369..35294c463 100644 --- a/DEVELOP.md +++ b/DEVELOP.md @@ -125,3 +125,32 @@ Files to edit: - `platform/osx/make_dmg.sh` - `platform/windows/wscript` - `platform/osx/wscript` + + +## Cucumber + +To run the cucumber tests you will need to build cucumber-cpp: + +git clone https://github.com/cucumber/cucumber-cpp.git +cd cucumber-cpp +cmake . +make +sudo make install + +It may give warnings about not finding Qt but you can ignore them +(Qt is not required). + +You then need three processes: + +`run/cucumber_server` + +Runs dcpomatic_cucumber (built from step_defintions/dcpomatic.cc): +a means to ask a running DCP-o-matic to do something. + +`dcpomatic2 --cucumber` + +A DCP-o-matic instance that is listening for dcpomatic_cucumber to send something. + +`cucumber .` + +The ruby program that performs the steps in the tests. diff --git a/features/add_still_image.feature b/features/add_still_image.feature new file mode 100644 index 000000000..6e3c48a47 --- /dev/null +++ b/features/add_still_image.feature @@ -0,0 +1,11 @@ +Feature: Add still image + +Scenario Outline: Add some content + Given I have created a new film + When I add file <file> as content + Then the result should be only <display> in the content list + +Examples: + | file | display | + | /home/carl/DCP/bbc405.png | bbc405.png [still] | + diff --git a/features/step_definitions/cucumber.wire b/features/step_definitions/cucumber.wire new file mode 100644 index 000000000..aa2b7b19a --- /dev/null +++ b/features/step_definitions/cucumber.wire @@ -0,0 +1,3 @@ +host: localhost +port: 3902 + diff --git a/features/step_definitions/dcpomatic.cc b/features/step_definitions/dcpomatic.cc new file mode 100644 index 000000000..98570b6b3 --- /dev/null +++ b/features/step_definitions/dcpomatic.cc @@ -0,0 +1,146 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "wx/cucumber_bridge.h" +#include "lib/nanomsg.h" +#include <fmt/format.h> +#include <boost/test/unit_test.hpp> + +#include <cucumber-cpp/autodetect.hpp> + + +using std::getline; +using std::string; +using std::stringstream; +using std::vector; +using cucumber::ScenarioScope; + + +auto constexpr TIMEOUT = 200; + + +struct Context +{ + Context() + : _nanomsg(false, "cucumber") + { + + } + + void click_button(string id) + { + general_delay(); + send(CUCUMBER_BRIDGE_CLICK_REGISTERED_BUTTON); + send(id); + } + + /* Click a button in the focussed dialog */ + void click_button(wxStandardID id) + { + general_delay(); + send(CUCUMBER_BRIDGE_CLICK_BUTTON_ID); + send(fmt::to_string(id)); + } + + void select_menu(string id) + { + general_delay(); + send(CUCUMBER_BRIDGE_SELECT_MENU); + send(id); + } + + void type(std::string text) + { + general_delay(); + send(CUCUMBER_BRIDGE_TYPE); + send(text); + } + + void add_content_file(string filename) + { + general_delay(); + send(CUCUMBER_BRIDGE_ADD_CONTENT_FILE); + send(filename); + } + + vector<string> content_list() + { + general_delay(); + send(CUCUMBER_BRIDGE_GET_CONTENT_LIST); + stringstream all(receive()); + vector<string> split; + string one; + while (getline(all, one, '\n')) { + split.push_back(one); + } + return split; + } + +private: + void send(string m) + { + BOOST_REQUIRE(_nanomsg.send(m + "\n", TIMEOUT)); + } + + std::string receive() + { + auto reply = _nanomsg.receive(TIMEOUT); + BOOST_REQUIRE(static_cast<bool>(reply)); + return *reply; + } + + void general_delay() + { + sleep(2); + } + + Nanomsg _nanomsg; +}; + + +GIVEN("^I have created a new film$") +{ + ScenarioScope<Context> context; + context->select_menu(CUCUMBER_MENU_FILE_NEW); + context->type("test"); + context->click_button(wxID_OK); +} + + +WHEN("^I add file (.+) as content$") +{ + REGEX_PARAM(string, filename); + ScenarioScope<Context> context; + /* I could not find a way to reliably enter text into a open file dialogue, so + * there's a custom method here. + */ + context->add_content_file(filename); +} + +THEN("^the result should be only (.+) in the content list$") +{ + REGEX_PARAM(string, entry); + ScenarioScope<Context> context; + auto content = context->content_list(); + BOOST_REQUIRE_EQUAL(content.size(), 1); + BOOST_CHECK_EQUAL(content.front(), entry); +} + diff --git a/features/support/env.rb b/features/support/env.rb new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/features/support/env.rb diff --git a/features/wscript b/features/wscript new file mode 100644 index 000000000..4b2d0b283 --- /dev/null +++ b/features/wscript @@ -0,0 +1,40 @@ +# +# Copyright (C) 2025 Carl Hetherington <cth@carlh.net> +# +# This file is part of DCP-o-matic. +# +# DCP-o-matic is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# DCP-o-matic is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. +# + + +def configure(conf): + conf.env['LIB_CUCUMBER'] = 'cucumber-cpp' + + conf.check_cxx(fragment=""" + #include <cucumber-cpp/generic.hpp>\n + #include <cucumber-cpp/autodetect.hpp>\n + int main() { return 0; }\n + """, + msg='Checking for cucumber-cpp library', + uselib_store='CUCUMBER_CPP') + + +def build(bld): + obj = bld(features='cxx cxxprogram') + obj.name = 'dcpomatic_cucumber' + obj.uselib = 'CUCUMBER CUCUMBER_CPP BOOST_FILESYSTEM BOOST_THREAD BOOST_REGEX BOOST_THREAD BOOST_TEST WXWIDGETS DCP ' + obj.uselib += 'AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE SWRESAMPLE ' + obj.use = 'libdcpomatic2' + obj.source = 'step_definitions/dcpomatic.cc' + obj.target = obj.name diff --git a/run/cucumber b/run/cucumber new file mode 100755 index 000000000..23d2c8b26 --- /dev/null +++ b/run/cucumber @@ -0,0 +1,11 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +export LD_LIBRARY_PATH=build/src/lib:build/src/wx:$LD_LIBRARY_PATH +export DCPOMATIC_GRAPHICS=$DIR/../graphics + +build/features/dcpomatic_cucumber & +sleep 5 +cucumber . + diff --git a/run/cucumber_server b/run/cucumber_server new file mode 100755 index 000000000..9c3db6990 --- /dev/null +++ b/run/cucumber_server @@ -0,0 +1,9 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +export LD_LIBRARY_PATH=build/src/lib:build/src/wx:$LD_LIBRARY_PATH +export DCPOMATIC_GRAPHICS=$DIR/../graphics + +build/features/dcpomatic_cucumber + diff --git a/src/lib/wscript b/src/lib/wscript index 2855041b2..4e4a41a01 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -260,14 +260,18 @@ def build(bld): obj.source = sources + ' version.cc' if bld.env.ENABLE_DISK: - obj.source += ' copy_to_drive_job.cc disk_writer_messages.cc ext.cc nanomsg.cc' - obj.uselib += ' LWEXT4 NANOMSG' + obj.source += ' copy_to_drive_job.cc disk_writer_messages.cc ext.cc' + obj.uselib += ' LWEXT4' if bld.env.TARGET_LINUX: obj.uselib += ' POLKIT' if bld.env.ENABLE_GROK: obj.source += ' grok_j2k_encoder_thread.cc' + if bld.env.ENABLE_DISK or bld.env.ENABLE_CUCUMBER: + obj.source += ' nanomsg.cc' + obj.uselib += ' NANOMSG' + if bld.env.TARGET_WINDOWS_64 or bld.env.TARGET_WINDOWS_32: obj.uselib += ' WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE SETUPAPI OLE32 UUID' obj.source += ' cross_windows.cc' diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index 3d9d571af..20f9beaa1 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net> + Copyright (C) 2012-2022 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -26,6 +26,10 @@ #include "wx/about_dialog.h" #include "wx/content_panel.h" +#ifdef DCPOMATIC_CUCUMBER +#include "wx/cucumber_bridge.h" +#include "wx/cucumber_registry.h" +#endif #include "wx/dcp_referencing_dialog.h" #include "wx/dkdm_dialog.h" #include "wx/export_subtitles_dialog.h" @@ -530,6 +534,17 @@ public: return _film; } +#ifdef DCPOMATIC_CUCUMBER + void cucumber_add_content_file(string filename) { + _film_editor->cucumber_add_content_file(filename); + } + + /* Could be called from any thread */ + std::string cucumber_get_content_list() { + return _film_editor->cucumber_get_content_list(); + } +#endif + private: void show (wxShowEvent& ev) @@ -1604,7 +1619,10 @@ static const wxCmdLineEntryDesc command_line_description[] = { { wxCMD_LINE_SWITCH, "n", "new", "create new film", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_OPTION, "c", "content", "add content file / directory", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_OPTION, "d", "dcp", "add content DCP", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, - { wxCMD_LINE_SWITCH, "v", "version", "show version", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL }, + { wxCMD_LINE_SWITCH, "v", "version", "show DCP-o-matic version", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL }, +#ifdef DCPOMATIC_CUCUMBER + { wxCMD_LINE_SWITCH, "t", "cucumber", "enable cucumber tests", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL }, +#endif { wxCMD_LINE_OPTION, "", "config", "directory containing config.xml and cinemas.xml", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_PARAM, 0, 0, "film to load or create", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 } @@ -1760,6 +1778,12 @@ private: grk_plugin::setMessengerLogger(new grk_plugin::GrokLogger("[GROK] ")); setup_grok_library_path(); #endif + +#ifdef DCPOMATIC_CUCUMBER + if (_cucumber_bridge) { + _cucumber_bridge->start(); + } +#endif } catch (exception& e) { @@ -1806,9 +1830,67 @@ private: State::override_path = wx_to_std (config); } +#ifdef DCPOMATIC_CUCUMBER + if (parser.Found(wxT("cucumber"))) { + _cucumber_bridge = new CucumberBridge(); + _cucumber_bridge->ClickRegisteredButton.connect(bind(&App::cucumber_click_registered_button, this, _1)); + _cucumber_bridge->ClickButtonId.connect(bind(&App::cucumber_click_button_id, this, _1)); + _cucumber_bridge->SelectMenu.connect(bind(&App::cucumber_select_menu, this, _1)); + _cucumber_bridge->Type.connect(bind(&App::cucumber_type, this, _1)); + _cucumber_bridge->AddContentFile.connect(bind(&App::cucumber_add_content_file, this, _1)); + _cucumber_bridge->GetContentList.connect(bind(&App::cucumber_get_content_list, this)); + } +#endif + return true; } + +#ifdef DCPOMATIC_CUCUMBER + void cucumber_add_content_file(string filename) + { + _frame->cucumber_add_content_file(filename); + } + + + std::string cucumber_get_content_list() + { + return _frame->cucumber_get_content_list(); + } + + + void cucumber_click_registered_button(string id) + { + CucumberRegistry::instance()->click_button(id); + } + + void cucumber_click_button_id(wxStandardID id) + { + wxCommandEvent event(wxEVT_BUTTON, id); + if (auto focus = wxWindow::FindFocus()) { + if (auto dialog = focus->GetParent()) { + event.SetEventObject(dialog); + dialog->HandleWindowEvent(event); + } + } + } + + void cucumber_select_menu(string id) + { + if (id == CUCUMBER_MENU_FILE_NEW) { + wxPostEvent(_frame, wxMenuEvent(wxEVT_MENU, ID_file_new)); + } + } + + + void cucumber_type(string text) + { + if (auto ctrl = dynamic_cast<wxTextCtrl*>(wxWindow::FindFocus())) { + ctrl->SetValue(std_to_wx(text)); + } + } +#endif + void report_exception () { try { @@ -2003,6 +2085,9 @@ private: string _film_to_create; string _content_to_add; string _dcp_to_add; +#ifdef DCPOMATIC_CUCUMBER + CucumberBridge* _cucumber_bridge = nullptr; +#endif }; diff --git a/src/tools/wscript b/src/tools/wscript index e6d7c4be1..7eae66636 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -103,10 +103,14 @@ def build(bld): if bld.env.ENABLE_DISK: if bld.env.TARGET_LINUX: uselib += 'POLKIT ' - uselib += 'LWEXT4 NANOMSG ' + uselib += 'LWEXT4 ' + + if bld.env.ENABLE_DISK or bld.env.ENABLE_CUCUMBER: + uselib += 'NANOMSG ' if bld.env.TARGET_WINDOWS_64 or bld.env.TARGET_WINDOWS_32: uselib += 'WINSOCK2 DBGHELP SHLWAPI MSWSOCK BOOST_LOCALE WINSOCK2 OLE32 DSOUND WINMM KSUSER SETUPAPI UUID ' + if bld.env.TARGET_LINUX: uselib += 'DL ' diff --git a/src/wx/content_panel.cc b/src/wx/content_panel.cc index 98eb88ee7..4e3c4d729 100644 --- a/src/wx/content_panel.cc +++ b/src/wx/content_panel.cc @@ -22,6 +22,10 @@ #include "audio_panel.h" #include "content_panel.h" #include "content_timeline_dialog.h" +#ifdef DCPOMATIC_CUCUMBER +#include "cucumber_bridge.h" +#include "cucumber_registry.h" +#endif #include "dcpomatic_button.h" #include "dir_dialog.h" #include "file_dialog.h" @@ -46,6 +50,7 @@ #include "lib/film.h" #include "lib/film_util.h" #include "lib/image_content.h" +#include "lib/job_manager.h" #include "lib/log.h" #include "lib/playlist.h" #include "lib/string_text_file.h" @@ -1021,3 +1026,33 @@ ContentPanel::window() const { return _splitter; } + + +#ifdef DCPOMATIC_CUCUMBER +void +ContentPanel::cucumber_add_content_file (string filename) +{ + add_files({filename}); + auto jm = JobManager::instance(); + while (jm->work_to_do()) { + while (signal_manager->ui_idle()) {} + dcpomatic_sleep_seconds(1); + } +} + + +std::string +ContentPanel::cucumber_get_content_list () +{ + if (!_film) { + return {}; + } + + std::string s; + for (auto content: _film->content()) { + bool alert; + s += wx_to_std(text_for_content(content, alert)) + "\n"; + } + return s; +} +#endif diff --git a/src/wx/content_panel.h b/src/wx/content_panel.h index f99d518a2..5a273888e 100644 --- a/src/wx/content_panel.h +++ b/src/wx/content_panel.h @@ -96,6 +96,11 @@ public: void add_dcp(boost::filesystem::path dcp); void add_folder(boost::filesystem::path folder); +#ifdef DCPOMATIC_CUCUMBER + void cucumber_add_content_file(std::string filename); + std::string cucumber_get_content_list(); +#endif + boost::signals2::signal<void (void)> SelectionChanged; private: diff --git a/src/wx/cucumber_bridge.cc b/src/wx/cucumber_bridge.cc new file mode 100644 index 000000000..3b9f896c9 --- /dev/null +++ b/src/wx/cucumber_bridge.cc @@ -0,0 +1,110 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "cucumber_bridge.h" +#include "lib/nanomsg.h" +#include "lib/dcpomatic_assert.h" +#include <dcp/raw_convert.h> +#include <boost/ref.hpp> +#include <iostream> + + +using std::string; +using boost::optional; +using boost::bind; + + +CucumberBridge::CucumberBridge() + : _nanomsg(true, "cucumber") +{ + +} + + +void +CucumberBridge::listener() +{ + if (!_nanomsg.send(CUCUMBER_BRIDGE_READY "\n", 200)) { + std::cerr << "Could not say hello to cucumber server.\n"; + } + + while (true) { + auto id = _nanomsg.receive(-1); + if (!id) { + continue; + } + + Message message; + message.id = *id; + + if ( + *id == CUCUMBER_BRIDGE_CLICK_REGISTERED_BUTTON || + *id == CUCUMBER_BRIDGE_CLICK_BUTTON_ID || + *id == CUCUMBER_BRIDGE_SELECT_MENU || + *id == CUCUMBER_BRIDGE_TYPE || + *id == CUCUMBER_BRIDGE_ADD_CONTENT_FILE + ) { + auto p = _nanomsg.receive(100); + DCPOMATIC_ASSERT(p); + message.parameter = *p; + } + + boost::mutex::scoped_lock lm(_mutex); + _queue.push_back(message); + } +} + + +void +CucumberBridge::start() +{ + wxTheApp->Bind(wxEVT_IDLE, boost::bind(&CucumberBridge::idle, this)); + _thread = boost::thread(&CucumberBridge::listener, this); +} + + +void +CucumberBridge::idle() +{ + boost::mutex::scoped_lock lm(_mutex); + if (_queue.empty()) { + return; + } + + auto message = _queue.front(); + _queue.pop_front(); + lm.unlock(); + + if (message.id == CUCUMBER_BRIDGE_CLICK_REGISTERED_BUTTON) { + ClickRegisteredButton(*message.parameter); + } else if (message.id == CUCUMBER_BRIDGE_CLICK_BUTTON_ID) { + ClickButtonId(static_cast<wxStandardID>(dcp::raw_convert<int>(*message.parameter))); + } else if (message.id == CUCUMBER_BRIDGE_SELECT_MENU) { + SelectMenu(*message.parameter); + } else if (message.id == CUCUMBER_BRIDGE_TYPE) { + Type(*message.parameter); + } else if (message.id == CUCUMBER_BRIDGE_ADD_CONTENT_FILE) { + AddContentFile(*message.parameter); + } else if (message.id == CUCUMBER_BRIDGE_GET_CONTENT_LIST) { + /* I hope it's OK to send and receive from nanomsg sockets from different threads */ + _nanomsg.send(GetContentList()->c_str(), -1); + } +} diff --git a/src/wx/cucumber_bridge.h b/src/wx/cucumber_bridge.h new file mode 100644 index 000000000..658480855 --- /dev/null +++ b/src/wx/cucumber_bridge.h @@ -0,0 +1,75 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "lib/nanomsg.h" +#include "lib/signaller.h" +#include <boost/signals2.hpp> +#include <boost/thread.hpp> +#include <wx/wx.h> + + +#define CUCUMBER_BRIDGE_READY "ready" +#define CUCUMBER_BRIDGE_CLICK_REGISTERED_BUTTON "click_registered_button" +#define CUCUMBER_BRIDGE_CLICK_BUTTON_ID "click_button_id" +#define CUCUMBER_BRIDGE_SELECT_MENU "select_menu" +#define CUCUMBER_BRIDGE_TYPE "type" +#define CUCUMBER_BRIDGE_CHARACTER "character" +#define CUCUMBER_BRIDGE_ADD_CONTENT_FILE "add_content_file" +#define CUCUMBER_BRIDGE_GET_CONTENT_LIST "get_content_list" + +#define CUCUMBER_MENU_FILE_NEW "file_new" + + +class Nanomsg; + + +class CucumberBridge : public Signaller +{ +public: + CucumberBridge(); + + void start(); + + /* Emitted in the GUI thread */ + boost::signals2::signal<void (std::string)> ClickRegisteredButton; + boost::signals2::signal<void (wxStandardID)> ClickButtonId; + boost::signals2::signal<void (std::string)> SelectMenu; + boost::signals2::signal<void (std::string)> Type; + boost::signals2::signal<void (std::string)> AddContentFile; + boost::signals2::signal<std::string ()> GetContentList; + +private: + void listener(); + void idle(); + + struct Message + { + std::string id; + boost::optional<std::string> parameter; + }; + + boost::thread _thread; + boost::mutex _mutex; + std::list<Message> _queue; + + Nanomsg _nanomsg; +}; + diff --git a/src/wx/cucumber_registry.cc b/src/wx/cucumber_registry.cc new file mode 100644 index 000000000..d4684d67a --- /dev/null +++ b/src/wx/cucumber_registry.cc @@ -0,0 +1,71 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "cucumber_registry.h" +#include "lib/dcpomatic_assert.h" +#include <wx/wx.h> + + +using std::map; +using std::string; + + +CucumberRegistry* CucumberRegistry::_instance = 0; + + +CucumberRegistry * +CucumberRegistry::instance() +{ + if (!_instance) { + _instance = new CucumberRegistry(); + } + + return _instance; +} + + +void +CucumberRegistry::add(string id, wxButton* button) +{ + _buttons[id] = button; +} + + +void +CucumberRegistry::remove(wxButton* button) +{ + for (map<string, wxButton*>::iterator i = _buttons.begin(); i != _buttons.end(); ++i) { + if (i->second == button) { + _buttons.erase(i); + return; + } + } +} + + +void +CucumberRegistry::click_button(string id) +{ + auto i = _buttons.find(id); + DCPOMATIC_ASSERT(i != _buttons.end()); + wxPostEvent(i->second, wxCommandEvent(wxEVT_BUTTON)); +} + diff --git a/src/wx/cucumber_registry.h b/src/wx/cucumber_registry.h new file mode 100644 index 000000000..c08053c48 --- /dev/null +++ b/src/wx/cucumber_registry.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2020 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include <map> +#include <string> + +class wxButton; + +class CucumberRegistry +{ +public: + void add(std::string id, wxButton* button); + void remove(wxButton* button); + void click_button(std::string id); + + static CucumberRegistry* instance(); + +private: + static CucumberRegistry* _instance; + + std::map<std::string, wxButton*> _buttons; +}; + diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index fae02787a..d0e9bf4a7 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -189,3 +189,18 @@ FilmEditor::first_shown () _content_panel->first_shown (); } + +#ifdef DCPOMATIC_CUCUMBER +void +FilmEditor::cucumber_add_content_file(string filename) +{ + _content_panel->cucumber_add_content_file(filename); +} + + +string +FilmEditor::cucumber_get_content_list() +{ + return _content_panel->cucumber_get_content_list(); +} +#endif diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h index 54d639ef5..70ee3be8b 100644 --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@ -65,6 +65,12 @@ public: private: +#ifdef DCPOMATIC_CUCUMBER + friend class DOMFrame; + void cucumber_add_content_file(std::string filename); + std::string cucumber_get_content_list(); +#endif + /* Handle changes to the model */ void film_change(ChangeType, FilmProperty); void film_content_change (ChangeType type, int); diff --git a/src/wx/wscript b/src/wx/wscript index 583fe4bb0..73b92fac9 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -333,7 +333,7 @@ def build(bld): obj.name = 'libdcpomatic2-wx' obj.export_includes = ['..'] - obj.uselib = 'BOOST_FILESYSTEM BOOST_THREAD BOOST_REGEX WXWIDGETS DCP SUB ZIP CXML RTAUDIO ICU AVUTIL ' + obj.uselib = 'BOOST_FILESYSTEM BOOST_THREAD BOOST_REGEX WXWIDGETS DCP SUB ZIP CXML RTAUDIO ICU AVUTIL NANOMSG' if bld.env.TARGET_LINUX: obj.uselib += 'GTK GL GLU ' if bld.env.TARGET_WINDOWS_64 or bld.env.TARGET_WINDOWS_32: @@ -341,6 +341,10 @@ def build(bld): if bld.env.TARGET_OSX: obj.framework = ['CoreAudio', 'OpenGL'] obj.use = 'libdcpomatic2' + + global sources + if bld.env.ENABLE_CUCUMBER: + sources += " cucumber_bridge.cc cucumber_registry.cc" obj.source = sources obj.target = 'dcpomatic2-wx' @@ -80,6 +80,7 @@ def options(opt): opt.add_option('--disable-more-warnings', action='store_true', default=False, help='disable some warnings raised by Xcode 15 with the 2.16 branch') opt.add_option('--c++17', action='store_true', default=False, help='build with C++17 and libxml++-4.0') opt.add_option('--variant', help="build with variant") + opt.add_option('--enable-cucumber', action='store_true', default=False, help='enable cucumber testing; requires Boost process and nanomsg libraries') def configure(conf): conf.load('compiler_cxx') @@ -93,7 +94,7 @@ def configure(conf): conf.env.PANGOMM_API = '2.48' conf.env.CAIROMM_API = '1.16' else: - cpp_std = '11' + cpp_std = '14' if conf.options.enable_cucumber else '11' conf.env.XMLPP_API = '2.6' conf.env.PANGOMM_API = '1.4' conf.env.CAIROMM_API = '1.0' @@ -117,6 +118,7 @@ def configure(conf): conf.env.VARIANT = conf.options.variant if conf.options.variant else "dcpomatic" conf.check_cxx(cxxflags=['-msse', '-mfpmath=sse'], msg='Checking for SSE support', mandatory=False, define_name='SSE') + conf.env.ENABLE_CUCUMBER = conf.options.enable_cucumber # Common CXXFLAGS conf.env.append_value('CXXFLAGS', ['-D__STDC_CONSTANT_MACROS', @@ -180,6 +182,9 @@ def configure(conf): if conf.options.enable_grok: conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_GROK') + if conf.options.enable_cucumber: + conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_CUCUMBER') + if conf.options.use_lld: try: conf.find_program('ld.lld') @@ -455,7 +460,7 @@ def configure(conf): conf.check_cfg(package='polkit-gobject-1', args='--cflags --libs', uselib_store='POLKIT', mandatory=True) # nanomsg - if conf.options.enable_disk: + if conf.options.enable_disk or conf.options.enable_cucumber: if conf.check_cfg(package='nanomsg', args='--cflags --libs', uselib_store='NANOMSG', mandatory=False) is None: conf.check_cfg(package='libnanomsg', args='--cflags --libs', uselib_store='NANOMSG', mandatory=True) if conf.env.TARGET_LINUX: @@ -663,6 +668,8 @@ def configure(conf): conf.recurse('src') if not conf.env.DISABLE_TESTS: conf.recurse('test') + if conf.env.ENABLE_CUCUMBER: + conf.recurse('features') Logs.pprint('YELLOW', '') if conf.env.TARGET_WINDOWS_64 or conf.env.TARGET_WINDOWS_32: @@ -714,6 +721,8 @@ def build(bld): bld.recurse('src') bld.recurse('graphics') bld.recurse('web') + if bld.env.ENABLE_CUCUMBER: + bld.recurse('features') if not bld.env.DISABLE_TESTS: bld.recurse('test') |
