Salome HOME
Test scenario playback made cleaner and less path dependent.
authorabn <adrien.bruneton@cea.fr>
Thu, 14 Apr 2016 09:17:26 +0000 (11:17 +0200)
committerabn <adrien.bruneton@cea.fr>
Thu, 14 Apr 2016 09:17:26 +0000 (11:17 +0200)
This requires GUI branch [abn/exec_sig]

src/MEDCalc/gui/DatasourceController.cxx
src/MEDCalc/gui/MEDModule.cxx
src/MEDCalc/gui/MEDModule.hxx
src/MEDCalc/gui/TestController.cxx
src/MEDCalc/gui/TestController.hxx
src/MEDCalc/test/CMakeLists.txt
src/MEDCalc/test/medcalc_testutils.py.in
src/MEDCalc/test/medfiles/test_scalarmap.med [new file with mode: 0644]
src/MEDCalc/test/scenarios/test_scalarmap.xml
src/MEDCalc/test/test_qttesting.py
src/MEDCalc/test/test_scalarmap.py

index 31a82b48b0632b23d247dc0069b098ecc1ad0c40..ccfe685b27947ae983cef24c7ef5b05d594fddee 100644 (file)
@@ -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()
index b76c6ca879ea7c0782b459c5a9511a057244311f..7b8ffb2e1f495267b9b9312023165e56067c8627 100644 (file)
 #include "PVViewer_ViewModel.h"
 #endif
 
+#include <QTimer>
+
 #include <sstream>
 
 //! 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;
+//}
index 12017f7bea4e67d77f7f253cc2a57b1a54626353..4a2e01851234a9dd92d1565f4ce48ca4198e905a 100644 (file)
@@ -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);
index 9f2bc8dbd4e4e2d3ebfc79f4a45c1e1280f6cad3..78bb46dd2c3f1d7134c33daa12ec1a1dccdcbe32 100644 (file)
 #include <QDir>
 #include <QIcon>
 #include <QTimer>
+#include <QEvent>
+
+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<PlayTestEvent *>(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);
   }
 }
 
index 7ce0b7bf30be824382b350f9e3964ba0f7421635..9f5acfd6f9f0a67d8620527a947fe75f08c37219 100644 (file)
@@ -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_ */
index c17548bd241d5451bf69bcebbc3c49386c29a6fc..2a1326e09dff77faaf030acf66876e7fe811cda9 100644 (file)
@@ -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)
index 59880a57fcd7fb4c35a1a9c534023cbcaa0bf78a..61fe9d12411cb355b22378f3bf912f341ba4eee5 100644 (file)
@@ -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 (file)
index 0000000..063a41d
Binary files /dev/null and b/src/MEDCalc/test/medfiles/test_scalarmap.med differ
index edbc38fb48bf8bdf849c188891b928922d5678ff..42a7210e92fa4adfaa2820e4ecf63ae33b944761 100644 (file)
@@ -3,7 +3,7 @@
   <pqevent object="1STD_TabDesktop0/1QMenuBar0" command="activate" arguments="Fic&amp;hier" />
   <pqevent object="1QMenu0" command="activate" arguments="LAB_LOCK_TEST" />
   <pqevent object="1STD_TabDesktop0/DatasourceToolbar/1QToolButton0" command="activate" arguments="" />
-  <pqevent object="Qt-subapplication-app" command="FilesOpen" arguments="/home/ab205030/agi_BKP.med" />
+  <pqevent object="Qt-subapplication-app" command="FilesOpen" arguments="/tmp/test_scalarmap.med" />
   <pqevent object="1STD_TabDesktop0/objectBrowserDock/objectBrowser/1QtxTreeView0" command="expand" arguments="0.0" />
   <pqevent object="1STD_TabDesktop0/objectBrowserDock/objectBrowser/1QtxTreeView0" command="expand" arguments="0.0.0.0" />
   <pqevent object="1STD_TabDesktop0/objectBrowserDock/objectBrowser/1QtxTreeView0" command="expand" arguments="0.0.0.0.0.0" />
index 390afe5fe270a439af7cce5bff37a461b4828480..165f93978bb5d71a3765950e647e29aa9525695e 100644 (file)
@@ -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)
 
 
index ddf508ee32f763cbe3b7dc66e73dfabac32dd034..0f6b0694d4a7ec999c758cb89312eb3772ba467d 100644 (file)
@@ -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()!