summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2025-01-26 02:18:21 +0100
committerCarl Hetherington <cth@carlh.net>2025-01-26 02:21:16 +0100
commit69fabac6d2219c07cfdcd9a8fe6e92bf4669be46 (patch)
tree1627020b59fb7380f6e7a4d5ba42de8f1955242c
parent811e70bdd09a618f8b0a512950a7f3254c591614 (diff)
Add a simple cucumber test.
-rw-r--r--DEVELOP.md29
-rw-r--r--features/add_still_image.feature11
-rw-r--r--features/step_definitions/cucumber.wire3
-rw-r--r--features/step_definitions/dcpomatic.cc146
-rw-r--r--features/support/env.rb0
-rw-r--r--features/wscript40
-rwxr-xr-xrun/cucumber11
-rwxr-xr-xrun/cucumber_server9
-rw-r--r--src/lib/wscript8
-rw-r--r--src/tools/dcpomatic.cc89
-rw-r--r--src/tools/wscript6
-rw-r--r--src/wx/content_panel.cc35
-rw-r--r--src/wx/content_panel.h5
-rw-r--r--src/wx/cucumber_bridge.cc110
-rw-r--r--src/wx/cucumber_bridge.h75
-rw-r--r--src/wx/cucumber_registry.cc71
-rw-r--r--src/wx/cucumber_registry.h41
-rw-r--r--src/wx/film_editor.cc15
-rw-r--r--src/wx/film_editor.h6
-rw-r--r--src/wx/wscript6
-rw-r--r--wscript13
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'
diff --git a/wscript b/wscript
index 448f390fc..3268f1251 100644
--- a/wscript
+++ b/wscript
@@ -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')