瀏覽代碼

Integrate the QtTesting framework into CTK: ctkQtTesting

Benjamin Long 14 年之前
父節點
當前提交
6c64ee6c1b

+ 1 - 1
CMake/ctkMacroBuildLib.cmake

@@ -29,7 +29,7 @@
 macro(ctkMacroBuildLib)
   ctkMacroParseArguments(MY
     "NAME;EXPORT_DIRECTIVE;SRCS;MOC_SRCS;UI_FORMS;INCLUDE_DIRECTORIES;TARGET_LIBRARIES;RESOURCES;LIBRARY_TYPE"
-    ""
+    "ENABLE_QTTESTING"
     ${ARGN}
     )
 

+ 80 - 0
CMakeExternals/QtTesting.cmake

@@ -0,0 +1,80 @@
+#
+# QtTesting
+#
+set(QtTesting_DEPENDS)
+if(CTK_USE_QTTESTING)
+
+  # Sanity checks
+  if(DEFINED QtTesting_DIR AND NOT EXISTS ${QtTesting_DIR})
+    message(FATAL_ERROR "QtTesting_DIR variable is defined but corresponds to non-existing directory")
+  endif()
+
+  set(proj QtTesting)
+  set(proj_DEPENDENCIES)
+  
+  list(APPEND CTK_DEPENDENCIES ${proj})
+
+  set(QtTesting_DEPENDS ${proj})
+  
+  set(${QtTesting_enabling_variable}_INCLUDE_DIRS QtTesting_INCLUDE_DIRS)
+  set(${QtTesting_enabling_variable}_FIND_PACKAGE_CMD QtTesting)
+  
+  if(CTK_SUPERBUILD)
+
+    if(NOT DEFINED QtTesting_DIR)
+
+      set(revision_tag 3454d26f1902571caaae704bc69176454cdbb4ea)
+      if(${proj}_REVISION_TAG)
+        set(revision_tag ${${proj}_REVISION_TAG})
+      endif()
+
+      set(location_args )
+      if(${proj}_URL)
+        set(location_args URL ${${proj}_URL})
+      elseif(${proj}_GIT_REPOSITORY)
+        set(location_args GIT_REPOSITORY ${${proj}_GIT_REPOSITORY}
+                          GIT_TAG ${revision_tag})
+      else()
+        set(location_args GIT_REPOSITORY "${git_protocol}://github.com/benjaminlong/QtTesting.git"
+                          GIT_TAG ${revision_tag})
+      endif()
+
+      # Set CMake OSX variable to pass down the external project
+      set(CMAKE_OSX_EXTERNAL_PROJECT_ARGS)
+      if(APPLE)
+        list(APPEND CMAKE_OSX_EXTERNAL_PROJECT_ARGS
+          -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}
+          -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT}
+          -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET})
+      endif()
+
+      message(STATUS "Adding project:${proj}")
+      ExternalProject_Add(${proj}
+        SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}
+        BINARY_DIR ${proj}-build
+        PREFIX ${proj}${ep_suffix}
+        ${location_args}
+        CMAKE_GENERATOR ${gen}
+        INSTALL_COMMAND ""
+        UPDATE_COMMAND ""
+        CMAKE_ARGS
+          ${ep_common_args}
+          -DBUILD_SHARED_LIBS:BOOL=ON
+          -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
+        DEPENDS
+          ${proj_DEPENDENCIES}
+        )
+      set(QtTesting_DIR ${CMAKE_BINARY_DIR}/${proj}-build)
+
+      # Since QtTesting is statically build, there is not need to add its corresponding
+      # library output directory to CTK_EXTERNAL_LIBRARY_DIRS
+
+    else()
+      ctkMacroEmptyExternalproject(${proj} "${proj_DEPENDENCIES}")
+    endif()
+
+    list(APPEND CTK_SUPERBUILD_EP_VARS QtTesting_DIR:PATH)
+    
+  endif()
+
+endif()

+ 20 - 0
CMakeLists.txt

@@ -243,6 +243,12 @@ if(BUILD_TESTING)
 endif()
 
 #-----------------------------------------------------------------------------
+# QtTesting
+#
+option(CTK_USE_QTTESTING "Enable/Disable QtTesting" OFF)
+mark_as_advanced(CTK_USE_QTTESTING)
+
+#-----------------------------------------------------------------------------
 # Coverage
 #
 option(WITH_COVERAGE "Enable/Disable coverage" OFF)
