From 5d20e3407611c56597173037d1ebb118659a4f8d Mon Sep 17 00:00:00 2001 From: abn Date: Thu, 14 Apr 2016 11:17:26 +0200 Subject: [PATCH] Test scenario playback made cleaner and less path dependent. This requires GUI branch [abn/exec_sig] --- src/MEDCalc/gui/DatasourceController.cxx | 9 +++ src/MEDCalc/gui/MEDModule.cxx | 25 ++++++- src/MEDCalc/gui/MEDModule.hxx | 2 + src/MEDCalc/gui/TestController.cxx | 70 ++++++++++++++---- src/MEDCalc/gui/TestController.hxx | 18 ++++- src/MEDCalc/test/CMakeLists.txt | 5 ++ src/MEDCalc/test/medcalc_testutils.py.in | 5 ++ src/MEDCalc/test/medfiles/test_scalarmap.med | Bin 0 -> 16114 bytes src/MEDCalc/test/scenarios/test_scalarmap.xml | 2 +- src/MEDCalc/test/test_qttesting.py | 42 +++++++---- src/MEDCalc/test/test_scalarmap.py | 7 +- 11 files changed, 144 insertions(+), 41 deletions(-) create mode 100644 src/MEDCalc/test/medfiles/test_scalarmap.med diff --git a/src/MEDCalc/gui/DatasourceController.cxx b/src/MEDCalc/gui/DatasourceController.cxx index 31a82b48b..ccfe685b2 100644 --- a/src/MEDCalc/gui/DatasourceController.cxx +++ b/src/MEDCalc/gui/DatasourceController.cxx @@ -122,6 +122,11 @@ DatasourceController::addDatasource(const char* filename) event->eventtype = DatasourceEvent::EVENT_ADD_DATASOURCE; event->objectalias = filename; emit datasourceSignal(event); +//#ifdef MED_WITH_QTTESTING +// _dirtyAddDataSource = true; +// while(_dirtyAddDataSource) +// QApplication::processEvents(); +//#endif } // After above data source creation, python console emits a signal, forwarded by workspace, to update the GUI void @@ -138,6 +143,10 @@ DatasourceController::updateTreeViewWithNewDatasource(const MEDCALC::DatasourceH // update Object browser _salomeModule->getApp()->updateObjectBrowser(true); + +//#ifdef MED_WITH_QTTESTING +// _dirtyAddDataSource = false; +//#endif } void DatasourceController::OnAddDatasource() diff --git a/src/MEDCalc/gui/MEDModule.cxx b/src/MEDCalc/gui/MEDModule.cxx index b76c6ca87..7b8ffb2e1 100644 --- a/src/MEDCalc/gui/MEDModule.cxx +++ b/src/MEDCalc/gui/MEDModule.cxx @@ -44,13 +44,16 @@ #include "PVViewer_ViewModel.h" #endif +#include + #include //! The only instance of the reference to engine MED_ORB::MED_Gen_var MEDModule::myEngine; MEDModule::MEDModule() : - SalomeApp_Module("MED"), _studyEditor(0), _datasourceController(0), _workspaceController(0), _presentationController(0), _processingController(0) + SalomeApp_Module("MED"), _studyEditor(0), _datasourceController(0), _workspaceController(0), + _presentationController(0), _processingController(0) { // Note also that we can't use the getApp() function here because // the initialize(...) function has not been called yet. @@ -90,6 +93,15 @@ MEDModule::init() } } +//void MEDModule::onEventLoopStarted() +//{ +// if(!getApp()->isMainEventLoopStarted()) +// { +// QTimer::singleShot(100, this, SLOT(onEventLoopStarted())); +// return; +// } +//} + void MEDModule::initialize( CAM_Application* app ) { @@ -180,6 +192,10 @@ MEDModule::activateModule( SUIT_Study* theStudy ) _presentationController->showDockWidgets(true); //this->setDockLayout(StandardApp_Module::DOCKLAYOUT_LEFT_VLARGE); + // Mark the start of the main event loop - important for test playback: +// QObject::connect(getApp(), SIGNAL(activated(SUIT_Application *)), this, SLOT(onEventLoopStarted(SUIT_Application *))); +// QTimer::singleShot(0, this, SLOT(onEventLoopStarted())); + // return the activation status return bOk; } @@ -370,5 +386,12 @@ MEDModule::onDblClick(const QModelIndex& index) void MEDModule::requestSALOMETermination() const { + STDLOG("Requesting SALOME termination!!"); SUIT_Session::session()->closeSession( SUIT_Session::DONT_SAVE, 1 ); // killServers = True } + + +//bool MEDModule::hasMainEventLoopStarted() const +//{ +// return _eventLoopStarted; +//} diff --git a/src/MEDCalc/gui/MEDModule.hxx b/src/MEDCalc/gui/MEDModule.hxx index 12017f7be..4a2e01851 100644 --- a/src/MEDCalc/gui/MEDModule.hxx +++ b/src/MEDCalc/gui/MEDModule.hxx @@ -85,6 +85,8 @@ public: void requestSALOMETermination() const; +// bool hasMainEventLoopStarted() const; + public slots: virtual bool activateModule(SUIT_Study* theStudy); virtual bool deactivateModule(SUIT_Study* theStudy); diff --git a/src/MEDCalc/gui/TestController.cxx b/src/MEDCalc/gui/TestController.cxx index 9f2bc8dbd..78bb46dd2 100644 --- a/src/MEDCalc/gui/TestController.cxx +++ b/src/MEDCalc/gui/TestController.cxx @@ -45,11 +45,22 @@ #include #include #include +#include + +class PlayTestEvent: public QEvent { +public: + PlayTestEvent(QEvent::Type type, const std::string & filename): QEvent(type), _filename(filename) {} + virtual ~PlayTestEvent() {} + const std::string _filename; +}; TestController::TestController(MEDModule* mod): _salomeModule(mod), _desk(SUIT_Session::session()->activeApplication()->desktop()), - _tester(0), _lock_action(0) + _tester(0), _lock_action(0), + _quitEventType(QEvent::registerEventType()), + _playEventType(QEvent::registerEventType()), + _aboutToPlayTest(false) { STDLOG("Creating a TestController"); _tester = new pqTestUtility(_desk); @@ -120,19 +131,12 @@ void TestController::onPlayTest() _tester->playTests(fileName); } -void TestController::onPlayTestScenario() -{ - STDLOG("About to play test " << _test_scenario.toStdString()); - _tester->playTests(_test_scenario); - STDLOG("Done playing test " << _test_scenario.toStdString()); -} - -void TestController::onLockViewSize() +void TestController::onLockViewSize() const { pqTestingReaction::lockViewSize(_lock_action->isChecked()); } -void TestController::onTakeSnapshot() +void TestController::onTakeSnapshot() const { pqSaveScreenshotReaction::saveScreenshot(); } @@ -140,10 +144,10 @@ void TestController::onTakeSnapshot() void TestController::onRequestTermination() { // Check if test playing - if (_tester->playingTest()) + if (_tester->playingTest() || _aboutToPlayTest) { - STDLOG("Termination requested, but test still playing ..."); - QTimer::singleShot(200, this, SLOT(onRequestTermination())); + QEvent * e = new QEvent((QEvent::Type)_quitEventType); + QApplication::postEvent(this, e); } else { @@ -151,6 +155,38 @@ void TestController::onRequestTermination() } } +void +TestController::customEvent(QEvent * event) +{ + if (event->type() == _quitEventType) + { + if(!_salomeModule->getApp()->isMainEventLoopStarted()) + // Repost (=delay) + QApplication::postEvent(this, new QEvent((QEvent::Type)_quitEventType)); + else + onRequestTermination(); + } + else if (event->type() == _playEventType) + { + PlayTestEvent * e = dynamic_cast(event); + if (e) + { +// // Wait for main event loop to start: + if(!_salomeModule->getApp()->isMainEventLoopStarted()) + // Repost (=delay) + QApplication::postEvent(this, new PlayTestEvent((QEvent::Type)_playEventType, e->_filename)); + else + { + STDLOG("About to play test " << e->_filename); + _tester->playTests(e->_filename.c_str()); + _aboutToPlayTest = false; + STDLOG("Done playing test " << e->_filename); + } + } + } + else + { QObject::customEvent(event); } +} void TestController::processWorkspaceEvent(const MEDCALC::MedEvent* event) @@ -159,13 +195,15 @@ TestController::processWorkspaceEvent(const MEDCALC::MedEvent* event) /* [ABN] Post an event. Indeed, calling the function directly would prevent the proper refresh of the * GUI which also needs to go through the MED event loop (WorkspaceController::processWorkspaceEvent) */ - _test_scenario = QString(event->filename); - QTimer::singleShot(100, this, SLOT(onPlayTestScenario())); + _aboutToPlayTest = true; // to prevent an early quit! + PlayTestEvent * e = new PlayTestEvent((QEvent::Type)_playEventType, std::string(event->filename)); + QApplication::postEvent(this, e); } else if ( event->type == MEDCALC::EVENT_QUIT_SALOME ) { // [ABN] again: post as an event to give a chance to other events (piled up by test // scenarios for example) to execute: - QTimer::singleShot(200, this, SLOT(onRequestTermination())); + QEvent * e = new QEvent((QEvent::Type)_quitEventType); + QApplication::postEvent(this, e); } } diff --git a/src/MEDCalc/gui/TestController.hxx b/src/MEDCalc/gui/TestController.hxx index 7ce0b7bf3..9f5acfd6f 100644 --- a/src/MEDCalc/gui/TestController.hxx +++ b/src/MEDCalc/gui/TestController.hxx @@ -40,14 +40,19 @@ public: void createActions(); + + +protected: + virtual void customEvent(QEvent * event); + public slots: void processWorkspaceEvent(const MEDCALC::MedEvent* event); void onRecordTest(); void onPlayTest(); - void onPlayTestScenario(); - void onLockViewSize(); - void onTakeSnapshot(); +// void onPlayTestFile(); + void onLockViewSize() const; + void onTakeSnapshot() const; void onRequestTermination(); protected: @@ -56,7 +61,12 @@ protected: pqTestUtility * _tester; QAction * _lock_action; - QString _test_scenario; + +private: + const int _playEventType; + const int _quitEventType; + + bool _aboutToPlayTest; }; #endif /* SRC_MEDCALC_GUI_TESTCONTROLLER_HXX_ */ diff --git a/src/MEDCalc/test/CMakeLists.txt b/src/MEDCalc/test/CMakeLists.txt index c17548bd2..2a1326e09 100644 --- a/src/MEDCalc/test/CMakeLists.txt +++ b/src/MEDCalc/test/CMakeLists.txt @@ -32,6 +32,10 @@ SET(_test_baselines baselines/test_scalarmap.png ) +SET(_test_files + medfiles/test_scalarmap.med +) + # Test rules - the test MEDCalcQtTesting must be run after install (since it starts SALOME) ADD_TEST(MEDCalcQtTesting ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_qttesting.py) @@ -41,3 +45,4 @@ SALOME_INSTALL_SCRIPTS(${CMAKE_CURRENT_BINARY_DIR}/medcalc_testutils.py ${SALOME INSTALL(FILES ${_test_scenarii} DESTINATION ${SALOME_MED_INSTALL_RES_DATA}/tests/scenarios) INSTALL(FILES ${_test_baselines} DESTINATION ${SALOME_MED_INSTALL_RES_DATA}/tests/baselines) +INSTALL(FILES ${_test_files} DESTINATION ${SALOME_MED_INSTALL_RES_DATA}/tests/medfiles) diff --git a/src/MEDCalc/test/medcalc_testutils.py.in b/src/MEDCalc/test/medcalc_testutils.py.in index 59880a57f..61fe9d124 100644 --- a/src/MEDCalc/test/medcalc_testutils.py.in +++ b/src/MEDCalc/test/medcalc_testutils.py.in @@ -34,3 +34,8 @@ def GetScenarioDir(): def GetBaselineDir(): relativeDir = "@SALOME_MED_INSTALL_RES_DATA@/tests/baselines" return os.path.join(__getRootDir(), relativeDir) + +def GetMEDFileDir(): + relativeDir = "@SALOME_MED_INSTALL_RES_DATA@/tests/medfiles" + return os.path.join(__getRootDir(), relativeDir) + diff --git a/src/MEDCalc/test/medfiles/test_scalarmap.med b/src/MEDCalc/test/medfiles/test_scalarmap.med new file mode 100644 index 0000000000000000000000000000000000000000..063a41dce9c8fbb3a76a5d95862b045c99ae491d GIT binary patch literal 16114 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 - + diff --git a/src/MEDCalc/test/test_qttesting.py b/src/MEDCalc/test/test_qttesting.py index 390afe5fe..165f93978 100644 --- a/src/MEDCalc/test/test_qttesting.py +++ b/src/MEDCalc/test/test_qttesting.py @@ -18,7 +18,7 @@ # # Author: A. Bruneton (CEA) -import unittest, os +import unittest, os, shutil from posixpath import basename class MEDGUITest(unittest.TestCase): @@ -42,30 +42,22 @@ class MEDGUITest(unittest.TestCase): """ Return name of the test being currently executed. """ return self.id().split(".")[-1] - def launchSalomeWithScript(self, scriptname, baseline): + def launchSalomeWithScript(self, scriptname): """ TODO: review this - what is the nicest way to launch SALOME GUI from a Python script? """ import shutil, subprocess from medcalc_testutils import GetScriptDir # TODO: review this! salomeCommand = os.path.join(os.environ.get("KERNEL_ROOT_DIR", ""), "bin", "salome", "runSalome.py") + args = "args:%s" % self._tmpDir pth = os.path.join(GetScriptDir(), scriptname) - # Remove a potentially already present image file from the tmp directory: - gen_image = os.path.join("/tmp", baseline) - try: - shutil.rmtree(gen_image) - except OSError: - pass # Launch SALOME with the test script: - status = subprocess.call([salomeCommand, pth]) + status = subprocess.call([salomeCommand, pth, args]) if status: raise Exception("SALOME exited abnormally for this test!") - try: - # Move generated image to the temporary test directory - ideally test should produce image there directly ... - shutil.move(gen_image, self._tmpDir) - except IOError: - raise Exception("Test script didn't produce expected image '%s'!" % gen_image) def compareSnapshot(self, basename): + """ Compare the screenshot in the current temporary test directory with the reference baseline. + Assert if not matching. """ import filecmp from medcalc_testutils import GetBaselineDir @@ -82,13 +74,31 @@ class MEDGUITest(unittest.TestCase): self.assertTrue(ret, "[%s] -- Failed screenshot equality, or unable to open baseline file - directory is kept alive: %s" % (self.getTestName(), self._tmpDir)) return ret + def prepareScenario(self, scenario, baseline, med_file): + """ Copy scenario to current temporary test dir and substitute paths inside """ + from medcalc_testutils import GetScenarioDir, GetMEDFileDir + scen_path = os.path.join(GetScenarioDir(), scenario) + scen_pth2 = os.path.join(self._tmpDir, scenario) + try: + shutil.copy(scen_path, scen_pth2) + except IOError: + raise Exception("Could not copy test scenario '%s' to local test directory!" % scen_path) + with open(scen_pth2,'r') as f: + filedata = f.read() + filedata = filedata.replace("/tmp/%s" % baseline, "%s/%s" % (self._tmpDir, baseline)) + filedata = filedata.replace("/tmp/%s" % med_file, os.path.join(GetMEDFileDir(), med_file)) + with open(scen_pth2,'w') as f: + f.write(filedata) + ## ## Now the tests themselves ## - def testScalarMap(self): baseline = "test_scalarmap.png" - self.launchSalomeWithScript("test_scalarmap.py", baseline) + med_file = "test_scalarmap.med" # will change + scenario = "test_scalarmap.xml" + self.prepareScenario(scenario, baseline, med_file) + self.launchSalomeWithScript("test_scalarmap.py") self.compareSnapshot(baseline) diff --git a/src/MEDCalc/test/test_scalarmap.py b/src/MEDCalc/test/test_scalarmap.py index ddf508ee3..0f6b0694d 100644 --- a/src/MEDCalc/test/test_scalarmap.py +++ b/src/MEDCalc/test/test_scalarmap.py @@ -23,7 +23,7 @@ This script is to be passed as an argument of the ./salome command and will be e Python console. """ -import os +import os, sys import SalomePyQt from medcalc_testutils import GetScenarioDir @@ -31,6 +31,7 @@ sgPyQt = SalomePyQt.SalomePyQt() sgPyQt.activateModule('MED') import medcalc # After module activation !! -medcalc.PlayQtTestingScenario(os.path.join(GetScenarioDir(), 'test_scalarmap.xml')) +localTestDir = sys.argv[1] +medcalc.PlayQtTestingScenario(os.path.join(localTestDir, 'test_scalarmap.xml')) -medcalc.RequestSALOMETermination() # not equivalent to quit()! +#medcalc.RequestSALOMETermination() # not equivalent to quit()! -- 2.39.2