@@ -461,6 +467,14 @@ ctk_lib_option(Visualization/VTK/Widgets
 #ctk_lib_option(Visualization/XIP
 #               "Build the XIP library" OFF)
 
+# Save the set of enabled libs in a cache file
+set(_enabled_libs)
+foreach(_lib ${CTK_LIBS})
+  if(CTK_LIB_${_lib})
+    list(APPEND _enabled_libs ${_lib})
+  endif()
+endforeach()
+set(CTK_ENABLED_LIBS ${_enabled_libs} CACHE INTERNAL "" FORCE)
 #-----------------------------------------------------------------------------
 # CTK Applications - Use ON or OFF to indicate if the application should be built by default
 #
@@ -722,6 +736,7 @@ ctkFunctionGenerateProjectXml(${CTK_BINARY_DIR} ${PROJECT_NAME} "${target_direct
 #-----------------------------------------------------------------------------
 set(CTK_POSSIBLE_DEPENDENCIES
   CTKData
+  QtTesting
   Log4Qt
   KWStyle
   VTK
@@ -837,6 +852,11 @@ foreach(lib ${CTK_LIBS})
 endforeach()
 
 #-----------------------------------------------------------------------------
+if(CTK_USE_QTTESTING)
+  add_subdirectory(Libs/QtTesting)
+endif()
+
+#-----------------------------------------------------------------------------
 # Add CTK plugin subdirectories
 #
 foreach(plugin ${CTK_PLUGINS})

+ 2 - 0
Libs/Core/CMakeLists.txt

@@ -72,6 +72,8 @@ set(KIT_SRCS
   ctkWorkflowStep.cpp
   ctkWorkflowStep_p.h
   ctkWorkflowTransitions.h
+  ctkSetName.cpp
+  ctkSetName.h
   )
 
 if(HAVE_BFD)

+ 58 - 0
Libs/Core/ctkSetName.cpp

@@ -0,0 +1,58 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+/*=========================================================================
+
+   Program: ParaView
+   Module:    pqSetName.cxx
+
+   Copyright (c) 2005-2008 Sandia Corporation, Kitware Inc.
+   All rights reserved.
+
+   ParaView is a free software; you can redistribute it and/or modify it
+   under the terms of the ParaView license version 1.2. 
+
+   See License_v1.2.txt for the full ParaView license.
+   A copy of this license can be obtained by contacting
+   Kitware Inc.
+   28 Corporate Drive
+   Clifton Park, NY 12065
+   USA
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+=========================================================================*/
+
+#include "ctkSetName.h"
+
+//-----------------------------------------------------------------------------
+ctkSetName::ctkSetName(const QString& name)
+  : Name(name)
+{
+}

+ 85 - 0
Libs/Core/ctkSetName.h

@@ -0,0 +1,85 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+/*=========================================================================
+
+   Program: ParaView
+   Module:    pqSetName.h
+
+   Copyright (c) 2005-2008 Sandia Corporation, Kitware Inc.
+   All rights reserved.
+
+   ParaView is a free software; you can redistribute it and/or modify it
+   under the terms of the ParaView license version 1.2. 
+
+   See License_v1.2.txt for the full ParaView license.
+   A copy of this license can be obtained by contacting
+   Kitware Inc.
+   28 Corporate Drive
+   Clifton Park, NY 12065
+   USA
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+=========================================================================*/
+
+#ifndef __ctkSetName_h
+#define __ctkSetName_h
+
+// Qt includes
+#include <QString>
+
+// CTK includes
+#include "ctkCoreExport.h"
+
+/**
+  Using ctkSetName, you can create and initialize Qt objects without having to create a bunch of temporaries:
+
+  \code
+  menu->addAction("Open") << ctkSetName("FileOpenMenu");
+  \endcode
+
+  \sa pqSetData, pqConnect
+*/
+struct CTK_CORE_EXPORT ctkSetName
+{
+  ctkSetName(const QString& Name);
+  const QString Name;
+};
+
+//-----------------------------------------------------------------------------
+/// Sets a Qt object's name
+template<typename T>
+T* operator<<(T* LHS, const ctkSetName& RHS)
+{
+  LHS->setObjectName(RHS.Name);
+  return LHS;
+}
+
+#endif

+ 29 - 0
Libs/QtTesting/CMake/ctkQtTesting.cmake

@@ -0,0 +1,29 @@
+
+macro(ctkQtTesting SRCS_OUTPUT_VAR MOC_CPP_OUTPUT_VAR UI_FORMS_OUTPUT_VAR)
+  # Do not export symbol in ctkEventTranslatorPlayerWidget class
+  add_definitions(-DNO_SYMBOL_EXPORT)
+
+  include_directories(
+    ${CTK_SOURCE_DIR}/Libs/QtTesting
+    ${CTK_BINARY_DIR}/Libs/QtTesting
+    )
+
+  message ("called: ${SRCS_OUTPUT_VAR}")
+  list(APPEND ${SRCS_OUTPUT_VAR}
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/ctkEventTranslatorPlayerWidget.h
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/ctkEventTranslatorPlayerWidget.cpp
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/ctkXMLEventSource.h
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/ctkXMLEventSource.cpp
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/ctkXMLEventObserver.h
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/ctkXMLEventObserver.cpp
+    )
+  list(APPEND ${MOC_CPP_OUTPUT_VAR}
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/ctkEventTranslatorPlayerWidget.h
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/ctkXMLEventSource.h
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/ctkXMLEventObserver.h
+    )
+  list(APPEND ${UI_FORMS_OUTPUT_VAR}
+    ${CTK_SOURCE_DIR}/Libs/QtTesting/Resources/UI/ctkEventTranslatorPlayerWidget.ui
+    )
+
+endmacro()

+ 187 - 0
Libs/QtTesting/CMakeLists.txt

@@ -0,0 +1,187 @@
+project(CTKQtTesting)
+
+#
+# 3rd party dependencies
+#
+find_package(QtTesting REQUIRED)
+
+#
+# See CTK/CMake/ctkMacroBuildLib.cmake for details
+#
+
+set(KIT_export_directive "CTK_QTTESTING_EXPORT")
+
+set(CTK_LIBS_WIDGET_DIR)
+
+# TODO The following code used to create CTK_ENABLED_LIBNAMES list could be
+#      moved in the main CMakeLists.txt
+set(CTK_ENABLED_LIBNAMES)
+foreach(enabled_lib ${CTK_ENABLED_LIBS})
+  string(REPLACE "/" "" lib_name_no_slash ${enabled_lib})
+  list(APPEND CTK_ENABLED_LIBNAMES CTK${lib_name_no_slash})
+  string(REGEX MATCHALL "Widgets" CTK_LIB_WIDGET ${lib_name_no_slash})
+  if(CTK_LIB_WIDGET)
+    list(APPEND CTK_LIBS_WIDGET_DIR CTK${lib_name_no_slash})
+  endif()
+endforeach()
+
+# Recover All the event player/translator headers
+set(CTK_HEADERS_EVENT_PLAYERS)
+set(CTK_HEADERS_EVENT_TRANSLATORS)
+
+foreach(CTK_LIB_WIDGET_DIR ${CTK_LIBS_WIDGET_DIR})
+  file(GLOB HEADERS RELATIVE ${${CTK_LIB_WIDGET_DIR}_SOURCE_DIR} "${${CTK_LIB_WIDGET_DIR}_SOURCE_DIR}/*.h")
+  foreach(HEADER ${HEADERS})
+    string(REGEX MATCHALL "EventPlayer" HEADER_PLAYER ${HEADER})
+    string(REGEX MATCHALL "EventTranslator" HEADER_TRANSLATOR ${HEADER})
+    if(HEADER_PLAYER)
+      list(APPEND CTK_HEADERS_EVENT_PLAYERS ${HEADER})
+    endif()
+    if(HEADER_TRANSLATOR)
+      list(APPEND CTK_HEADERS_EVENT_TRANSLATORS ${HEADER})
+    endif()
+  endforeach()
+endforeach()
+
+if(CTK_HEADERS_EVENT_PLAYERS)
+  list(SORT CTK_HEADERS_EVENT_PLAYERS)
+endif()
+if(CTK_HEADERS_EVENT_TRANSLATORS)
+  list(SORT CTK_HEADERS_EVENT_TRANSLATORS)
+endif()
+
+# Define the C++ code for the file ctkQtTestingUtility
+# Define the headers
+# Define the methode addWidgetEventTranslator/Player()
+set(CTK_ADD_HEADER_EVENT_PLAYERS "// Event player includes\n")
+#set(CTK_ADD_WIDGET_EVENT_PLAYERS)
+foreach(CTK_HEADER_EVENT_PLAYERS ${CTK_HEADERS_EVENT_PLAYERS})
+  set(CTK_ADD_HEADER_EVENT_PLAYERS "${CTK_ADD_HEADER_EVENT_PLAYERS}#include <${CTK_HEADER_EVENT_PLAYERS}>\n")
+  get_filename_component(CTK_HEADER_EVENT_PLAYER_NAME ${CTK_HEADER_EVENT_PLAYERS} NAME_WE)
+
+  set(is_special_player FALSE)
+
+  string(COMPARE EQUAL "ctkVTKRenderViewEventPlayer" ${CTK_HEADER_EVENT_PLAYER_NAME} is_special_player)
+  if(is_special_player)
+    set(CTK_ADD_WIDGET_EVENT_PLAYERS "${CTK_ADD_WIDGET_EVENT_PLAYERS}  this->eventPlayer()->addWidgetEventPlayer(new ${CTK_HEADER_EVENT_PLAYER_NAME}(\"QVTKWidget\"));\n")
+  endif()
+
+  if(NOT is_special_player)
+    # Explicitly skip this player so that it could be added first in the list of players to register.
+    string(COMPARE EQUAL "ctkFileDialogEventPlayer" ${CTK_HEADER_EVENT_PLAYER_NAME} is_special_player)
+  endif()
+
+  if(NOT is_special_player)
+    set(CTK_ADD_WIDGET_EVENT_PLAYERS "${CTK_ADD_WIDGET_EVENT_PLAYERS}  this->eventPlayer()->addWidgetEventPlayer(new ${CTK_HEADER_EVENT_PLAYER_NAME}());\n")
+  endif()
+endforeach()
+
+set(CTK_ADD_WIDGET_EVENT_PLAYERS "${CTK_ADD_WIDGET_EVENT_PLAYERS}  this->eventPlayer()->addWidgetEventPlayer(new ctkFileDialogEventPlayer(util));\n")
+
+set(CTK_ADD_HEADER_EVENT_TRANSLATORS "// Event translator includes\n")
+#set(CTK_ADD_WIDGET_EVENT_TRANSLATORS)
+foreach(CTK_HEADER_EVENT_TRANSLATORS ${CTK_HEADERS_EVENT_TRANSLATORS})
+  set(CTK_ADD_HEADER_EVENT_TRANSLATORS "${CTK_ADD_HEADER_EVENT_TRANSLATORS}#include <${CTK_HEADER_EVENT_TRANSLATORS}>\n")
+  get_filename_component(CTK_HEADER_EVENT_TRANSLATOR_NAME ${CTK_HEADER_EVENT_TRANSLATORS} NAME_WE)
+
+  set(is_special_translator FALSE)
+
+  string(COMPARE EQUAL "ctkVTKRenderViewEventTranslator" ${CTK_HEADER_EVENT_TRANSLATOR_NAME} is_special_translator)
+  if(is_special_translator)
+    set(CTK_ADD_WIDGET_EVENT_TRANSLATORS "${CTK_ADD_WIDGET_EVENT_TRANSLATORS}  this->eventTranslator()->addWidgetEventTranslator(new ${CTK_HEADER_EVENT_TRANSLATOR_NAME}(\"QVTKWidget\"));\n")
+  endif()
+
+  if(NOT is_special_translator)
+    # Explicitly skip this translator so that it could be added first in the list of translators to register.
+    string(COMPARE EQUAL "ctkFileDialogEventTranslator" ${CTK_HEADER_EVENT_TRANSLATOR_NAME} is_special_translator)
+  endif()
+
+  if(NOT is_special_translator)
+    set(CTK_ADD_WIDGET_EVENT_TRANSLATORS "${CTK_ADD_WIDGET_EVENT_TRANSLATORS}  this->eventTranslator()->addWidgetEventTranslator(new ${CTK_HEADER_EVENT_TRANSLATOR_NAME}());\n")
+  endif()
+endforeach()
+
+set(CTK_ADD_WIDGET_EVENT_TRANSLATORS "${CTK_ADD_WIDGET_EVENT_TRANSLATORS}  this->eventTranslator()->addWidgetEventTranslator(new ctkFileDialogEventTranslator(util));\n")
+
+set(KIT_INCLUDE_DIRECTORIES
+  ${QtTesting_INCLUDE_DIRS}
+  ${CTK_SUPERBUILD_BINARY_DIR} # For ctkConfig.h
+  )
+
+foreach(libname ${CTK_ENABLED_LIBNAMES})
+  list(APPEND KIT_INCLUDE_DIRECTORIES
+    ${${libname}_SOURCE_DIR}
+    ${${libname}_BINARY_DIR}
+    )
+endforeach()
+
+configure_file(
+  ctkQtTestingUtility.cpp.in
+  ${CMAKE_CURRENT_BINARY_DIR}/ctkQtTestingUtility.cpp
+  )
+
+# Source files
+set(KIT_SRCS
+  ctkEventTranslatorPlayerWidget.cpp
+  ctkEventTranslatorPlayerWidget.h
+  ${CMAKE_CURRENT_BINARY_DIR}/ctkQtTestingUtility.cpp
+  ctkQtTestingUtility.h
+  ctkXMLEventObserver.cpp
+  ctkXMLEventObserver.h
+  ctkXMLEventSource.cpp
+  ctkXMLEventSource.h
+  )
+
+# Header that should run through moc
+set(KIT_MOC_SRCS
+  ctkEventTranslatorPlayerWidget.h
+  ctkQtTestingUtility.h
+  ctkXMLEventObserver.h
+  ctkXMLEventSource.h
+  )
+
+# UI files
+set(KIT_UI_FORMS
+  Resources/UI/ctkEventTranslatorPlayerWidget.ui
+)
+
+# Resources
+set(KIT_resources
+)
+
+# dependencie dynamic from all the libraries with Widgets + QtTesting
+set(KIT_target_libraries QtTesting)
+
+foreach(libname ${CTK_ENABLED_LIBNAMES})
+  if(${libname} MATCHES "Widgets$")
+    list(APPEND KIT_target_libraries ${libname})
+  endif()
+endforeach()
+
+#message("${KIT_target_libraries}")
+
+ctkMacroBuildLib(
+  NAME ${PROJECT_NAME}
+  EXPORT_DIRECTIVE ${KIT_export_directive}
+  SRCS ${KIT_SRCS}
+  MOC_SRCS ${KIT_MOC_SRCS}
+  UI_FORMS ${KIT_UI_FORMS}
+  INCLUDE_DIRECTORIES ${KIT_INCLUDE_DIRECTORIES}
+  TARGET_LIBRARIES ${KIT_target_libraries}
+  RESOURCES ${KIT_resources}
+  LIBRARY_TYPE ${CTK_LIBRARY_MODE}
+  )
+
+if(CTK_WRAP_PYTHONQT_FULL OR CTK_WRAP_PYTHONQT_LIGHT)
+  ctkMacroBuildLibWrapper(
+    TARGET ${PROJECT_NAME}
+    SRCS ${KIT_SRCS}
+    WRAPPER_LIBRARY_TYPE ${CTK_LIBRARY_MODE}
+    )
+endif()
+
+# Testing
+#if(BUILD_TESTING)
+#  add_subdirectory(Testing)
+#endif()
+

+ 88 - 0
Libs/QtTesting/Resources/UI/ctkEventTranslatorPlayerWidget.ui

@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ctkEventTranslatorPlayerWidget</class>
+ <widget class="QMainWindow" name="ctkEventTranslatorPlayerWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>494</width>
+    <height>331</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QHBoxLayout" name="horizontalLayout">
+    <item>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QLabel" name="TestCaseLabel">
+        <property name="text">
+         <string>TestCase :</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QComboBox" name="TestCaseComboBox"/>
+      </item>
+      <item>
+       <spacer name="verticalSpacer">
+        <property name="orientation">
+         <enum>Qt::Vertical</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>20</width>
+          <height>40</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QPushButton" name="TranslatorButton">
+        <property name="text">
+         <string>Record</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="PlayerButton">
+        <property name="text">
+         <string>Playback</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <widget class="QStackedWidget" name="stackedWidget">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>494</width>
+     <height>23</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 452 - 0
Libs/QtTesting/ctkEventTranslatorPlayerWidget.cpp

@@ -0,0 +1,452 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+// QT includes
+#include <QDebug>
+#include <QFileDialog>
+#include <QFile>
+#include <QMetaEnum>
+#include <QTextStream>
+#include <QVBoxLayout>
+
+// CTKTesting includes
+#include "ctkCallback.h"
+#include "ctkEventTranslatorPlayerWidget.h"
+#include "ctkQtTestingUtility.h"
+#include "ctkXMLEventObserver.h"
+#include "ctkXMLEventSource.h"
+#include "ui_ctkEventTranslatorPlayerWidget.h"
+
+// Standard include
+#include <cmath>
+
+//-----------------------------------------------------------------------------
+class ctkEventTranslatorPlayerWidgetPrivate
+  : public Ui_ctkEventTranslatorPlayerWidget
+{
+public:
+  ~ctkEventTranslatorPlayerWidgetPrivate();
+  QList<ctkEventTranslatorPlayerWidget::InfoTestCase*>  TestCase;
+  pqTestUtility*        TestUtility;
+};
+
+ctkEventTranslatorPlayerWidgetPrivate::~ctkEventTranslatorPlayerWidgetPrivate()
+{
+  delete this->TestUtility;
+  this->TestUtility = 0;
+}
+
+//-----------------------------------------------------------------------------
+ctkEventTranslatorPlayerWidget::ctkEventTranslatorPlayerWidget()
+  :  Superclass()
+  , d_ptr(new ctkEventTranslatorPlayerWidgetPrivate)
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  d->setupUi(this);
+
+  QObject::connect(d->TranslatorButton, SIGNAL(clicked(bool)),
+                   this, SLOT(onClickedRecord(bool)));
+  QObject::connect(d->PlayerButton, SIGNAL(clicked(bool)),
+                   this, SLOT(onClickedPlayback(bool)));
+  QObject::connect(d->TestCaseComboBox, SIGNAL(currentIndexChanged(int)),
+                   this, SLOT(switchTestCase(int)));
+
+  d->TestUtility = new pqTestUtility(this);
+  d->TestUtility->addEventObserver("xml", new ctkXMLEventObserver(d->TestUtility));
+  d->TestUtility->addEventSource("xml", new ctkXMLEventSource(d->TestUtility));
+}
+
+//-----------------------------------------------------------------------------
+ctkEventTranslatorPlayerWidget::~ctkEventTranslatorPlayerWidget()
+{
+}
+
+//-----------------------------------------------------------------------------
+void ctkEventTranslatorPlayerWidget::addTestCase(QWidget *widget,
+                                                 QString fileName,
+                                                 void (*newCallback)(void * data))
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+
+  InfoTestCase* infoTestCase = new InfoTestCase;
+  infoTestCase->Widget = widget;
+  infoTestCase->FileName = fileName;
+  infoTestCase->Callback = new ctkCallback(newCallback);
+  infoTestCase->Callback->setCallbackData(widget);
+  infoTestCase->Dialog = false;
+
+  d->TestCase.push_back(infoTestCase);
+  
+  d->stackedWidget->addWidget(widget);
+  d->TestCaseComboBox->addItem(QString::number(d->TestCase.count()),
+                               QVariant(d->TestCase.count()));
+}
+
+//-----------------------------------------------------------------------------
+void ctkEventTranslatorPlayerWidget::addTestCase(QDialog *dialog,
+                                                 QString fileName,
+                                                 void (*newCallback)(void * data))
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+
+  InfoTestCase* infoTestCase = new InfoTestCase;
+  infoTestCase->Widget = dialog;
+  infoTestCase->FileName = fileName;
+  infoTestCase->Callback = new ctkCallback(newCallback);
+  infoTestCase->Callback->setCallbackData(dialog);
+  infoTestCase->Dialog = false;
+
+  d->TestCase.push_back(infoTestCase);
+
+//  QVBoxLayout* layout = new QVBoxLayout();
+  QPushButton* button = new QPushButton("Open the Dialog");
+  connect(button, SIGNAL(clicked(bool)), this, SLOT(popupDialog()));
+
+  d->stackedWidget->addWidget(button);
+  d->TestCaseComboBox->addItem(QString::number(d->TestCase.count()),
+                               QVariant(d->TestCase.count()));
+}
+
+//-----------------------------------------------------------------------------
+void ctkEventTranslatorPlayerWidget::setTestUtility(pqTestUtility* newTestUtility)
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  d->TestUtility = newTestUtility;
+  d->TestUtility->addEventObserver("xml", new ctkXMLEventObserver(this));
+  d->TestUtility->addEventSource("xml", new ctkXMLEventSource(this));
+}
+
+//-----------------------------------------------------------------------------
+pqTestUtility* ctkEventTranslatorPlayerWidget::testUtility() const
+{
+  Q_D(const ctkEventTranslatorPlayerWidget);
+  return d->TestUtility;
+}
+
+//-----------------------------------------------------------------------------
+void  ctkEventTranslatorPlayerWidget::addWidgetEventPlayer(
+  pqWidgetEventPlayer* player)
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  d->TestUtility->eventPlayer()->addWidgetEventPlayer(player);
+}
+
+//-----------------------------------------------------------------------------
+void  ctkEventTranslatorPlayerWidget::addWidgetEventTranslator(
+  pqWidgetEventTranslator* translator)
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  d->TestUtility->eventTranslator()->addWidgetEventTranslator(translator);
+}
+
+//-----------------------------------------------------------------------------
+void ctkEventTranslatorPlayerWidget::record(int currentTestCase)
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  if(d->TestCase.count() == 0 ||
+     currentTestCase > d->TestCase.count() - 1)
+    {
+    qWarning() << "Problem with the test case. Please verify that you added a test case.";
+    return;
+    }
+
+  // We load the xml and check if it is different form the other test case
+  QFile* filexml = new QFile(d->TestCase[currentTestCase]->FileName);
+  if (!filexml->open(QIODevice::ReadWrite))
+    {
+    qWarning() << "The file .xml was not created";
+    return;
+    }
+  for(int i = 0 ; i < currentTestCase && i != currentTestCase; i++)
+    {
+    if (d->TestCase[i]->FileName ==
+          d->TestCase[currentTestCase]->FileName)
+      {
+      qWarning() << "This xml file should already recorded\n";
+      return;
+      }
+    }
+
+  d->TestUtility->recordTests(filexml->fileName());
+}
+
+//-----------------------------------------------------------------------------
+bool ctkEventTranslatorPlayerWidget::play(int currentTestCase)
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  if(d->TestCase.count() == 0)
+    {
+    qWarning() << "No test case had been added. Please add a test case.";
+    return false;
+    }
+
+  // Connect the slot player done to the the good callback
+  QObject::connect(this, SIGNAL(playerDone(QWidget*)),
+                   d->TestCase[currentTestCase]->Callback, SLOT(invoke()));
+
+  if (!QFile::exists(d->TestCase[currentTestCase]->FileName))
+    {
+    qWarning() << "No .xml file for this test case";
+    return false;
+    }
+
+  if (!d->TestUtility->playTests(QStringList(d->TestCase[currentTestCase]->FileName)))
+    {
+    qWarning() << "The Test case " << currentTestCase
+               << " playback has failed !";
+    return false;
+    }
+  emit this->playerDone(d->TestCase[currentTestCase]->Widget);
+
+  QObject::disconnect(d->TestCase[currentTestCase]->Callback);
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+void ctkEventTranslatorPlayerWidget::play()
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  bool success = true;
+  for(int i = 0 ; i < d->TestCase.count() ; i++ )
+    {
+    d->TestCaseComboBox->setCurrentIndex(i);
+    success &= this->play(i);
+    }
+  QApplication::exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+//-----------------------------------------------------------------------------
+void ctkEventTranslatorPlayerWidget::popupDialog()
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  QDialog* widget = qobject_cast<QDialog*>(d->TestCase[d->TestCaseComboBox->currentIndex()]->Widget);
+  widget->exec();
+}
+
+//-----------------------------------------------------------------------------
+void ctkEventTranslatorPlayerWidget::onClickedPlayback(bool)
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  this->play(d->TestCaseComboBox->currentIndex());
+}
+
+//-----------------------------------------------------------------------------
+void ctkEventTranslatorPlayerWidget::onClickedRecord(bool)
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  this->record(d->TestCaseComboBox->currentIndex());
+}
+
+//-----------------------------------------------------------------------------
+void ctkEventTranslatorPlayerWidget::switchTestCase(int index)
+{
+  Q_D(ctkEventTranslatorPlayerWidget);
+  d->stackedWidget->setCurrentIndex(index);
+}
+
+//-----------------------------------------------------------------------------
+const char* enumValueToKey(QObject* object, const char* enumName, int value)
+{
+  const QMetaObject * metaObject = object->metaObject();
+  QMetaEnum theEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
+  return theEnum.valueToKey(value);
+}
+
+//-----------------------------------------------------------------------------
+bool ctkEventTranslatorPlayerWidget::compare(const double &actual,
+                                             const double& expected,
+                                             const char* actualName,
+                                             const char* expectedName,
+                                             const char * function, int line)
+{
+  if (actual != expected)
+    {
+    QTextStream(stderr, QIODevice::WriteOnly)
+        << "Line " << line << " - Problem with function " << function << "\n"
+        << "\tActual value : '" << actualName << "' = " << actual << " \n"
+        << "\tExpected value : '" << expectedName << "' = " << expected << endl;
+    QApplication::exit(EXIT_FAILURE);
+    return false;
+    }
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkEventTranslatorPlayerWidget::compare(const int &actual,
+                                             const int& expected,
+                                             const char* actualName,
+                                             const char* expectedName,
+                                             const char * function, int line)
+{
+  if (actual != expected)
+    {
+    QTextStream(stderr, QIODevice::WriteOnly)
+        << "Line " << line << " - Problem with function " << function << "\n"
+        << "\tActual value : '" << actualName << "' = " << actual << " \n"
+        << "\tExpected value : '" << expectedName << "' = " << expected << endl;
+    QApplication::exit(EXIT_FAILURE);
+    return false;
+    }
+//  enumValueToKey(widget, "Axis", currentCurrentAxis)
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkEventTranslatorPlayerWidget::compare(const QString& actual,
+                                             const QString& expected,
+                                             const char* actualName,
+                                             const char* expectedName,
+                                             const char * function, int line)
+{
+  if (actual != expected)
+    {
+    QTextStream(stderr, QIODevice::WriteOnly)
+        << "Line " << line << " - Problem with function " << function << "\n"
+        << "\tActual value : '" << actualName << "' = " << actual << " \n"
+        << "\tExpected value : '" << expectedName << "' = " << expected << endl;
+    QApplication::exit(EXIT_FAILURE);
+    return false;
+    }
+//  enumValueToKey(widget, "Axis", currentCurrentAxis)
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkEventTranslatorPlayerWidget::compare(const QStringList& actual,
+                                             const QStringList& expected,
+                                             const char* actualName,
+                                             const char* expectedName,
+                                             const char * function, int line)
+{
+  if (actual != expected)
+    {
+    QTextStream(stderr, QIODevice::WriteOnly)
+        << "Line " << line << " - Problem with function " << function << "\n"
+        << "\tActual value : '" << actualName << "' = " << actual.join(" ") << " \n"
+        << "\tExpected value : '" << expectedName << "' = " << expected.join(" ") << endl;
+    QApplication::exit(EXIT_FAILURE);
+    return false;
+    }
+//  enumValueToKey(widget, "Axis", currentCurrentAxis)
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkEventTranslatorPlayerWidget::compare(const QDateTime& actual,
+                                             const QDateTime& expected,
+                                             const char* actualName,
+                                             const char* expectedName,
+                                             const char * function, int line)
+{
+  if (actual != expected)
+    {
+    QTextStream(stderr, QIODevice::WriteOnly)
+        << "Line " << line << " - Problem with function " << function << "\n"
+        << "\tActual value : '" << actualName << "' = " << actual.date().toString() << " \n"
+        << "\tExpected value : '" << expectedName << "' = " << expected.date().toString() << endl;
+    QApplication::exit(EXIT_FAILURE);
+    return false;
+    }
+//  enumValueToKey(widget, "Axis", currentCurrentAxis)
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkEventTranslatorPlayerWidget::compare(const QColor& actual,
+                                             const QColor& expected,
+                                             const char* actualName,
+                                             const char* expectedName,
+                                             const char * function, int line)
+{
+  if (actual != expected)
+    {
+    QTextStream(stderr, QIODevice::WriteOnly)
+        << "Line " << line << " - Problem with function " << function << "\n"
+        << "\tActual value : '" << actualName << "' = R:" << actual.red() << " G:"<< actual.green() << " B:" << actual.blue() << "\n"
+        << "\tExpected value : '" << expectedName << "' = R:" << expected.red() << " G:"<< expected.green() << " B:" << expected.blue()<< endl;
+    QApplication::exit(EXIT_FAILURE);
+    return false;
+    }
+//  enumValueToKey(widget, "Axis", currentCurrentAxis)
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkEventTranslatorPlayerWidget::compare(const QImage& actual,
+                                             const QImage& expected,
+                                             const char* actualName,
+                                             const char* expectedName,
+                                             const char * function, int line)
+{
+  double totaldiff = 0.0 ; //holds the number of different pixels
+
+  // images are considered the same if both contain a null image
+  if (actual.isNull() && expected.isNull())
+    {
+    return true;
+    }
+  // images are not the same if one images contains a null image
+  if (actual.isNull() || expected.isNull())
+    {
+    QTextStream(stderr, QIODevice::WriteOnly)
+        << "Line " << line << " - 1 image is Null " << function << "\n" << endl;
+    QApplication::exit(EXIT_FAILURE);
+    return false;
+    }
+  // images do not have the same size
+  if (actual.size() != expected.size())
+    {
+    QTextStream(stderr, QIODevice::WriteOnly)
+        << "Line " << line << " - The 2 Images don't have the same size " << function << "\n"
+        << "\tActual value : '" << actualName << "' = W:" << actual.width() << " H:"<< actual.height() << "\n"
+        << "\tExpected value : '" << expectedName << "' = W:" << expected.width() << " H:"<< expected.height() << endl;
+    QApplication::exit(EXIT_FAILURE);
+    return false;
+    }
+
+  QImage a = actual.convertToFormat(QImage::Format_ARGB32);
+  QImage e = expected.convertToFormat(QImage::Format_ARGB32);
+
+  for ( int y=0; y<a.height(); y++ )
+    {
+    for ( int x=0; x<a.width(); x++ )
+      {
+      QRgb actPix = a.pixel(x, y);
+      QRgb expPix = e.pixel(x, y);
+      if (qAlpha(actPix) == 0 && qAlpha(expPix) == 0)
+        {
+        continue;
+        }
+      if (actPix != expPix)
+        {
+        totaldiff ++;
+        }
+      }
+    }
+  totaldiff = (totaldiff * 100)  / (a.width() * a.height());
+  if (totaldiff >= 0.01)
+    {
+    QTextStream(stderr, QIODevice::WriteOnly)
+        << "Line " << line << " - The 2 Images have "
+        << totaldiff << "% differencies \n" << endl;
+    QApplication::exit(EXIT_FAILURE);
+    return false;
+    }
+  return true;
+}

+ 118 - 0
Libs/QtTesting/ctkEventTranslatorPlayerWidget.h

@@ -0,0 +1,118 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+#ifndef __ctkEventTranslatorPlayerWidget_h
+#define __ctkEventTranslatorPlayerWidget_h
+
+// Qt includes
+#include <QDateTime>
+#include <QMainWindow>
+
+// CTK includes
+#if !defined(NO_SYMBOL_EXPORT)
+# include "ctkQtTestingExport.h"
+#else
+# define CTK_QTTESTING_EXPORT
+#endif
+class ctkCallback;
+class ctkEventTranslatorPlayerWidgetPrivate;
+
+// QtTesting includes
+class pqTestUtility;
+class pqWidgetEventPlayer;
+class pqWidgetEventTranslator;
+
+//-----------------------------------------------------------------------------
+#define CTKCOMPARE(actual, expected) \
+do \
+{\
+    if (!ctkEventTranslatorPlayerWidget::compare(actual, expected, #actual, #expected, __FILE__, __LINE__))\
+      { \
+      return;\
+      } \
+} while (0)
+
+//-----------------------------------------------------------------------------
+class CTK_QTTESTING_EXPORT ctkEventTranslatorPlayerWidget
+  : public QMainWindow
+{
+  Q_OBJECT
+public:
+  typedef QMainWindow Superclass;
+  ctkEventTranslatorPlayerWidget();
+  ~ctkEventTranslatorPlayerWidget();
+
+  void addTestCase(QWidget* widget, QString fileName, void(*newCallback)(void* data));
+  void addTestCase(QDialog* dialog, QString fileName, void(*newCallback)(void* data));
+
+  void setTestUtility(pqTestUtility* newTestUtility);
+  pqTestUtility* testUtility() const;
+
+  void addWidgetEventPlayer(pqWidgetEventPlayer* player);
+  void addWidgetEventTranslator(pqWidgetEventTranslator* translator);
+
+  static const char* enumValueToKey(QObject* object, const char* enumName, int value);
+
+  static bool compare(const double& actual, const double& expected,
+                      const char* actualName, const char* expectedName, const char * function, int line);
+  static bool compare(const int& actual, const int& expected,
+                      const char* actualName, const char* expectedName, const char * function, int line);
+  static bool compare(const QString& actual,const QString& expected,
+                      const char* actualName, const char* expectedName, const char * function, int line);
+  static bool compare(const QStringList& actual,const QStringList& expected,
+                      const char* actualName, const char* expectedName, const char * function, int line);
+  static bool compare(const QDateTime& actual,const QDateTime& expected,
+                      const char* actualName, const char* expectedName, const char * function, int line);
+  static bool compare(const QColor& actual,const QColor& expected,
+                      const char* actualName, const char* expectedName, const char * function, int line);
+  static bool compare(const QImage& actual,const QImage& expected,
+                      const char* actualName, const char* expectedName, const char * function, int line);
+
+public slots:
+  void play();
+
+protected slots:
+  void record(int currentTestCase);
+  bool play(int currentTestCase);
+  void popupDialog();
+  void onClickedPlayback(bool);
+  void onClickedRecord(bool);
+  void switchTestCase(int testCase);
+
+signals:
+  void startPlayerBack(QWidget* widget);
+  void playerDone(QWidget* widget);
+  void recordDone(QWidget* widget);
+
+protected:
+  QScopedPointer< ctkEventTranslatorPlayerWidgetPrivate > d_ptr;
+private:
+  Q_DECLARE_PRIVATE(ctkEventTranslatorPlayerWidget);
+  Q_DISABLE_COPY(ctkEventTranslatorPlayerWidget);
+
+  struct InfoTestCase {
+    QWidget*       Widget;
+    ctkCallback*   Callback;
+    QString        FileName;
+    bool           Dialog;
+  };
+
+};
+
+#endif

+ 58 - 0
Libs/QtTesting/ctkQtTestingUtility.cpp.in

@@ -0,0 +1,58 @@
+// Qt includes
+#include <QDebug>
+
+// CTKTesting inlcudes
+#include "ctkQtTestingUtility.h"
+
+@CTK_ADD_HEADER_EVENT_PLAYERS@
+
+@CTK_ADD_HEADER_EVENT_TRANSLATORS@
+
+//-----------------------------------------------------------------------------
+ctkQtTestingUtility::ctkQtTestingUtility(QObject* p)
+  : Superclass(p)
+{
+  this->addDefaultCTKWidgetEventTranslatorsToTranslator(this);
+  this->addDefaultCTKWidgetEventPlayersToPlayer(this);
+}
+
+//-----------------------------------------------------------------------------
+ctkQtTestingUtility::~ctkQtTestingUtility()
+{
+}
+
+//-----------------------------------------------------------------------------
+void ctkQtTestingUtility::addDefaultCTKWidgetEventTranslatorsToTranslator(pqTestUtility* util)
+{
+  Q_UNUSED(util);
+@CTK_ADD_WIDGET_EVENT_TRANSLATORS@
+  this->eventTranslator()->removeWidgetEventTranslator(
+    QLatin1String("pqNativeFileDialogEventTranslator"));
+}
+
+//-----------------------------------------------------------------------------
+void ctkQtTestingUtility::addDefaultCTKWidgetEventPlayersToPlayer(pqTestUtility* util)
+{
+  Q_UNUSED(util);
+@CTK_ADD_WIDGET_EVENT_PLAYERS@
+  this->eventPlayer()->removeWidgetEventPlayer(
+    QLatin1String("pqNativeFileDialogEventPlayer"));
+}
+
+//-----------------------------------------------------------------------------
+void ctkQtTestingUtility::addTranslator(pqWidgetEventTranslator* translator)
+{
+  if(translator)
+    {
+    this->eventTranslator()->addWidgetEventTranslator(translator);
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkQtTestingUtility::addPlayer(pqWidgetEventPlayer* player)
+{
+  if(player)
+    {
+    this->eventPlayer()->addWidgetEventPlayer(player);
+    }
+}

+ 52 - 0
Libs/QtTesting/ctkQtTestingUtility.h

@@ -0,0 +1,52 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkQtTestingUtility_h
+#define __ctkQtTestingUtility_h
+
+// QtTesting includes
+#include <pqTestUtility.h>
+
+// CTKQtTesting includes
+#if !defined(NO_SYMBOL_EXPORT)
+# include "ctkQtTestingExport.h"
+#else
+# define CTK_QTTESTING_EXPORT
+#endif
+
+
+class CTK_QTTESTING_EXPORT ctkQtTestingUtility : public pqTestUtility
+{
+  Q_OBJECT
+
+public:
+  typedef pqTestUtility Superclass;
+
+  ctkQtTestingUtility(QObject* parent=0);
+  ~ctkQtTestingUtility();
+
+  void addDefaultCTKWidgetEventTranslatorsToTranslator(pqTestUtility* util);
+  void addDefaultCTKWidgetEventPlayersToPlayer(pqTestUtility* util);
+
+  void addTranslator(pqWidgetEventTranslator* translator);
+  void addPlayer(pqWidgetEventPlayer* player);
+};
+
+#endif

+ 150 - 0
Libs/QtTesting/ctkXMLEventObserver.cpp

@@ -0,0 +1,150 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// QT includes
+#include <QApplication>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QLayout>
+#include <QMainWindow>
+#include <QVariant>
+
+// CTKQtTesting includes
+#include "ctkXMLEventObserver.h"
+
+//-----------------------------------------------------------------------------
+// ctkXMLEventObserver methods
+
+//-----------------------------------------------------------------------------
+ctkXMLEventObserver::ctkXMLEventObserver(QObject* p)
+  : pqEventObserver(p)
+{
+  this->XMLStream = NULL;
+  this->TestUtility = qobject_cast<pqTestUtility*>(p);
+}
+
+//-----------------------------------------------------------------------------
+ctkXMLEventObserver::~ctkXMLEventObserver()
+{
+  delete this->XMLStream;
+  this->TestUtility = 0;
+}
+
+//-----------------------------------------------------------------------------
+void ctkXMLEventObserver::recordApplicationSettings()
+{
+  if(this->XMLStream)
+    {
+    this->XMLStream->writeStartElement("settings");
+
+    // Informations about the application
+    this->recordApplicationSetting("name","qApp", "applicationName",
+                                   QCoreApplication::applicationName());
+    this->recordApplicationSetting("version" , "qApp", "applicationVersion",
+                                   QCoreApplication::applicationVersion());
+
+    // save Geometry and State of the application
+    QMainWindow* window = NULL;
+    foreach(QWidget * widget, QApplication::topLevelWidgets())
+      {
+      window = qobject_cast<QMainWindow*>(widget);
+      if (window)
+        {
+        this->recordApplicationSetting("geometry" , "MainWindow", "mainWindowGeometry",
+                                       QString(window->saveGeometry().toHex()));
+
+        this->recordApplicationSetting("state" , "MainWindow", "mainWindowState",
+                                       QString(window->saveState().toHex()));
+        break;
+        }
+      }
+
+    // Save extra properties from the application
+    QMap<QObject*, QString> states = this->TestUtility->objectStateProperty();
+    QMap<QObject*, QString>::iterator iter;
+    for(iter = states.begin() ; iter!=states.end() ; ++iter)
+      {
+      this->recordApplicationSetting(
+          iter.value(),
+          iter.key()->metaObject()->className(),
+          iter.value(),
+          iter.key()->property(iter.value().toLatin1()).toString()
+          );
+      }
+
+    this->XMLStream->writeEndElement();
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkXMLEventObserver::recordApplicationSetting(const QString &startElement,
+                                                   const QString &attribute1,
+                                                   const QString &attribute2,
+                                                   const QString &attribute3)
+{
+  this->XMLStream->writeStartElement(startElement);
+  this->XMLStream->writeAttribute("widget", attribute1);
+  this->XMLStream->writeAttribute("command", attribute2);
+  this->XMLStream->writeAttribute("arguments", attribute3);
+  this->XMLStream->writeEndElement();
+}
+
+//-----------------------------------------------------------------------------
+void ctkXMLEventObserver::setStream(QTextStream* stream)
+{
+  if (this->XMLStream)
+    {
+    this->XMLStream->writeEndElement();
+    this->XMLStream->writeEndElement();
+    this->XMLStream->writeEndDocument();
+    delete this->XMLStream;
+    this->XMLStream = NULL;
+    }
+  if (this->Stream)
+    {
+    *this->Stream << this->XMLString;
+    }
+  this->XMLString = QString();
+  pqEventObserver::setStream(stream);
+  if (this->Stream)
+    {
+    this->XMLStream = new QXmlStreamWriter(&this->XMLString);
+    this->XMLStream->setAutoFormatting(true);
+    this->XMLStream->writeStartDocument();
+    this->XMLStream->writeStartElement("xml");
+    this->recordApplicationSettings();
+    this->XMLStream->writeStartElement("events");
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkXMLEventObserver::onRecordEvent(const QString& widget,
+                                     const QString& command,
+                                     const QString& arguments)
+{
+  if(this->XMLStream)
+    {
+    this->XMLStream->writeStartElement("event");
+    this->XMLStream->writeAttribute("widget", widget);
+    this->XMLStream->writeAttribute("command", command);
+    this->XMLStream->writeAttribute("arguments", arguments);
+    this->XMLStream->writeEndElement();
+    }
+}

+ 69 - 0
Libs/QtTesting/ctkXMLEventObserver.h

@@ -0,0 +1,69 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkXMLEventObserver_h
+#define __ctkXMLEventObserver_h
+
+// QT includes
+#include <QString>
+#include <QXmlStreamWriter>
+#include <QMainWindow>
+
+// QtTesting includes
+#include <pqEventObserver.h>
+#include <pqTestUtility.h>
+
+// CTKQtTesting includes
+#if !defined(NO_SYMBOL_EXPORT)
+# include "ctkQtTestingExport.h"
+#else
+# define CTK_QTTESTING_EXPORT
+#endif
+
+
+class QTextStream;
+
+//-----------------------------------------------------------------------------
+class CTK_QTTESTING_EXPORT ctkXMLEventObserver : public pqEventObserver
+{
+  Q_OBJECT
+
+public:
+  ctkXMLEventObserver(QObject* p);
+  ~ctkXMLEventObserver();
+
+  virtual void setStream(QTextStream* stream);
+  virtual void onRecordEvent(const QString& widget,
+                             const QString& command,
+                             const QString& arguments);
+
+  void recordApplicationSettings();
+  void recordApplicationSetting(const QString& startElement,
+                                const QString& attribute1,
+                                const QString& attribute2,
+                                const QString& attribute3);
+protected:
+  QXmlStreamWriter* XMLStream;
+  QString XMLString;
+  pqTestUtility* TestUtility;
+
+};
+
+#endif // __ctkXMLEventObserver_h

+ 238 - 0
Libs/QtTesting/ctkXMLEventSource.cpp

@@ -0,0 +1,238 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// QT includes
+#include <QApplication>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QFile>
+#include <QMap>
+#include <QMessageBox>
+#include <QVariant>
+
+// CTKQtTesting includes
+#include "ctkXMLEventSource.h"
+
+//-----------------------------------------------------------------------------
+// ctkXMLEventSource methods
+
+//-----------------------------------------------------------------------------
+ctkXMLEventSource::ctkXMLEventSource(QObject* p)
+  : Superclass(p)
+{
+  this->Automatic = false;
+  this->XMLStream = NULL;
+  this->TestUtility = qobject_cast<pqTestUtility*>(p);
+}
+
+//-----------------------------------------------------------------------------
+ctkXMLEventSource::~ctkXMLEventSource()
+{
+  delete this->XMLStream;
+  this->TestUtility = 0;
+}
+
+//-----------------------------------------------------------------------------
+void ctkXMLEventSource::setRestoreSettingsAuto(bool value)
+{
+  this->Automatic = value;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkXMLEventSource::restoreSettingsAuto() const
+{
+  return this->Automatic;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkXMLEventSource::setContent(const QString& xmlfilename)
+{
+  delete this->XMLStream;
+  this->XMLStream = NULL;
+
+  QFile xml(xmlfilename);
+  if (!xml.open(QIODevice::ReadOnly))
+    {
+    qDebug() << "Failed to load " << xmlfilename;
+    return false;
+    }
+  QByteArray data = xml.readAll();
+  this->XMLStream = new QXmlStreamReader(data);
+  /* This checked for valid event objects, but also caused the first event
+   * to get dropped. Commenting this out in the example. If you wish to report
+   * empty XML test files a flag indicating whether valid events were found is
+   * probably the best way to go.
+  while (!this->XMLStream->atEnd())
+    {
+    QXmlStreamReader::TokenType token = this->XMLStream->readNext();
+    if (token == QXmlStreamReader::StartElement)
+      {
+      if (this->XMLStream->name() == "event")
+        {
+        break;
+        }
+      }
+    } */
+  if (this->XMLStream->atEnd())
+    {
+    qDebug() << "Invalid xml" << endl;
+    return false;
+    }
+
+  if(this->settingsRecorded())
+    {
+    qDebug() << "settings recorded";
+    this->OldSettings = this->recoverSettingsFromXML();
+    if(!this->settingsUpToData())
+      {
+      qDebug() << "restoring ...";
+      this->restoreApplicationSettings();
+      }
+    }
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+int ctkXMLEventSource::getNextEvent(QString& widget, QString& command, QString&arguments)
+{
+  if (this->XMLStream->atEnd())
+    {
+    return DONE;
+    }
+  while (!this->XMLStream->atEnd())
+    {
+    QXmlStreamReader::TokenType token = this->XMLStream->readNext();
+    if (token == QXmlStreamReader::StartElement)
+      {
+      if (this->XMLStream->name() == "event")
+        {
+        break;
+        }
+      }
+    }
+  if (this->XMLStream->atEnd())
+    {
+    return DONE;
+    }
+  widget = this->XMLStream->attributes().value("widget").toString();
+  command = this->XMLStream->attributes().value("command").toString();
+  arguments = this->XMLStream->attributes().value("arguments").toString();
+  return SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkXMLEventSource::settingsRecorded()
+{
+  while(this->XMLStream->name() != "settings" && this->XMLStream->name() != "events")
+    {
+    this->XMLStream->readNext();
+    }
+  return (this->XMLStream->name() == "settings") ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkXMLEventSource::settingsUpToData()
+{
+  QMainWindow* window = this->mainWindow();
+
+  bool result = true;
+  QMap<QObject*, QString> states = this->TestUtility->objectStateProperty();
+
+  result &= (this->OldSettings.value("geometry") == QString(window->saveGeometry().toHex()));
+  result &= (this->OldSettings.value("state") == QString(window->saveState().toHex()));
+
+  QMap<QObject*, QString>::iterator iter;
+  for(iter = states.begin() ; iter!=states.end() ; ++iter)
+    {
+    result &= (this->OldSettings.value(iter.value()) ==
+              iter.key()->property(iter.value().toLatin1()).toString());
+    }
+
+  return result;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkXMLEventSource::restoreApplicationSettings()
+{
+  QMainWindow* window = this->mainWindow();
+
+  bool result = false;
+  QMap<QObject*, QString> states = this->TestUtility->objectStateProperty();
+
+  if (!this->Automatic)
+    {
+    if (QMessageBox::No == QMessageBox::warning(0, tr("Playback ..."),
+                                                tr("The settings are differents from the record Settings.\n"
+                                                   "Do you want to restore the settings?"),
+                                                QMessageBox::Yes | QMessageBox::No,
+                                                QMessageBox::Yes))
+      {
+      return false;
+      }
+
+    result = window->restoreState(
+                QByteArray::fromHex(QByteArray(this->OldSettings.value("state").toLocal8Bit().constData())));
+    result = window->restoreGeometry(
+                QByteArray::fromHex(QByteArray(this->OldSettings.value("geometry").toLocal8Bit().constData())));
+
+    QMap<QObject*, QString>::iterator iter;
+    for(iter = states.begin() ; iter!=states.end() ; ++iter)
+      {
+      iter.key()->setProperty(iter.value().toLatin1(),
+                              QVariant(this->OldSettings.value(iter.value())));
+      }
+    }
+
+  return result;
+}
+
+//-----------------------------------------------------------------------------
+QMap<QString, QString> ctkXMLEventSource::recoverSettingsFromXML()
+{
+  // Recover the settings
+  QMap<QString, QString> settings;
+  while (this->XMLStream->tokenType() != QXmlStreamReader::EndElement ||
+         this->XMLStream->name() != "settings")
+    {
+    this->XMLStream->readNext();
+    if (!this->XMLStream->name().isEmpty() &&
+        this->XMLStream->tokenType() == QXmlStreamReader::StartElement)
+      {
+      settings.insert(this->XMLStream->name().toString(),
+                      this->XMLStream->attributes().value("arguments").toString());
+      }
+    }
+  return settings;
+}
+
+//-----------------------------------------------------------------------------
+QMainWindow* ctkXMLEventSource::mainWindow()
+{
+  QMainWindow* window = NULL;
+  foreach(QWidget * widget, QApplication::topLevelWidgets())
+    {
+    window = qobject_cast<QMainWindow*>(widget);
+    if (window)
+      {
+      return window;
+      }
+    }
+  return 0;
+}

+ 70 - 0
Libs/QtTesting/ctkXMLEventSource.h

@@ -0,0 +1,70 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkXMLEventSource_h
+#define __ctkXMLEventSource_h
+
+// QT includes
+#include <QMainWindow>
+#include <QXmlStreamReader>
+
+// QtTesting includes
+#include <pqEventSource.h>
+#include <pqTestUtility.h>
+
+// CTKQtTesting includes
+#if !defined(NO_SYMBOL_EXPORT)
+# include "ctkQtTestingExport.h"
+#else
+# define CTK_QTTESTING_EXPORT
+#endif
+
+//-----------------------------------------------------------------------------
+class CTK_QTTESTING_EXPORT ctkXMLEventSource : public pqEventSource
+{
+  Q_OBJECT
+
+public:
+  typedef pqEventSource Superclass;
+
+  ctkXMLEventSource(QObject* p);
+  ~ctkXMLEventSource();
+
+  virtual bool setContent(const QString& xmlfilename);
+  int getNextEvent(QString& widget, QString& command, QString&arguments);
+
+  void setRestoreSettingsAuto(bool value);
+  bool restoreSettingsAuto() const;
+
+  bool settingsRecorded();
+  bool settingsUpToData();
+  bool restoreApplicationSettings();
+  QMap<QString, QString> recoverSettingsFromXML();
+
+protected:
+  QMainWindow* mainWindow();
+
+  bool                    Automatic;
+  QXmlStreamReader*       XMLStream;
+  pqTestUtility*          TestUtility;
+  QMap<QString, QString>  OldSettings;
+};
+
+#endif // __ctkXMLEventSource_h

+ 3 - 0
Libs/Testing/CMakeLists.txt

@@ -3,6 +3,9 @@ project(CTKTesting)
 # CMake Macros
 include(CMake/ctkMacroGenerateMocs.cmake)
 
+if(CTK_USE_QTTESTING)
+  include(../QtTesting/CMake/ctkQtTesting.cmake)
+endif()
 install(FILES
   ctkTest.h
   DESTINATION ${CTK_INSTALL_INCLUDE_DIR} COMPONENT Development

+ 1 - 0
SuperBuild.cmake

@@ -73,6 +73,7 @@ endforeach()
 set(ctk_cmake_boolean_args
   BUILD_TESTING
   CTK_BUILD_QTDESIGNER_PLUGINS
+  CTK_USE_QTTESTING
   CTK_USE_KWSTYLE
   WITH_COVERAGE
   DOCUMENTATION_TARGET_IN_ALL

+ 1 - 0
ctkConfig.h.in

@@ -2,6 +2,7 @@
 #define __ctkConfig_h
 
 #define CTK_PLUGIN_DIR "@CTK_PLUGIN_LIBRARIES_DIR_CONFIG@/"
+#define CTK_SOURCE_DIR "@CTK_SOURCE_DIR@"
 
 #endif