diff --git a/CMakeLists.txt b/CMakeLists.txt index 87b6acd186..3b6a8bd634 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,165 +1,165 @@ cmake_minimum_required(VERSION 3.0) project(KDevelop VERSION 5.2.40) # KDevelop SOVERSION # E.g. for KDevelop 5.2.0 => SOVERSION 52 (we only promise ABI compatibility between patch version updates) set(KDEVELOP_SOVERSION 53) # plugin version as used e.g. in plugin installation path set(KDEV_PLUGIN_VERSION 31) # we need some parts of the ECM CMake helpers find_package (ECM 5.14.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${KDevelop_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH}) include(KDECompilerSettings NO_POLICY_SCOPE) # needs to be first, as set policies influence following macros include(ECMOptionalAddSubdirectory) include(ECMInstallIcons) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMAddTests) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(ECMQtDeclareLoggingCategory) include(GenerateExportHeader) include(CMakePackageConfigHelpers) include(FeatureSummary) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) set(QT_MIN_VERSION "5.5.0") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Widgets Concurrent Quick QuickWidgets) if(BUILD_TESTING) find_package(Qt5Test ${QT_MIN_VERSION} CONFIG REQUIRED) endif() set(KF5_DEP_VERSION "5.15.0") # we need KCrash::initialize find_package(KF5 ${KF5_DEP_VERSION} REQUIRED COMPONENTS Config Declarative DocTools IconThemes I18n ItemModels ItemViews JobWidgets KCMUtils KIO NewStuff NotifyConfig Parts Service TextEditor ThreadWeaver XmlGui WindowSystem Crash GuiAddons Archive Notifications ) find_package(KF5SysGuard CONFIG) set_package_properties(KF5SysGuard PROPERTIES PURPOSE "Framework for process listing. Required for the 'Attach to Process' feature" TYPE RECOMMENDED ) find_package(KDevelop-PG-Qt 1.90.90 CONFIG) set_package_properties(KDevelop-PG-Qt PROPERTIES PURPOSE "KDevelop parser generator library. Required for the QMake Builder/Manager plugin." TYPE RECOMMENDED ) find_package(SharedMimeInfo REQUIRED) add_definitions( -DQT_DEPRECATED_WARNINGS -DQT_DISABLE_DEPRECATED_BEFORE=0x050500 -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING -DQT_STRICT_ITERATORS -DQT_USE_QSTRINGBUILDER ) function(add_compile_flag_if_supported _flag) unset(_have_flag CACHE) string(REGEX REPLACE "[-=]" "_" _varname ${_flag}) string(TOUPPER ${_varname} _varname) set(_varname "HAVE${_varname}") check_cxx_compiler_flag("${_flag}" "${_varname}") if (${${_varname}}) add_compile_options(${_flag}) endif() endfunction() # Turn off missing-field-initializers warning for GCC to avoid noise from false positives with empty {} # See discussion: http://mail.kde.org/pipermail/kdevelop-devel/2014-February/046910.html add_compile_flag_if_supported(-Wno-missing-field-initializers) add_compile_flag_if_supported(-Werror=undefined-bool-conversion) add_compile_flag_if_supported(-Werror=tautological-undefined-compare) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_flag_if_supported(-Wdocumentation) # This warning is triggered by every call to qCDebug() add_compile_flag_if_supported(-Wno-gnu-zero-variadic-macro-arguments) endif() if (CMAKE_COMPILER_CXX_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_flag_if_supported(-pedantic) endif() include_directories(${KDevelop_SOURCE_DIR} ${KDevelop_BINARY_DIR}) string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER) if(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug" OR CMAKE_BUILD_TYPE_TOLOWER STREQUAL "") set(COMPILER_OPTIMIZATIONS_DISABLED TRUE) else() set(COMPILER_OPTIMIZATIONS_DISABLED FALSE) endif() # create config-kdevelop.h configure_file(config-kdevelop.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kdevelop.h) add_subdirectory(kdevplatform) add_subdirectory(plugins) add_subdirectory(pics) add_subdirectory(app) add_subdirectory(app_templates) add_subdirectory(file_templates) add_subdirectory(shortcuts) add_subdirectory(doc) set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KDevelop") configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KDevelopConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KDevelopConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) ecm_setup_version(${KDevelop_VERSION_MAJOR}.${KDevelop_VERSION_MINOR}.${KDevelop_VERSION_PATCH} VARIABLE_PREFIX KDEVELOP VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kdevelop_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KDevelopConfigVersion.cmake" ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kdevelop_version.h" DESTINATION "${KDE_INSTALL_INCLUDEDIR}/kdevelop") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KDevelopConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KDevelopConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" ) install(EXPORT KDevelopTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" NAMESPACE KDev:: FILE KDevelopTargets.cmake) # kdebugsettings file install(FILES kdevelop.categories DESTINATION ${KDE_INSTALL_CONFDIR}) # CTestCustom.cmake has to be in the CTEST_BINARY_DIR. # in the KDE build system, this is the same as CMAKE_BINARY_DIR. configure_file(${CMAKE_SOURCE_DIR}/CTestCustom.cmake ${CMAKE_BINARY_DIR}/CTestCustom.cmake) install(FILES org.kde.kdevelop.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) # Make it possible to use the po files fetched by the fetch-translations step -ki18n_install("${CMAKE_CURRENT_BINARY_DIR}/po") +ki18n_install(po) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/app/3rdparty/qtsingleapplication/CMakeLists.txt b/app/3rdparty/qtsingleapplication/CMakeLists.txt new file mode 100644 index 0000000000..fca5aa70e5 --- /dev/null +++ b/app/3rdparty/qtsingleapplication/CMakeLists.txt @@ -0,0 +1,8 @@ +set(qtsingleapplication_SRCS qtlocalpeer.cpp qtsingleapplication.cpp qtlockedfile.cpp) +if(WIN32) + list(APPEND qtsingleapplication_SRCS qtlockedfile_win.cpp) +else() + list(APPEND qtsingleapplication_SRCS qtlockedfile_unix.cpp) +endif() +add_library(qtsingleapplication STATIC ${qtsingleapplication_SRCS}) +target_link_libraries(qtsingleapplication Qt5::Core Qt5::Network Qt5::Widgets) diff --git a/app/qtlocalpeer.cpp b/app/3rdparty/qtsingleapplication/qtlocalpeer.cpp similarity index 100% rename from app/qtlocalpeer.cpp rename to app/3rdparty/qtsingleapplication/qtlocalpeer.cpp diff --git a/app/qtlocalpeer.h b/app/3rdparty/qtsingleapplication/qtlocalpeer.h similarity index 100% rename from app/qtlocalpeer.h rename to app/3rdparty/qtsingleapplication/qtlocalpeer.h diff --git a/app/qtlockedfile.cpp b/app/3rdparty/qtsingleapplication/qtlockedfile.cpp similarity index 100% rename from app/qtlockedfile.cpp rename to app/3rdparty/qtsingleapplication/qtlockedfile.cpp diff --git a/app/qtlockedfile.h b/app/3rdparty/qtsingleapplication/qtlockedfile.h similarity index 100% rename from app/qtlockedfile.h rename to app/3rdparty/qtsingleapplication/qtlockedfile.h diff --git a/app/qtlockedfile_unix.cpp b/app/3rdparty/qtsingleapplication/qtlockedfile_unix.cpp similarity index 100% rename from app/qtlockedfile_unix.cpp rename to app/3rdparty/qtsingleapplication/qtlockedfile_unix.cpp diff --git a/app/qtlockedfile_win.cpp b/app/3rdparty/qtsingleapplication/qtlockedfile_win.cpp similarity index 100% rename from app/qtlockedfile_win.cpp rename to app/3rdparty/qtsingleapplication/qtlockedfile_win.cpp diff --git a/app/qtsingleapplication.cpp b/app/3rdparty/qtsingleapplication/qtsingleapplication.cpp similarity index 100% rename from app/qtsingleapplication.cpp rename to app/3rdparty/qtsingleapplication/qtsingleapplication.cpp diff --git a/app/qtsingleapplication.h b/app/3rdparty/qtsingleapplication/qtsingleapplication.h similarity index 100% rename from app/qtsingleapplication.h rename to app/3rdparty/qtsingleapplication/qtsingleapplication.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 43574b58e4..e343d071a5 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,101 +1,94 @@ find_package(KF5Plasma CONFIG) set_package_properties(KF5Plasma PROPERTIES PURPOSE "Plasma libraries for enabling the plasma addons" URL "http://www.kde.org" TYPE OPTIONAL) if(KF5Plasma_FOUND) add_subdirectory(plasma) endif() option(KDEVELOP_SINGLE_APP "Use QtSingleApplication as KDevelop's base" OFF) -set(singleapp_SRCS qtlocalpeer.cpp qtsingleapplication.cpp qtlockedfile.cpp) -if(WIN32) - set(singleapp_SRCS ${singleapp_SRCS} qtlockedfile_win.cpp) -else() - set(singleapp_SRCS ${singleapp_SRCS} qtlockedfile_unix.cpp) -endif() - set(kdevelop_SRCS main.cpp kdevideextension.cpp) -if(KDEVELOP_SINGLE_APP) - set(kdevelop_SRCS ${kdevelop_SRCS} ${singleapp_SRCS}) -endif() ecm_qt_declare_logging_category(kdevelop_SRCS HEADER debug.h IDENTIFIER APP CATEGORY_NAME "kdevelop.app" ) qt5_add_resources(kdevelop_SRCS kdevelop.qrc) #TODO: remove CMAKE_CURRENT_SOURCE_DIR prefix when ECM is fixed set(kdevelop_ICONS ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-apps-kdevelop.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/32-apps-kdevelop.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/48-apps-kdevelop.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/64-apps-kdevelop.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/128-apps-kdevelop.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/256-apps-kdevelop.png ) ecm_install_icons(ICONS ${kdevelop_ICONS} DESTINATION ${KDE_INSTALL_ICONDIR}) ecm_add_app_icon(kdevelop_SRCS ICONS ${kdevelop_ICONS}) qt5_add_resources(kdevelop_SRCS kdevelop.qrc) add_executable(kdevelop ${kdevelop_SRCS} ) - target_link_libraries(kdevelop KDev::Interfaces KDev::Shell KDev::Util KF5::Declarative KF5::IconThemes KF5::Crash ) IF(APPLE) target_link_libraries(kdevelop "-framework CoreFoundation") ENDIF() +if(KDEVELOP_SINGLE_APP) + add_subdirectory(3rdparty/qtsingleapplication) + target_link_libraries(kdevelop qtsingleapplication) +endif() install(TARGETS kdevelop ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) install(FILES kdevelop! DESTINATION bin PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) configure_file(start-kdevelop.py.in ${CMAKE_CURRENT_BINARY_DIR}/start-kdevelop.py @ONLY) option(INSTALL_KDEVELOP_LAUNCH_WRAPPER "Install a wrapper script that sets required environment variables. Only useful when installing into a separate prefix" OFF) if(INSTALL_KDEVELOP_LAUNCH_WRAPPER) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/start-kdevelop.py DESTINATION bin PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) endif() if (WIN32) install(FILES windows/kdevelop-msvc.bat DESTINATION bin PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) endif (WIN32) if (APPLE) # the MacOSX bundle display name property (CFBundleDisplayName) is not currently supported by cmake, # so has to be set for all targets in this cmake file set(MACOSX_BUNDLE_DISPLAY_NAME KDevelop) # We provide our own Info.plist to disable AppNap for KDevelop: set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in) set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.KDevelop") set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDevelop") set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_DISPLAY_NAME "KDevelop") set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_INFO_STRING "The KDevelop Integrated Development Environment") set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_LONG_VERSION_STRING "KDevelop ${KDevelop_VERSION_MAJOR}.${KDevelop_VERSION_MINOR}") set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING "${KDevelop_VERSION_MAJOR}.${KDevelop_VERSION_MINOR}") set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_BUNDLE_VERSION "${KDevelop_VERSION_MAJOR}.${KDevelop_VERSION_MINOR}") set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_ICON_FILE "kdevelop_SRCS.icns") set_target_properties(kdevelop PROPERTIES MACOSX_BUNDLE_COPYRIGHT "KDevelop Team") endif (APPLE) ########### install files ############### install( PROGRAMS org.kde.kdevelop.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install( PROGRAMS org.kde.kdevelop_ps.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install( FILES kdevelop.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} ) install( FILES kdevelop.xml DESTINATION ${KDE_INSTALL_MIMEDIR} ) update_xdg_mimetypes( ${KDE_INSTALL_MIMEDIR} ) diff --git a/appimage/kdevelop-recipe-centos6.sh b/appimage/kdevelop-recipe-centos6.sh index c7e5edca89..cd99681d32 100755 --- a/appimage/kdevelop-recipe-centos6.sh +++ b/appimage/kdevelop-recipe-centos6.sh @@ -1,513 +1,513 @@ #!/bin/bash # Halt on errors set -e # Be verbose set -x # Now we are inside CentOS 6 grep -r "CentOS release 6" /etc/redhat-release || exit 1 git_pull_rebase_helper() { git fetch git stash || true git rebase $(git rev-parse --abbrev-ref --symbolic-full-name @{u}) || true git stash pop || true } SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" QTVERSION=5.7.1 QVERSION_SHORT=5.7 QTDIR=/usr/local/Qt-${QTVERSION}/ if [ -z "$KDEVELOP_VERSION" ]; then KDEVELOP_VERSION=5.1 fi if [ -z "$KDEV_PG_QT_VERSION" ]; then KDEV_PG_QT_VERSION=2.0 fi -KF5_VERSION=v5.32.0 +KF5_VERSION=v5.37.0 KDE_PLASMA_VERSION=master # need libksysguard master (contains a0e69617442d720c76da5ebe3323e7a977929db4 -- patch which makes plasma dep optional) KDE_APPLICATION_VERSION=v16.12.3 GRANTLEE_VERSION=v5.1.0 export LLVM_ROOT=/opt/llvm/ export PATH=/opt/rh/python27/root/usr/bin/:$PATH export LD_LIBRARY_PATH=/opt/rh/python27/root/usr/lib64:$LD_LIBRARY_PATH # qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment. That's # not always set correctly in CentOS 6.7 export LC_ALL=en_US.UTF-8 export LANG=en_us.UTF-8 # Determine which architecture should be built if [[ "$(arch)" = "i686" || "$(arch)" = "x86_64" ]] ; then ARCH=$(arch) else echo "Architecture could not be determined" exit 1 fi # Make sure we build from the /, parts of this script depends on that. We also need to run as root... cd / # Build AppImageKit #rm -Rf /AppImageKit if [ ! -d AppImageKit ] ; then git clone --depth 1 https://github.com/probonopd/AppImageKit.git /AppImageKit fi cd /AppImageKit/ git checkout stable/v1.0 git_pull_rebase_helper git reset --hard ./build.sh cd / # Use the new compiler . /opt/rh/devtoolset-4/enable # TODO: Use these vars more export FAKEROOT=/kdevelop.appdir export PREFIX=/kdevelop.appdir/usr/ export SRC=$HOME/src/ export BUILD=$HOME/build export CMAKE_PREFIX_PATH=$QTDIR:/kdevelop.appdir/share/llvm/ # if the library path doesn't point to our usr/lib, linking will be broken and we won't find all deps either export LD_LIBRARY_PATH=/usr/lib64/:/usr/lib:/kdevelop.appdir/usr/lib:$QTDIR/lib/:/opt/python3.6/lib/:$LD_LIBRARY_PATH # Workaround for: On CentOS 6, .pc files in /usr/lib/pkgconfig are not recognized # However, this is where .pc files get installed when bulding libraries... (FIXME) # I found this by comparing the output of librevenge's "make install" command # between Ubuntu and CentOS 6 ln -sf /usr/share/pkgconfig /usr/lib/pkgconfig # Prepare the install location if [ -z "$SKIP_PRUNE" ]; then rm -rf /kdevelop.appdir/ || true mkdir -p /kdevelop.appdir/usr # refresh ldconfig cache ldconfig # make sure lib and lib64 are the same thing mkdir -p /kdevelop.appdir/usr/lib cd /kdevelop.appdir/usr ln -s lib lib64 fi # start building the deps function build_project { ( PROJECT=$1 VERSION=$2 # clone if not there mkdir -p $SRC cd $SRC if ( test -d $PROJECT ) then echo "$PROJECT already cloned" cd $PROJECT git stash git reset --hard git fetch git fetch --tags cd .. else if [ -z "$CUSTOM_GIT_URL" ]; then git clone git://anongit.kde.org/$PROJECT else git clone $CUSTOM_GIT_URL fi fi cd $PROJECT git checkout $VERSION git rebase $(git rev-parse --abbrev-ref --symbolic-full-name @{u}) || true # git rebase will fail if a tag is checked out git stash pop || true cd .. if [ "$PROJECT" = "knotifications" ]; then cd $PROJECT echo "patching knotifications" git reset --hard cat > no_phonon.patch << EOF diff --git a/CMakeLists.txt b/CMakeLists.txt index b97425f..8f15f08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,10 +59,10 @@ find_package(KF5Config ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Codecs ${KF5_DEP_VERSION} REQUIRED) find_package(KF5CoreAddons ${KF5_DEP_VERSION} REQUIRED) -find_package(Phonon4Qt5 4.6.60 REQUIRED NO_MODULE) +find_package(Phonon4Qt5 4.6.60 NO_MODULE) set_package_properties(Phonon4Qt5 PROPERTIES DESCRIPTION "Qt-based audio library" - TYPE REQUIRED + TYPE OPTIONAL PURPOSE "Required to build audio notification support") if (Phonon4Qt5_FOUND) add_definitions(-DHAVE_PHONON4QT5) EOF cat no_phonon.patch |patch -p1 cd .. fi # create build dir mkdir -p $BUILD/$PROJECT # go there cd $BUILD/$PROJECT # cmake it cmake3 $SRC/$PROJECT -G Ninja -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=$PREFIX $3 # make ninja # install ninja install ) } function build_framework { ( build_project $1 $KF5_VERSION $2 ) } # KDE Frameworks if [ -z "$SKIP_FRAMEWORKS" ]; then build_framework extra-cmake-modules build_framework kconfig build_framework kguiaddons build_framework ki18n build_framework kitemviews build_framework sonnet build_framework kwindowsystem build_framework kwidgetsaddons build_framework kcompletion build_framework kdbusaddons build_framework karchive build_framework kcoreaddons build_framework kjobwidgets build_framework kcrash build_framework kservice build_framework kcodecs build_framework kauth build_framework kconfigwidgets build_framework kiconthemes build_framework ktextwidgets build_framework kglobalaccel build_framework kxmlgui build_framework kbookmarks build_framework solid build_framework kio build_framework kparts build_framework kitemmodels build_framework threadweaver build_framework attica build_framework knewstuff build_framework syntax-highlighting build_framework ktexteditor build_framework kpackage build_framework kdeclarative build_framework kcmutils build_framework knotifications build_framework knotifyconfig build_framework kdoctools build_framework breeze-icons -DBINARY_ICONS_RESOURCE=1 build_framework kpty build_framework kinit fi # KDE Plasma build_project libksysguard $KDE_PLASMA_VERSION build_project kdecoration $KDE_PLASMA_VERSION # for breeze build_project breeze $KDE_PLASMA_VERSION # KDE Applications build_project libkomparediff2 $KDE_APPLICATION_VERSION build_project kate $KDE_APPLICATION_VERSION # for snippet plugin, see T3826 build_project konsole $KDE_APPLICATION_VERSION # Extra (CUSTOM_GIT_URL=https://github.com/steveire/grantlee.git build_project grantlee $GRANTLEE_VERSION) # KDevelop build_project kdevelop-pg-qt $KDEV_PG_QT_VERSION build_project kdevplatform $KDEVELOP_VERSION build_project kdevelop $KDEVELOP_VERSION build_project kdev-php $KDEVELOP_VERSION # Build kdev-python export LD_LIBRARY_PATH=$LD_LIBRARY_PATH/kdevelop.appdir/usr/lib/ build_project kdev-python $KDEVELOP_VERSION # Install some colorschemes cd $SRC $SCRIPT_DIR/install_colorschemes.sh cd /kdevelop.appdir # FIXME: How to find out which subset of plugins is really needed? I used strace when running the binary mkdir -p ./usr/lib/qt5/plugins/ if [ -e $(dirname $QTDIR/plugins/bearer) ] ; then PLUGINS=$(dirname $QTDIR/plugins/bearer) else PLUGINS=../../$QTVERSION_SHORT/gc*/plugins/ fi echo $PLUGINS # /usr/lib64/qt5/plugins if build system Qt is found cp -r $PLUGINS/bearer ./usr/lib/qt5/plugins/ cp -r $PLUGINS/generic ./usr/lib/qt5/plugins/ cp -r $PLUGINS/imageformats ./usr/lib/qt5/plugins/ cp -r $PLUGINS/platforms ./usr/lib/qt5/plugins/ cp -r $PLUGINS/iconengines ./usr/lib/qt5/plugins/ cp -r $PLUGINS/platforminputcontexts ./usr/lib/qt5/plugins/ # cp -r $PLUGINS/platformthemes ./usr/lib/qt5/plugins/ cp -r $PLUGINS/xcbglintegrations ./usr/lib/qt5/plugins/ cp -R /kdevelop.appdir/usr/lib/grantlee/ /kdevelop.appdir/usr/lib/qt5/plugins/ rm -Rf /kdevelop.appdir/usr/lib/grantlee cp -ru /usr/share/mime/* /kdevelop.appdir/usr/share/mime update-mime-database /kdevelop.appdir/usr/share/mime/ cp -R ./usr/lib/plugins/* ./usr/lib/qt5/plugins/ rm -Rf ./usr/lib/plugins/ cp $(ldconfig -p | grep libsasl2.so.2 | cut -d ">" -f 2 | xargs) ./usr/lib/ # Fedora 23 seemed to be missing SOMETHING from the Centos 6.7. The only message was: # This application failed to start because it could not find or load the Qt platform plugin "xcb". # Setting export QT_DEBUG_PLUGINS=1 revealed the cause. # QLibraryPrivate::loadPlugin failed on "/usr/lib64/qt5/plugins/platforms/libqxcb.so" : # "Cannot load library /usr/lib64/qt5/plugins/platforms/libqxcb.so: (/lib64/libEGL.so.1: undefined symbol: drmGetNodeTypeFromFd)" # Which means that we have to copy libEGL.so.1 in too cp $(ldconfig -p | grep libEGL.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/ # Otherwise F23 cannot load the Qt platform plugin "xcb" cp $(ldconfig -p | grep libxcb.so.1 | cut -d ">" -f 2 | xargs) ./usr/lib/ cp $(ldconfig -p | grep libfreetype.so.6 | cut -d ">" -f 2 | xargs) ./usr/lib/ # For Fedora 20 ldd usr/bin/kdevelop | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true cp /usr/bin/cmake usr/bin/cmake ldd usr/bin/cmake | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true #ldd usr/lib64/kdevelop/*.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true #ldd usr/lib64/plugins/imageformats/*.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true ldd usr/lib/qt5/plugins/platforms/libqxcb.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./usr/lib || true # Copy in the indirect dependencies FILES=$(find . -type f -executable) for FILE in $FILES ; do echo "*** Processing:" $FILE ldd "${FILE}" | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -vu '{}' usr/lib || true done # The following are assumed to be part of the base system rm -f usr/lib/libcom_err.so.2 || true rm -f usr/lib/libcrypt.so.1 || true rm -f usr/lib/libdl.so.2 || true rm -f usr/lib/libexpat.so.1 || true #rm -f usr/lib/libfontconfig.so.1 || true rm -f usr/lib/libgcc_s.so.1 || true rm -f usr/lib/libglib-2.0.so.0 || true rm -f usr/lib/libgpg-error.so.0 || true rm -f usr/lib/libgssapi_krb5.so.2 || true rm -f usr/lib/libgssapi.so.3 || true rm -f usr/lib/libhcrypto.so.4 || true rm -f usr/lib/libheimbase.so.1 || true rm -f usr/lib/libheimntlm.so.0 || true rm -f usr/lib/libhx509.so.5 || true rm -f usr/lib/libICE.so.6 || true rm -f usr/lib/libidn.so.11 || true rm -f usr/lib/libk5crypto.so.3 || true rm -f usr/lib/libkeyutils.so.1 || true rm -f usr/lib/libkrb5.so.26 || true rm -f usr/lib/libkrb5.so.3 || true rm -f usr/lib/libkrb5support.so.0 || true # rm -f usr/lib/liblber-2.4.so.2 || true # needed for debian wheezy # rm -f usr/lib/libldap_r-2.4.so.2 || true # needed for debian wheezy rm -f usr/lib/libm.so.6 || true rm -f usr/lib/libp11-kit.so.0 || true rm -f usr/lib/libpcre.so.3 || true rm -f usr/lib/libpthread.so.0 || true rm -f usr/lib/libresolv.so.2 || true rm -f usr/lib/libroken.so.18 || true rm -f usr/lib/librt.so.1 || true rm -f usr/lib/libSM.so.6 || true rm -f usr/lib/libusb-1.0.so.0 || true rm -f usr/lib/libuuid.so.1 || true rm -f usr/lib/libwind.so.0 || true # Remove these libraries, we need to use the system versions; this means 11.04 is not supported (12.04 is our baseline) rm -f usr/lib/libGL.so.* || true rm -f usr/lib/libdrm.so.* || true #rm -f usr/lib/libz.so.1 || true # These seem to be available on most systems but not Ubuntu 11.04 # rm -f usr/lib/libffi.so.6 usr/lib/libGL.so.1 usr/lib/libglapi.so.0 usr/lib/libxcb.so.1 usr/lib/libxcb-glx.so.0 || true # Delete potentially dangerous libraries rm -f usr/lib/libstdc* usr/lib/libgobject* usr/lib/libc.so.* || true # Do NOT delete libX* because otherwise on Ubuntu 11.04: # loaded library "Xcursor" malloc.c:3096: sYSMALLOc: Assertion (...) Aborted # We don't bundle the developer stuff rm -rf usr/include || true rm -rf usr/lib/cmake || true rm -rf usr/lib/pkgconfig || true rm -rf usr/share/ECM/ || true rm -rf usr/share/gettext || true rm -rf usr/share/pkgconfig || true strip -g $(find usr) || true # We do not bundle this, so let's not search that inside the AppImage. # Fixes "Qt: Failed to create XKB context!" and lets us enter text #sed -i -e 's|././/share/X11/|/usr/share/X11/|g' ./usr/lib/qt5/plugins/platforminputcontexts/libcomposeplatforminputcontextplugin.so #sed -i -e 's|././/share/X11/|/usr/share/X11/|g' ./usr/lib/libQt5XcbQpa.so.5 # Workaround for: # D-Bus library appears to be incorrectly set up; # failed to read machine uuid: Failed to open # The file is more commonly in /etc/machine-id # sed -i -e 's|/var/lib/dbus/machine-id|//././././etc/machine-id|g' ./usr/lib/libdbus-1.so.3 # or rm -f ./usr/lib/libdbus-1.so.3 || true # Remove python rm -f ./usr/bin/python* rm -f ./usr/bin/pydoc* rm -f ./usr/bin/pyenv* # remove big execs rm -f ./usr/bin/verify-uselistorder rm -f ./usr/bin/obj2yaml ./usr/bin/yaml2obj rm -f ./usr/bin/kwrite ./usr/bin/kate cp /kdevelop.appdir/usr/lib/libexec/kf5/* /kdevelop.appdir/usr/bin/ cd / if [ ! -d appimage-exec-wrapper ]; then git clone git://anongit.kde.org/scratch/brauch/appimage-exec-wrapper fi; cd /appimage-exec-wrapper/ make clean make cd /kdevelop.appdir cp -v /appimage-exec-wrapper/exec.so exec_wrapper.so cat > AppRun << EOF #!/bin/bash DIR="\`dirname \"\$0\"\`" DIR="\`( cd \"\$DIR\" && pwd )\`" export APPDIR=\$DIR export LD_PRELOAD=\$DIR/exec_wrapper.so export APPIMAGE_ORIGINAL_QML2_IMPORT_PATH=\$QML2_IMPORT_PATH export APPIMAGE_ORIGINAL_LD_LIBRARY_PATH=\$LD_LIBRARY_PATH export APPIMAGE_ORIGINAL_QT_PLUGIN_PATH=\$QT_PLUGIN_PATH export APPIMAGE_ORIGINAL_XDG_DATA_DIRS=\$XDG_DATA_DIRS export APPIMAGE_ORIGINAL_PATH=\$PATH export APPIMAGE_ORIGINAL_PYTHONHOME=\$PYTHONHOME export QML2_IMPORT_PATH=\$DIR/usr/lib/qml:\$QML2_IMPORT_PATH export LD_LIBRARY_PATH=\$DIR/usr/lib/:\$LD_LIBRARY_PATH export QT_PLUGIN_PATH=\$DIR/usr/lib/qt5/plugins/ export XDG_DATA_DIRS=\$DIR/usr/share/:\$XDG_DATA_DIRS export PATH=\$DIR/usr/bin:\$PATH export KDE_FORK_SLAVES=1 export PYTHONHOME=\$DIR/usr/ export APPIMAGE_STARTUP_QML2_IMPORT_PATH=\$QML2_IMPORT_PATH export APPIMAGE_STARTUP_LD_LIBRARY_PATH=\$LD_LIBRARY_PATH export APPIMAGE_STARTUP_QT_PLUGIN_PATH=\$QT_PLUGIN_PATH export APPIMAGE_STARTUP_XDG_DATA_DIRS=\$XDG_DATA_DIRS export APPIMAGE_STARTUP_PATH=\$PATH export APPIMAGE_STARTUP_PYTHONHOME=\$PYTHONHOME export KDEV_DISABLE_PLUGINS=KDevWelcomePage cd \$HOME kdevelop \$@ EOF chmod +x AppRun cat > kdevelop.desktop << EOF [Desktop Entry] GenericName=Integrated development environment Name=KDevelop MimeType=text/plain; Exec=AppRun -b %U StartupNotify=true X-KDE-HasTempFileOption=true Icon=kdevelop X-DocPath=kdevelop/index.html Type=Application Terminal=false InitialPreference=9 Categories=Qt;KDE;Utility;TextEditor; EOF cp $SRC/kdevelop/app/icons/48-apps-kdevelop.png kdevelop.png cp -R /usr/lib/python3.6 /kdevelop.appdir/usr/lib/ rm -Rf /kdevelop.appdir/usr/lib/python3.6/{test,config-3.5m,__pycache__,site-packages,lib-dynload,distutils,idlelib,unittest,tkinter,ensurepip} mkdir -p /kdevelop.appdir/usr/share/kdevelop/ cp $BUILD/breeze-icons/icons/breeze-icons.rcc /kdevelop.appdir/usr/share/kdevelop/icontheme.rcc rm -Rf /kdevelop.appdir/usr/share/icons/breeze* # not needed because of the rcc rm -f /kdevelop.appdir/usr/bin/llvm* rm -f /kdevelop.appdir/usr/bin/clang* rm -f /kdevelop.appdir/usr/bin/opt rm -f /kdevelop.appdir/usr/bin/lli rm -f /kdevelop.appdir/usr/bin/sancov rm -f /kdevelop.appdir/usr/bin/cmake rm -f /kdevelop.appdir/usr/bin/python rm -Rf /kdevelop.appdir/usr/lib/pkgconfig rm -Rf /kdevelop.appdir/usr/share/man rm -Rf /kdevelop.appdir/usr/share/locale rm -Rf /kdevelop.appdir/usr/lib/libLTO.so #At first it seems like "we shouldn't ship X11", but actually we should; the X11 protocol is sort of guaranteed to stay compatible, #while these libraries are not. # rm -Rf /kdevelop.appdir/usr/lib/libxcb* # add that back in # cp /usr/lib64/libxcb-keysyms.so.1 /kdevelop.appdir/usr/lib/ # rm -Rf /kdevelop.appdir/usr/lib/{libX11.so.6,libXau.so.6,libXext.so.6,libXi.so.6,libXxf86vm.so.1,libX11-xcb.so.1,libXdamage.so.1,libXfixes.so.3,libXrender.so.1} rm -f /kdevelop.appdir/usr/bin/llc rm -f /kdevelop.appdir/usr/bin/bugpoint find /kdevelop.appdir -name '*.a' -exec rm {} \; cd / APP=KDevelop VERSION="git" if [[ "$ARCH" = "x86_64" ]] ; then APPIMAGE=$APP"-"$VERSION"-x86_64.AppImage" fi if [[ "$ARCH" = "i686" ]] ; then APPIMAGE=$APP"-"$VERSION"-i386.AppImage" fi echo $APPIMAGE mkdir -p /out rm -f /out/*.AppImage || true AppImageKit/AppImageAssistant.AppDir/package /kdevelop.appdir/ /out/$APPIMAGE chmod a+rwx /out/$APPIMAGE # So that we can edit the AppImage outside of the Docker container diff --git a/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.cpp b/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.cpp index bef0d4e07d..6407aa26e1 100644 --- a/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.cpp +++ b/plugins/custom-definesandincludes/compilerprovider/gcclikecompiler.cpp @@ -1,214 +1,216 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "gcclikecompiler.h" #include #include #include #include #include #include #include using namespace KDevelop; namespace { QString languageOption(Utils::LanguageType type) { switch (type) { case Utils::C: return QStringLiteral("-xc"); case Utils::Cpp: return QStringLiteral("-xc++"); case Utils::OpenCl: return QStringLiteral("-xcl"); case Utils::Cuda: return QStringLiteral("-xcuda"); case Utils::ObjC: return QStringLiteral("-xobjective-c"); default: Q_UNREACHABLE(); } } QString languageStandard(const QString& arguments) { // TODO: handle -ansi flag: In C mode, this is equivalent to -std=c90. In C++ mode, it is equivalent to -std=c++98. const QRegularExpression regexp(QStringLiteral("-std=(\\S+)")); auto result = regexp.match(arguments); if (result.hasMatch()) return result.captured(0); // no -std= flag passed -> assume c++11 return QStringLiteral("-std=c++11"); } } Defines GccLikeCompiler::defines(Utils::LanguageType type, const QString& arguments) const { auto& data = m_definesIncludes[arguments]; if (!data.definedMacros.isEmpty() ) { return data.definedMacros; } // #define a 1 // #define a QRegExp defineExpression( "#define\\s+(\\S+)(?:\\s+(.*)\\s*)?"); const auto rt = ICore::self()->runtimeController()->currentRuntime(); QProcess proc; proc.setProcessChannelMode( QProcess::MergedChannels ); // TODO: what about -mXXX or -target= flags, some of these change search paths/defines QStringList compilerArguments{languageOption(type), languageStandard(arguments)}; compilerArguments.append(QStringLiteral("-dM")); compilerArguments.append(QStringLiteral("-E")); - compilerArguments.append(QProcess::nullDevice()); + compilerArguments.append(QStringLiteral("-")); + proc.setStandardInputFile(QProcess::nullDevice()); proc.setProgram(path()); proc.setArguments(compilerArguments); rt->startProcess(&proc); if ( !proc.waitForStarted( 2000 ) || !proc.waitForFinished( 2000 ) ) { qCDebug(DEFINESANDINCLUDES) << "Unable to read standard macro definitions from "<< path(); return {}; } if (proc.exitCode() != 0) { qCWarning(DEFINESANDINCLUDES) << "error while fetching defines for the compiler:" << path() << proc.readAll(); return {}; } while ( proc.canReadLine() ) { auto line = proc.readLine(); if ( defineExpression.indexIn( line ) != -1 ) { data.definedMacros[defineExpression.cap( 1 )] = defineExpression.cap( 2 ).trimmed(); } } return data.definedMacros; } Path::List GccLikeCompiler::includes(Utils::LanguageType type, const QString& arguments) const { auto& data = m_definesIncludes[arguments]; if ( !data.includePaths.isEmpty() ) { return data.includePaths; } const auto rt = ICore::self()->runtimeController()->currentRuntime(); QProcess proc; proc.setProcessChannelMode( QProcess::MergedChannels ); // The following command will spit out a bunch of information we don't care // about before spitting out the include paths. The parts we care about // look like this: // #include "..." search starts here: // #include <...> search starts here: // /usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2 // /usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/i486-linux-gnu // /usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/backward // /usr/local/include // /usr/lib/gcc/i486-linux-gnu/4.1.2/include // /usr/include // End of search list. QStringList compilerArguments{languageOption(type), languageStandard(arguments)}; compilerArguments.append(QStringLiteral("-E")); compilerArguments.append(QStringLiteral("-v")); - compilerArguments.append(QProcess::nullDevice()); + compilerArguments.append(QStringLiteral("-")); + proc.setStandardInputFile(QProcess::nullDevice()); proc.setProgram(path()); proc.setArguments(compilerArguments); rt->startProcess(&proc); if ( !proc.waitForStarted( 2000 ) || !proc.waitForFinished( 2000 ) ) { qCDebug(DEFINESANDINCLUDES) << "Unable to read standard include paths from " << path(); return {}; } if (proc.exitCode() != 0) { qCWarning(DEFINESANDINCLUDES) << "error while fetching includes for the compiler:" << path() << proc.readAll(); return {}; } // We'll use the following constants to know what we're currently parsing. enum Status { Initial, FirstSearch, Includes, Finished }; Status mode = Initial; const auto output = QString::fromLocal8Bit( proc.readAllStandardOutput() ); foreach( const auto &line, output.splitRef( '\n' ) ) { switch ( mode ) { case Initial: if ( line.indexOf( QLatin1String("#include \"...\"") ) != -1 ) { mode = FirstSearch; } break; case FirstSearch: if ( line.indexOf( QLatin1String("#include <...>") ) != -1 ) { mode = Includes; } break; case Includes: //Detect the include-paths by the first space that is prepended. Reason: The list may contain relative paths like "." if ( !line.startsWith( ' ' ) ) { // We've reached the end of the list. mode = Finished; } else { // This is an include path, add it to the list. auto hostPath = rt->pathInHost(Path(line.trimmed().toString())); data.includePaths << Path(QFileInfo(hostPath.toLocalFile()).canonicalFilePath()); } break; default: break; } if ( mode == Finished ) { break; } } return data.includePaths; } void GccLikeCompiler::invalidateCache() { m_definesIncludes.clear(); } GccLikeCompiler::GccLikeCompiler(const QString& name, const QString& path, bool editable, const QString& factoryName): ICompiler(name, path, factoryName, editable) { connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, &GccLikeCompiler::invalidateCache); } diff --git a/plugins/custom-definesandincludes/tests/test_definesandincludes.cpp b/plugins/custom-definesandincludes/tests/test_definesandincludes.cpp index 7d890dc8c8..424026a9d0 100644 --- a/plugins/custom-definesandincludes/tests/test_definesandincludes.cpp +++ b/plugins/custom-definesandincludes/tests/test_definesandincludes.cpp @@ -1,153 +1,153 @@ /************************************************************************ * * * Copyright 2010 Andreas Pakulat * * Copyright 2014 Sergey Kalinichev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 or version 3 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ************************************************************************/ #include "test_definesandincludes.h" #include "projectsgenerator.h" #include #include #include #include #include #include #include #include "idefinesandincludesmanager.h" using namespace KDevelop; static IProject* s_currentProject = nullptr; void TestDefinesAndIncludes::cleanupTestCase() { TestCore::shutdown(); } void TestDefinesAndIncludes::initTestCase() { - AutoTestShell::init(); + AutoTestShell::init({QStringLiteral("kdevdefinesandincludesmanager"), QStringLiteral("KDevCustomBuildSystem"), QStringLiteral("KDevStandardOutputView")}); TestCore::initialize(); } void TestDefinesAndIncludes::cleanup() { ICore::self()->projectController()->closeProject( s_currentProject ); } void TestDefinesAndIncludes::loadSimpleProject() { s_currentProject = ProjectsGenerator::GenerateSimpleProject(); QVERIFY( s_currentProject ); auto manager = IDefinesAndIncludesManager::manager(); QVERIFY( manager ); const auto actualIncludes = manager->includes( s_currentProject->projectItem(), IDefinesAndIncludesManager::UserDefined ); const auto actualDefines = manager->defines( s_currentProject->projectItem(), IDefinesAndIncludesManager::UserDefined ); qDebug() << actualDefines << actualIncludes; QCOMPARE( actualIncludes, Path::List() << Path( "/usr/include/mydir") ); Defines defines; defines.insert( QStringLiteral("_DEBUG"), QLatin1String("") ); defines.insert( QStringLiteral("VARIABLE"), QStringLiteral("VALUE") ); QCOMPARE( actualDefines, defines ); QVERIFY(!manager->parserArguments(s_currentProject->projectItem()).isEmpty()); QVERIFY(!manager->parserArguments(QStringLiteral("/some/path/to/file.cpp")).isEmpty()); } void TestDefinesAndIncludes::loadMultiPathProject() { s_currentProject = ProjectsGenerator::GenerateMultiPathProject(); QVERIFY( s_currentProject ); auto manager = IDefinesAndIncludesManager::manager(); QVERIFY( manager ); Path::List includes = Path::List() << Path(QStringLiteral("/usr/include/otherdir")); QHash defines; defines.insert(QStringLiteral("SOURCE"), QStringLiteral("CONTENT")); defines.insert(QStringLiteral("_COPY"), QLatin1String("")); QCOMPARE( manager->includes( s_currentProject->projectItem(), IDefinesAndIncludesManager::UserDefined ), includes ); QCOMPARE( manager->defines( s_currentProject->projectItem(), IDefinesAndIncludesManager::UserDefined ), defines ); QVERIFY(!manager->parserArguments(s_currentProject->projectItem()).isEmpty()); ProjectBaseItem* mainfile = nullptr; for (const auto& file: s_currentProject->fileSet() ) { for (auto i: s_currentProject->filesForPath(file)) { if( i->text() == QLatin1String("main.cpp") ) { mainfile = i; break; } } } QVERIFY(mainfile); includes.prepend(Path(QStringLiteral("/usr/local/include/mydir"))); defines.insert(QStringLiteral("BUILD"), QStringLiteral("debug")); qDebug() << includes << "VS" << manager->includes( mainfile, IDefinesAndIncludesManager::UserDefined ); qDebug() << mainfile << mainfile->path(); QCOMPARE(manager->includes( mainfile, IDefinesAndIncludesManager::UserDefined ), includes); QCOMPARE(defines, manager->defines( mainfile, IDefinesAndIncludesManager::UserDefined )); QVERIFY(!manager->parserArguments(mainfile).isEmpty()); } void TestDefinesAndIncludes::testNoProjectIncludeDirectories() { s_currentProject = ProjectsGenerator::GenerateSimpleProjectWithOutOfProjectFiles(); QVERIFY(s_currentProject); auto manager = KDevelop::IDefinesAndIncludesManager::manager(); QVERIFY(manager); auto projectIncludes = manager->includes(s_currentProject->projectItem(), IDefinesAndIncludesManager::UserDefined); Path includePath1(s_currentProject->path().path() + QDir::separator() + "include1.h"); Path includePath2(s_currentProject->path().path() + QDir::separator() + "include2.h"); QVERIFY(!projectIncludes.contains(includePath1)); QVERIFY(!projectIncludes.contains(includePath2)); auto noProjectIncludes = manager->includes(s_currentProject->path().path() + "/src/main.cpp"); QVERIFY(noProjectIncludes.contains(includePath1)); QVERIFY(noProjectIncludes.contains(includePath2)); QVERIFY(!manager->parserArguments(s_currentProject->projectItem()).isEmpty()); } void TestDefinesAndIncludes::testEmptyProject() { s_currentProject = ProjectsGenerator::GenerateEmptyProject(); QVERIFY(s_currentProject); auto manager = KDevelop::IDefinesAndIncludesManager::manager(); QVERIFY(manager); auto projectIncludes = manager->includes(s_currentProject->projectItem()); auto projectDefines = manager->defines(s_currentProject->projectItem()); auto parserArguments = manager->parserArguments(s_currentProject->projectItem()); QVERIFY(!projectIncludes.isEmpty()); QVERIFY(!projectDefines.isEmpty()); QVERIFY(!parserArguments.isEmpty()); } QTEST_MAIN(TestDefinesAndIncludes) diff --git a/plugins/debuggercommon/midebugger.cpp b/plugins/debuggercommon/midebugger.cpp index 2072ec4f3f..81ca2a71b6 100644 --- a/plugins/debuggercommon/midebugger.cpp +++ b/plugins/debuggercommon/midebugger.cpp @@ -1,374 +1,374 @@ /* * Low level GDB interface. * * Copyright 1999 John Birch * Copyright 2007 Vladimir Prus * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "midebugger.h" #include "debuglog.h" #include "mi/micommand.h" #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #endif // #define DEBUG_NO_TRY //to get a backtrace to where the exception was thrown using namespace KDevMI; using namespace KDevMI::MI; MIDebugger::MIDebugger(QObject* parent) : QObject(parent) , m_process(nullptr) , m_currentCmd(nullptr) { m_process = new KProcess(this); m_process->setOutputChannelMode(KProcess::SeparateChannels); connect(m_process, &KProcess::readyReadStandardOutput, this, &MIDebugger::readyReadStandardOutput); connect(m_process, &KProcess::readyReadStandardError, this, &MIDebugger::readyReadStandardError); connect(m_process, static_cast(&KProcess::finished), this, &MIDebugger::processFinished); connect(m_process, static_cast(&KProcess::error), this, &MIDebugger::processErrored); } MIDebugger::~MIDebugger() { // prevent Qt warning: QProcess: Destroyed while process is still running. if (m_process && m_process->state() == QProcess::Running) { disconnect(m_process, static_cast(&KProcess::error), this, &MIDebugger::processErrored); m_process->kill(); m_process->waitForFinished(10); } } void MIDebugger::execute(MICommand* command) { m_currentCmd = command; QString commandText = m_currentCmd->cmdToSend(); qCDebug(DEBUGGERCOMMON) << "SEND:" << commandText.trimmed(); QByteArray commandUtf8 = commandText.toUtf8(); m_process->write(commandUtf8, commandUtf8.length()); command->markAsSubmitted(); QString prettyCmd = m_currentCmd->cmdToSend(); prettyCmd.remove( QRegExp("set prompt \032.\n") ); prettyCmd = "(gdb) " + prettyCmd; if (m_currentCmd->isUserCommand()) emit userCommandOutput(prettyCmd); else emit internalCommandOutput(prettyCmd); } bool MIDebugger::isReady() const { return m_currentCmd == nullptr; } void MIDebugger::interrupt() { #ifndef Q_OS_WIN int pid = m_process->pid(); if (pid != 0) { ::kill(pid, SIGINT); } #else SetConsoleCtrlHandler(nullptr, true); GenerateConsoleCtrlEvent(0, 0); SetConsoleCtrlHandler(nullptr, false); #endif } MICommand* MIDebugger::currentCommand() const { return m_currentCmd; } void MIDebugger::kill() { m_process->kill(); } void MIDebugger::readyReadStandardOutput() { m_process->setReadChannel(QProcess::StandardOutput); m_buffer += m_process->readAll(); for (;;) { /* In MI mode, all messages are exactly one line. See if we have any complete lines in the buffer. */ int i = m_buffer.indexOf('\n'); if (i == -1) break; QByteArray reply(m_buffer.left(i)); m_buffer = m_buffer.mid(i+1); processLine(reply); } } void MIDebugger::readyReadStandardError() { m_process->setReadChannel(QProcess::StandardError); emit debuggerInternalOutput(QString::fromUtf8(m_process->readAll())); } void MIDebugger::processLine(const QByteArray& line) { qCDebug(DEBUGGERCOMMON) << "Debugger (" << m_process->pid() <<") output: " << line; FileSymbol file; file.contents = line; std::unique_ptr r(m_parser.parse(&file)); if (!r) { // simply ignore the invalid MI message because both gdb and lldb // sometimes produces invalid messages that can be safely ignored. qCDebug(DEBUGGERCOMMON) << "Invalid MI message:" << line; // We don't consider the current command done. // So, if a command results in unparseable reply, // we'll just wait for the "right" reply, which might // never come. However, marking the command as // done in this case is even more risky. // It's probably possible to get here if we're debugging // natively without PTY, though this is uncommon case. return; } #ifndef DEBUG_NO_TRY try { #endif switch(r->kind) { case MI::Record::Result: { MI::ResultRecord& result = static_cast(*r); // it's still possible for the user to issue a MI command, // emit correct signal if (m_currentCmd && m_currentCmd->isUserCommand()) { emit userCommandOutput(QString::fromUtf8(line) + '\n'); } else { emit internalCommandOutput(QString::fromUtf8(line) + '\n'); } // protect against wild replies that sometimes returned from gdb without a pending command if (!m_currentCmd) { qCWarning(DEBUGGERCOMMON) << "Received a result without a pending command"; throw std::runtime_error("Received a result without a pending command"); } else if (m_currentCmd->token() != result.token) { std::stringstream ss; ss << "Received a result with token not matching pending command. " << "Pending: " << m_currentCmd->token() << "Received: " << result.token; qCWarning(DEBUGGERCOMMON) << ss.str().c_str(); throw std::runtime_error(ss.str()); } // GDB doc: "running" and "exit" are status codes equivalent to "done" if (result.reason == QLatin1String("done") || result.reason == QLatin1String("running") || result.reason == QLatin1String("exit")) { qCDebug(DEBUGGERCOMMON) << "Result token is" << result.token; m_currentCmd->markAsCompleted(); qCDebug(DEBUGGERCOMMON) << "Command successful, times " << m_currentCmd->totalProcessingTime() << m_currentCmd->queueTime() << m_currentCmd->gdbProcessingTime(); m_currentCmd->invokeHandler(result); } else if (result.reason == QLatin1String("error")) { qCDebug(DEBUGGERCOMMON) << "Handling error"; m_currentCmd->markAsCompleted(); qCDebug(DEBUGGERCOMMON) << "Command error, times" << m_currentCmd->totalProcessingTime() << m_currentCmd->queueTime() << m_currentCmd->gdbProcessingTime(); // Some commands want to handle errors themself. if (m_currentCmd->handlesError() && m_currentCmd->invokeHandler(result)) { qCDebug(DEBUGGERCOMMON) << "Invoked custom handler\n"; // Done, nothing more needed } else emit error(result); } else { qCDebug(DEBUGGERCOMMON) << "Unhandled result code: " << result.reason; } delete m_currentCmd; m_currentCmd = nullptr; emit ready(); break; } case MI::Record::Async: { MI::AsyncRecord& async = static_cast(*r); switch (async.subkind) { case MI::AsyncRecord::Exec: { // Prefix '*'; asynchronous state changes of the target if (async.reason == QLatin1String("stopped")) { emit programStopped(async); } else if (async.reason == QLatin1String("running")) { emit programRunning(); } else { qCDebug(DEBUGGERCOMMON) << "Unhandled exec notification: " << async.reason; } break; } case MI::AsyncRecord::Notify: { // Prefix '='; supplementary information that we should handle (new breakpoint etc.) emit notification(async); break; } case MI::AsyncRecord::Status: { // Prefix '+'; GDB documentation: // On-going status information about progress of a slow operation; may be ignored break; } default: Q_ASSERT(false); } break; } case MI::Record::Stream: { MI::StreamRecord& s = static_cast(*r); if (s.subkind == MI::StreamRecord::Target) { emit applicationOutput(s.message); } else if (s.subkind == MI::StreamRecord::Console) { if (m_currentCmd && m_currentCmd->isUserCommand()) emit userCommandOutput(s.message); else emit internalCommandOutput(s.message); if (m_currentCmd) m_currentCmd->newOutput(s.message); } else { emit debuggerInternalOutput(s.message); } emit streamRecord(s); break; } case MI::Record::Prompt: break; } #ifndef DEBUG_NO_TRY } catch(const std::exception& e) { KMessageBox::detailedSorry( qApp->activeWindow(), i18nc("Internal debugger error", "

The debugger component encountered internal error while " "processing reply from gdb. Please submit a bug report. " "The debug session will now end to prevent potential crash"), i18n("The exception is: %1\n" "The MI response is: %2", e.what(), QString::fromLatin1(line)), i18n("Internal debugger error")); emit exited(true, e.what()); } #endif } void MIDebugger::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { qCDebug(DEBUGGERCOMMON) << "Debugger FINISHED\n"; bool abnormal = exitCode != 0 || exitStatus != QProcess::NormalExit; emit userCommandOutput(QStringLiteral("Process exited\n")); emit exited(abnormal, i18n("Process exited")); } void MIDebugger::processErrored(QProcess::ProcessError error) { qCDebug(DEBUGGERCOMMON) << "Debugger ERRORED" << error; if(error == QProcess::FailedToStart) { KMessageBox::information( qApp->activeWindow(), i18n("Could not start debugger." "

Could not run '%1'. " "Make sure that the path name is specified correctly.", - debuggerExecutable_), + m_debuggerExecutable), i18n("Could not start debugger")); emit userCommandOutput(QStringLiteral("Process failed to start\n")); emit exited(true, i18n("Process failed to start")); } else if (error == QProcess::Crashed) { KMessageBox::error( qApp->activeWindow(), i18n("Debugger crashed." "

The debugger process '%1' crashed.
" "Because of that the debug session has to be ended.
" "Try to reproduce the crash without KDevelop and report a bug.
", - debuggerExecutable_), + m_debuggerExecutable), i18n("Debugger crashed")); emit userCommandOutput(QStringLiteral("Process crashed\n")); emit exited(true, i18n("Process crashed")); } } diff --git a/plugins/debuggercommon/midebugger.h b/plugins/debuggercommon/midebugger.h index 5677444be1..f934c029cf 100644 --- a/plugins/debuggercommon/midebugger.h +++ b/plugins/debuggercommon/midebugger.h @@ -1,151 +1,151 @@ /* * Low level Debugger MI interface. * * Copyright 2007 Vladimir Prus * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MIDEBUGGER_H #define MIDEBUGGER_H #include "mi/mi.h" #include "mi/miparser.h" #include #include #include class KConfigGroup; class KProcess; namespace KDevMI { namespace MI { class MICommand; } class MIDebugger : public QObject { Q_OBJECT public: explicit MIDebugger(QObject* parent = nullptr); ~MIDebugger() override; /** Starts the debugger. This should be done after connecting to all signals the client is interested in. */ virtual bool start(KConfigGroup& config, const QStringList& extraArguments = {}) = 0; /** Executes a command. This method may be called at most once each time 'ready' is emitted. When the debugger instance is just constructed, one should wait for 'ready' as well. The ownership of 'command' is transferred to the debugger. */ void execute(MI::MICommand* command); /** Returns true if 'execute' can be called immediately. */ bool isReady() const; /** FIXME: temporary, to be eliminated. */ MI::MICommand* currentCommand() const; /** Arrange to debugger to stop doing whatever it's doing, and start waiting for a command. FIXME: probably should make sure that 'ready' is emitted, or something. */ void interrupt(); /** Kills the debugger. */ void kill(); Q_SIGNALS: /** Emitted when debugger becomes ready -- i.e. when isReady call will return true. */ void ready(); /** Emitted when the debugger itself exits. This could happen because it just crashed due to internal bug, or we killed it explicitly. */ void exited(bool abnormal, const QString &msg); /** Emitted when debugger reports stop, with 'r' being the data provided by the debugger. */ void programStopped(const MI::AsyncRecord& r); /** Emitted when debugger believes that the program is running. */ void programRunning(); /** Emitted for each MI stream record found. Presently only used to recognize some CLI messages that mean that the program has died. FIXME: connect to parseCliLine */ void streamRecord(const MI::StreamRecord& s); /** Reports an async notification record. */ void notification(const MI::AsyncRecord& n); /** Emitted for error that is not handled by the command being executed. */ void error(const MI::ResultRecord& s); /** Reports output from the running application. Generally output will only be available when using remote debugger targets. When running locally, the output will either appear on debugger stdout, and ignored, or routed via pty. */ void applicationOutput(const QString& s); /** Reports output of a command explicitly typed by the user, or output from .gdbinit commands. */ void userCommandOutput(const QString& s); /** Reports output of a command issued internally by KDevelop. */ void internalCommandOutput(const QString& s); /** Reports debugger interal output, including stderr output from debugger and the 'log' MI channel */ void debuggerInternalOutput(const QString& s); protected Q_SLOTS: void readyReadStandardOutput(); void readyReadStandardError(); void processFinished(int exitCode, QProcess::ExitStatus exitStatus); void processErrored(QProcess::ProcessError); protected: void processLine(const QByteArray& line); protected: - QString debuggerExecutable_; + QString m_debuggerExecutable; KProcess* m_process; MI::MICommand* m_currentCmd; MI::MIParser m_parser; /** The unprocessed output from debugger. Output is processed as soon as we see newline. */ QByteArray m_buffer; }; } #endif diff --git a/plugins/gdb/gdb.cpp b/plugins/gdb/gdb.cpp index 463cbe237d..993e657167 100644 --- a/plugins/gdb/gdb.cpp +++ b/plugins/gdb/gdb.cpp @@ -1,98 +1,98 @@ /* * Low level GDB interface. * * Copyright 1999 John Birch * Copyright 2007 Vladimir Prus * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gdb.h" #include "dbgglobal.h" #include "debuglog.h" #include #include #include #include #include #include #include using namespace KDevMI::GDB; using namespace KDevMI::MI; GdbDebugger::GdbDebugger(QObject* parent) : MIDebugger(parent) { } GdbDebugger::~GdbDebugger() { } bool GdbDebugger::start(KConfigGroup& config, const QStringList& extraArguments) { // FIXME: verify that default value leads to something sensible QUrl gdbUrl = config.readEntry(Config::GdbPathEntry, QUrl()); if (gdbUrl.isEmpty()) { - debuggerExecutable_ = QStringLiteral("gdb"); + m_debuggerExecutable = QStringLiteral("gdb"); } else { // FIXME: verify its' a local path. - debuggerExecutable_ = gdbUrl.url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash); + m_debuggerExecutable = gdbUrl.url(QUrl::PreferLocalFile | QUrl::StripTrailingSlash); } QStringList arguments = extraArguments; arguments << QStringLiteral("--interpreter=mi2") << QStringLiteral("-quiet"); QUrl shell = config.readEntry(Config::DebuggerShellEntry, QUrl()); if(!shell.isEmpty()) { qCDebug(DEBUGGERGDB) << "have shell" << shell; QString shell_without_args = shell.toLocalFile().split(QChar(' ')).first(); QFileInfo info(shell_without_args); /*if( info.isRelative() ) { shell_without_args = build_dir + "/" + shell_without_args; info.setFile( shell_without_args ); }*/ if(!info.exists()) { KMessageBox::information( qApp->activeWindow(), i18n("Could not locate the debugging shell '%1'.", shell_without_args ), i18n("Debugging Shell Not Found") ); return false; } - arguments.insert(0, debuggerExecutable_); + arguments.insert(0, m_debuggerExecutable); arguments.insert(0, shell.toLocalFile()); m_process->setShellCommand(KShell::joinArgs(arguments)); } else { - m_process->setProgram(debuggerExecutable_, arguments); + m_process->setProgram(m_debuggerExecutable, arguments); } m_process->start(); - qCDebug(DEBUGGERGDB) << "Starting GDB with command" << shell.toLocalFile() + QLatin1Char(' ') + debuggerExecutable_ + qCDebug(DEBUGGERGDB) << "Starting GDB with command" << shell.toLocalFile() + QLatin1Char(' ') + m_debuggerExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')); qCDebug(DEBUGGERGDB) << "GDB process pid:" << m_process->pid(); - emit userCommandOutput(shell.toLocalFile() + QLatin1Char(' ') + debuggerExecutable_ + emit userCommandOutput(shell.toLocalFile() + QLatin1Char(' ') + m_debuggerExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')) + QLatin1Char('\n')); return true; } diff --git a/plugins/lldb/lldbdebugger.cpp b/plugins/lldb/lldbdebugger.cpp index 14bd17761d..f621e3b869 100644 --- a/plugins/lldb/lldbdebugger.cpp +++ b/plugins/lldb/lldbdebugger.cpp @@ -1,85 +1,85 @@ /* * Low level LLDB interface. * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "lldbdebugger.h" #include "dbgglobal.h" #include "debuglog.h" #include #include #include #include #include using namespace KDevelop; using namespace KDevMI::LLDB; using namespace KDevMI::MI; LldbDebugger::LldbDebugger(QObject* parent) : MIDebugger(parent) { } LldbDebugger::~LldbDebugger() { } bool LldbDebugger::start(KConfigGroup& config, const QStringList& extraArguments) { // Get path to executable QUrl lldbUrl = config.readEntry(Config::LldbExecutableEntry, QUrl()); if (!lldbUrl.isValid() || !lldbUrl.isLocalFile()) { - debuggerExecutable_ = QStringLiteral("lldb-mi"); + m_debuggerExecutable = QStringLiteral("lldb-mi"); } else { - debuggerExecutable_ = lldbUrl.toLocalFile(); + m_debuggerExecutable = lldbUrl.toLocalFile(); } // Get arguments QStringList arguments = extraArguments; //arguments << "-quiet"; arguments.append(KShell::splitArgs(config.readEntry(Config::LldbArgumentsEntry, QString()))); // Get environment const EnvironmentProfileList egl(config.config()); const auto &envs = egl.variables(config.readEntry(Config::LldbEnvironmentEntry, egl.defaultProfileName())); QProcessEnvironment processEnv; if (config.readEntry(Config::LldbInheritSystemEnvEntry, true)) { processEnv = QProcessEnvironment::systemEnvironment(); } for (auto it = envs.begin(), ite = envs.end(); it != ite; ++it) { processEnv.insert(it.key(), it.value()); } // Start! m_process->setProcessEnvironment(processEnv); - m_process->setProgram(debuggerExecutable_, arguments); + m_process->setProgram(m_debuggerExecutable, arguments); m_process->start(); - qCDebug(DEBUGGERLLDB) << "Starting LLDB with command" << debuggerExecutable_ + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')); + qCDebug(DEBUGGERLLDB) << "Starting LLDB with command" << m_debuggerExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')); qCDebug(DEBUGGERLLDB) << "LLDB process pid:" << m_process->pid(); - emit userCommandOutput(debuggerExecutable_ + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')) + QLatin1Char('\n')); + emit userCommandOutput(m_debuggerExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' ')) + QLatin1Char('\n')); return true; } diff --git a/plugins/lldb/unittests/test_lldb.cpp b/plugins/lldb/unittests/test_lldb.cpp index d0803146f7..a6dcd25332 100644 --- a/plugins/lldb/unittests/test_lldb.cpp +++ b/plugins/lldb/unittests/test_lldb.cpp @@ -1,1898 +1,1903 @@ /* * Unit tests for LLDB debugger plugin Copyright 2009 Niko Sams Copyright 2013 Vlas Puhov * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "test_lldb.h" #include "controllers/framestackmodel.h" #include "debugsession.h" #include "tests/debuggers-tests-config.h" #include "tests/testhelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WAIT_FOR_STATE(session, state) \ do { if (!KDevMI::waitForState((session), (state), __FILE__, __LINE__)) return; } while (0) #define WAIT_FOR_STATE_AND_IDLE(session, state) \ do { if (!KDevMI::waitForState((session), (state), __FILE__, __LINE__, true)) return; } while (0) #define WAIT_FOR_A_WHILE(session, ms) \ do { if (!KDevMI::waitForAWhile((session), (ms), __FILE__, __LINE__)) return; } while (0) #define WAIT_FOR(session, condition) \ do { \ KDevMI::TestWaiter w((session), #condition, __FILE__, __LINE__); \ while (w.waitUnless((condition))) /* nothing */ ; \ } while(0) #define COMPARE_DATA(index, expected) \ do { if (!KDevMI::compareData((index), (expected), __FILE__, __LINE__)) return; } while (0) #define SKIP_IF_ATTACH_FORBIDDEN() \ do { \ if (KDevMI::isAttachForbidden(__FILE__, __LINE__)) \ return; \ } while(0) using namespace KDevelop; using namespace KDevMI::LLDB; using KDevMI::findExecutable; using KDevMI::findFile; using KDevMI::findSourceFile; namespace { class WritableEnvironmentProfileList : public EnvironmentProfileList { public: explicit WritableEnvironmentProfileList(KConfig* config) : EnvironmentProfileList(config) {} using EnvironmentProfileList::variables; using EnvironmentProfileList::saveSettings; using EnvironmentProfileList::removeProfile; }; class TestLaunchConfiguration : public ILaunchConfiguration { public: explicit TestLaunchConfiguration(const QUrl& executable = findExecutable(QStringLiteral("debuggee_debugee")), const QUrl& workingDirectory = QUrl()) { qDebug() << "FIND" << executable; c = new KConfig(); c->deleteGroup("launch"); cfg = c->group("launch"); cfg.writeEntry("isExecutable", true); cfg.writeEntry("Executable", executable); cfg.writeEntry("Working Directory", workingDirectory); } ~TestLaunchConfiguration() override { delete c; } const KConfigGroup config() const override { return cfg; } KConfigGroup config() override { return cfg; }; QString name() const override { return QStringLiteral("Test-Launch"); } KDevelop::IProject* project() const override { return nullptr; } KDevelop::LaunchConfigurationType* type() const override { return nullptr; } KConfig *rootConfig() { return c; } private: KConfigGroup cfg; KConfig *c; }; class TestFrameStackModel : public LldbFrameStackModel { public: explicit TestFrameStackModel(DebugSession* session) : LldbFrameStackModel(session), fetchFramesCalled(0), fetchThreadsCalled(0) {} void fetchFrames(int threadNumber, int from, int to) override { fetchFramesCalled++; LldbFrameStackModel::fetchFrames(threadNumber, from, to); } void fetchThreads() override { fetchThreadsCalled++; LldbFrameStackModel::fetchThreads(); } int fetchFramesCalled; int fetchThreadsCalled; }; class TestDebugSession : public DebugSession { Q_OBJECT public: TestDebugSession() : DebugSession() { // explicit set formatter path to force use in-tree formatters, not the one installed in system. auto formatter = findFile(LLDB_SRC_DIR, "formatters/all.py"); setFormatterPath(formatter); setSourceInitFile(false); m_frameStackModel = new TestFrameStackModel(this); KDevelop::ICore::self()->debugController()->addSession(this); } TestFrameStackModel* frameStackModel() const override { return m_frameStackModel; } private: TestFrameStackModel* m_frameStackModel; }; } // end of anonymous namespace BreakpointModel* LldbTest::breakpoints() { return m_core->debugController()->breakpointModel(); } VariableCollection *LldbTest::variableCollection() { return m_core->debugController()->variableCollection(); } Variable *LldbTest::watchVariableAt(int i) { auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); auto idx = variableCollection()->index(i, 0, watchRoot); return dynamic_cast(variableCollection()->itemForIndex(idx)); } QModelIndex LldbTest::localVariableIndexAt(int i, int col) { auto localRoot = variableCollection()->indexForItem(variableCollection()->locals(), 0); return variableCollection()->index(i, col, localRoot); } // Called before the first testfunction is executed void LldbTest::initTestCase() { AutoTestShell::init(); m_core = TestCore::initialize(Core::NoUi); m_iface = m_core->pluginController() ->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute")) ->extension(); Q_ASSERT(m_iface); m_debugeeFileName = findSourceFile("debugee.cpp"); + + const QString lldbMiExecutable = QStandardPaths::findExecutable(QStringLiteral("lldb-mi")); + if (lldbMiExecutable.isEmpty()) { + QSKIP("Skipping, lldb-mi not available"); + } } // Called after the last testfunction was executed void LldbTest::cleanupTestCase() { TestCore::shutdown(); } // Called before each testfunction is executed void LldbTest::init() { //remove all breakpoints - so we can set our own in the test KConfigGroup bpCfg = KSharedConfig::openConfig()->group("breakpoints"); bpCfg.writeEntry("number", 0); bpCfg.sync(); breakpoints()->removeRows(0, breakpoints()->rowCount()); while (variableCollection()->watches()->childCount() > 0) { auto var = watchVariableAt(0); if (!var) break; var->die(); } } void LldbTest::cleanup() { // Called after every testfunction } void LldbTest::testStdout() { TestDebugSession *session = new TestDebugSession; QSignalSpy outputSpy(session, &TestDebugSession::inferiorStdoutLines); TestLaunchConfiguration cfg; QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, KDevelop::IDebugSession::EndedState); QVERIFY(outputSpy.count() > 0); QStringList outputLines; while (outputSpy.count() > 0) { QList arguments = outputSpy.takeFirst(); for (const auto &item : arguments) { outputLines.append(item.toStringList()); } } QCOMPARE(outputLines, QStringList() << "Hello, world!" << "Hello"); } void LldbTest::testEnvironmentSet() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeechoenv"))); cfg.config().writeEntry("EnvironmentGroup", "LldbTestGroup"); WritableEnvironmentProfileList envProfiles(cfg.rootConfig()); envProfiles.removeProfile(QStringLiteral("LldbTestGroup")); auto &envs = envProfiles.variables(QStringLiteral("LldbTestGroup")); envs[QStringLiteral("VariableA")] = QStringLiteral("-A' \" complex --value"); envs[QStringLiteral("VariableB")] = QStringLiteral("-B' \" complex --value"); envProfiles.saveSettings(cfg.rootConfig()); QSignalSpy outputSpy(session, &TestDebugSession::inferiorStdoutLines); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, KDevelop::IDebugSession::EndedState); QVERIFY(outputSpy.count() > 0); QStringList outputLines; while (outputSpy.count() > 0) { QList arguments = outputSpy.takeFirst(); for (const auto &item : arguments) { outputLines.append(item.toStringList()); } } QCOMPARE(outputLines, QStringList() << "-A' \" complex --value" << "-B' \" complex --value"); } void LldbTest::testBreakpoint() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 29); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); QCOMPARE(session->currentLine(), 29); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); } void LldbTest::testBreakOnStart() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; cfg.config().writeEntry(KDevMI::Config::BreakOnStartEntry, true); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); // line 28 is the start of main function in debugee.cpp QCOMPARE(session->currentLine(), 27); // currentLine is zero-based session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testDisableBreakpoint() { QSKIP("Skipping... In lldb-mi -d flag has no effect when mixed with -f"); //Description: We must stop only on the third breakpoint int firstBreakLine=28; int secondBreakLine=23; int thirdBreakLine=24; int fourthBreakLine=31; TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint *b; b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), firstBreakLine); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //this is needed to emulate debug from GUI. If we are in edit mode, the debugSession doesn't exist. m_core->debugController()->breakpointModel()->blockSignals(true); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), secondBreakLine); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //all disabled breakpoints were added auto *thirdBreak = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), thirdBreakLine); m_core->debugController()->breakpointModel()->blockSignals(false); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), thirdBreak->line()); //disable existing breakpoint thirdBreak->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //add another disabled breakpoint b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), fourthBreakLine); WAIT_FOR_A_WHILE(session, 300); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); WAIT_FOR_A_WHILE(session, 300); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testChangeLocationBreakpoint() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; auto *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 27); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 27); WAIT_FOR_A_WHILE(session, 100); b->setLine(28); WAIT_FOR_A_WHILE(session, 100); session->run(); WAIT_FOR_A_WHILE(session, 100); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 28); WAIT_FOR_A_WHILE(session, 500); breakpoints()->setData(breakpoints()->index(0, KDevelop::Breakpoint::LocationColumn), QString(m_debugeeFileName+":30")); QCOMPARE(b->line(), 29); WAIT_FOR_A_WHILE(session, 100); QCOMPARE(b->line(), 29); session->run(); WAIT_FOR_A_WHILE(session, 100); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 29); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testDeleteBreakpoint() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; QCOMPARE(breakpoints()->rowCount(), 0); //add breakpoint before startDebugging breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 21); QCOMPARE(breakpoints()->rowCount(), 1); breakpoints()->removeRow(0); QCOMPARE(breakpoints()->rowCount(), 0); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 22); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); breakpoints()->removeRow(0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testPendingBreakpoint() { QSKIP("Skipping... Pending breakpoint not work on lldb-mi"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 28); auto * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("test_lldb.cpp")), 10); QCOMPARE(b->state(), Breakpoint::NotStartedState); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QCOMPARE(b->state(), Breakpoint::PendingState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testUpdateBreakpoint() { // Description: user might insert breakpoints using lldb console. model should // pick up the manually set breakpoint TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; // break at line 29 auto b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 28); QCOMPARE(breakpoints()->rowCount(), 1); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // stop at line 29 session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // stop after step QCOMPARE(session->currentLine(), 23-1); // at the beginning of foo():23: ++i; session->addUserCommand(QStringLiteral("break set --file %1 --line %2").arg(m_debugeeFileName).arg(33)); WAIT_FOR_A_WHILE(session, 20); QCOMPARE(breakpoints()->rowCount(), 2); b = breakpoints()->breakpoint(1); QCOMPARE(b->url(), QUrl::fromLocalFile(m_debugeeFileName)); QCOMPARE(b->line(), 33-1); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); // stop at line 25 QCOMPARE(session->currentLine(), 33-1); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testIgnoreHitsBreakpoint() { QSKIP("Skipping... lldb-mi doesn't provide breakpoint hit count update"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; KDevelop::Breakpoint * b1 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 21); b1->setIgnoreHits(1); KDevelop::Breakpoint * b2 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 22); QVERIFY(session->startDebugging(&cfg, m_iface)); //WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR(session, session->state() == DebugSession::PausedState && b2->hitCount() == 1); b2->setIgnoreHits(1); session->run(); //WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR(session, session->state() == DebugSession::PausedState && b1->hitCount() == 1); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testConditionBreakpoint() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; auto b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 39); b->setCondition(QStringLiteral("x[0] == 'H'")); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 23); b->setCondition(QStringLiteral("i==2")); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR(session, session->state() == DebugSession::PausedState && session->currentLine() == 24); b->setCondition(QStringLiteral("i == 0")); WAIT_FOR_A_WHILE(session, 100); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 39); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnWriteBreakpoint() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); breakpoints()->addWatchpoint(QStringLiteral("i")); WAIT_FOR_A_WHILE(session, 100); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnWriteWithConditionBreakpoint() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); KDevelop::Breakpoint *b = breakpoints()->addWatchpoint(QStringLiteral("i")); b->setCondition(QStringLiteral("i==2")); QTest::qWait(100); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnReadBreakpoint() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addReadWatchpoint(QStringLiteral("foo::i")); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnReadBreakpoint2() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); breakpoints()->addReadWatchpoint(QStringLiteral("i")); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 22); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakOnAccessBreakpoint() { QSKIP("Skipping... lldb-mi doesn't have proper watchpoint support"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); breakpoints()->addAccessWatchpoint(QStringLiteral("i")); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 22); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testInsertBreakpointWhileRunning() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeslow"))); QString fileName = findSourceFile("debugeeslow.cpp"); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::ActiveState); WAIT_FOR_A_WHILE(session, 2000); qDebug() << "adding breakpoint"; KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); WAIT_FOR_A_WHILE(session, 500); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 500); QCOMPARE(session->currentLine(), 25); b->setDeleted(); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testInsertBreakpointWhileRunningMultiple() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeslow"))); QString fileName = findSourceFile("debugeeslow.cpp"); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::ActiveState); WAIT_FOR_A_WHILE(session, 2000); qDebug() << "adding breakpoint"; auto b1 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 24); auto b2 = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); WAIT_FOR_A_WHILE(session, 500); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 500); QCOMPARE(session->currentLine(), 24); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 500); QCOMPARE(session->currentLine(), 25); b1->setDeleted(); b2->setDeleted(); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testInsertBreakpointFunctionName() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 27); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testManualBreakpoint() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 27); breakpoints()->removeRows(0, 1); WAIT_FOR_A_WHILE(session, 100); QCOMPARE(breakpoints()->rowCount(), 0); session->addCommand(MI::NonMI, QStringLiteral("break set --file debugee.cpp --line 23")); WAIT_FOR_A_WHILE(session, 100); QCOMPARE(breakpoints()->rowCount(), 1); auto b = breakpoints()->breakpoint(0); QCOMPARE(b->line(), 22); session->addCommand(MI::NonMI, QStringLiteral("break disable 2")); session->addCommand(MI::NonMI, QStringLiteral("break modify -c 'i == 1' 2")); session->addCommand(MI::NonMI, QStringLiteral("break modify -i 1 2")); WAIT_FOR_A_WHILE(session, 1000); QCOMPARE(b->enabled(), false); QEXPECT_FAIL("", "LLDB 4.0 does not report condition in mi response", Continue); QCOMPARE(b->condition(), QString("i == 1")); QEXPECT_FAIL("", "LLDB 4.0 does not report ignore hits in mi response", Continue); QCOMPARE(b->ignoreHits(), 1); session->addCommand(MI::NonMI, QStringLiteral("break delete 2")); WAIT_FOR_A_WHILE(session, 100); QEXPECT_FAIL("", "LLDB 4.0 does not report breakpoint deletion as mi notification", Continue); QCOMPARE(breakpoints()->rowCount(), 0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } //Bug 201771 void LldbTest::testInsertAndRemoveBreakpointWhileRunning() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeslow"))); QString fileName = findSourceFile("debugeeslow.cpp"); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::ActiveState); WAIT_FOR_A_WHILE(session, 1000); KDevelop::Breakpoint *b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); WAIT_FOR_A_WHILE(session, 200); // wait for feedback notification from lldb-mi b->setDeleted(); WAIT_FOR_A_WHILE(session, 3000); // give slow debugee extra time to run WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testPickupManuallyInsertedBreakpoint() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->addCommand(MI::NonMI, QStringLiteral("break set --file debugee.cpp --line 32")); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 2); QCOMPARE(breakpoints()->rowCount(), 2); KDevelop::Breakpoint *b = breakpoints()->breakpoint(1); QVERIFY(b); QCOMPARE(b->line(), 31); //we start with 0, gdb with 1 QCOMPARE(b->url().fileName(), QString("debugee.cpp")); } //Bug 270970 void LldbTest::testPickupManuallyInsertedBreakpointOnlyOnce() { TestDebugSession *session = new TestDebugSession; QString sourceFile = findSourceFile("debugee.cpp"); //inject here, so it behaves similar like a command from .lldbinit QTemporaryFile configScript; configScript.open(); configScript.write(QStringLiteral("break set --file %0 --line 32\n").arg(sourceFile).toLocal8Bit()); configScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::LldbConfigScriptEntry, QUrl::fromLocalFile(configScript.fileName())); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(QStringLiteral("debugee.cpp")), 31); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 1); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakpointWithSpaceInPath() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeespace"))); KConfigGroup grp = cfg.config(); QString fileName = findSourceFile("debugee space.cpp"); KDevelop::Breakpoint * b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 20); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 20); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBreakpointDisabledOnStart() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; auto b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 23); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 29); b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 34); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QEXPECT_FAIL("", "See LLDB bug 28703: -d flag has no effect", Abort); QCOMPARE(session->currentLine(), 29); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Checked); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 34); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testMultipleLocationsBreakpoint() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeemultilocbreakpoint"))); breakpoints()->addCodeBreakpoint(QStringLiteral("aPlusB")); //TODO check if the additional location breakpoint is added QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 19); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testMultipleBreakpoint() { TestDebugSession *session = new TestDebugSession; //there'll be about 3-4 breakpoints, but we treat it like one. TestLaunchConfiguration c(findExecutable(QStringLiteral("debuggee_debugeemultiplebreakpoint"))); auto b = breakpoints()->addCodeBreakpoint(QStringLiteral("debugeemultiplebreakpoint.cpp:52")); QVERIFY(session->startDebugging(&c, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 1); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testRegularExpressionBreakpoint() { QSKIP("Skipping... lldb has only one breakpoint for multiple locations" " (and lldb-mi returns the first one), not support this yet"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration c(findExecutable(QStringLiteral("debuggee_debugeemultilocbreakpoint"))); breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&c, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->addCommand(MI::NonMI, QStringLiteral("break set --func-regex .*aPl.*B")); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(breakpoints()->breakpoints().count(), 3); session->addCommand(MI::BreakDelete, QLatin1String("")); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testChangeBreakpointWhileRunning() { QSKIP("Skipping... lldb-mi command -break-enable doesn't enable breakpoint"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration c(findExecutable(QStringLiteral("debuggee_debugeeslow"))); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QStringLiteral("debugeeslow.cpp:25")); QVERIFY(session->startDebugging(&c, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QVERIFY(session->currentLine() >= 24 && session->currentLine() <= 26 ); session->run(); WAIT_FOR_STATE(session, DebugSession::ActiveState); qDebug() << "Disabling breakpoint"; b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); //to make one loop WAIT_FOR_A_WHILE(session, 2500); qDebug() << "Waiting for active"; WAIT_FOR_STATE(session, DebugSession::ActiveState); qDebug() << "Enabling breakpoint"; // Use native user command works, but not through -break-enable, which is triggered by setData session->addCommand(MI::NonMI, QStringLiteral("break enable")); //b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Checked); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); b->setData(KDevelop::Breakpoint::EnableColumn, Qt::Unchecked); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testCatchpoint() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeexception"))); session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestFrameStackModel* fsModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("debugeeexception.cpp")), 29); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(fsModel->currentFrame(), 0); QCOMPARE(session->currentLine(), 29); session->addCommand(MI::NonMI, QStringLiteral("break set -E c++")); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); const auto frames = fsModel->frames(fsModel->currentThread()); QVERIFY(frames.size() >= 2); // frame 0 is somewhere inside libstdc++ QCOMPARE(frames[1].file, QUrl::fromLocalFile(findSourceFile("debugeeexception.cpp"))); QCOMPARE(frames[1].line, 22); QCOMPARE(breakpoints()->rowCount(),2); QVERIFY(!breakpoints()->breakpoint(0)->location().isEmpty()); QVERIFY(!breakpoints()->breakpoint(1)->location().isEmpty()); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testShowStepInSource() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; QSignalSpy showStepInSourceSpy(session, &TestDebugSession::showStepInSource); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 29); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); { QCOMPARE(showStepInSourceSpy.count(), 3); QList arguments = showStepInSourceSpy.takeFirst(); QCOMPARE(arguments.first().value(), QUrl::fromLocalFile(m_debugeeFileName)); QCOMPARE(arguments.at(1).toInt(), 29); arguments = showStepInSourceSpy.takeFirst(); QCOMPARE(arguments.first().value(), QUrl::fromLocalFile(m_debugeeFileName)); QCOMPARE(arguments.at(1).toInt(), 22); arguments = showStepInSourceSpy.takeFirst(); QCOMPARE(arguments.first().value(), QUrl::fromLocalFile(m_debugeeFileName)); QCOMPARE(arguments.at(1).toInt(), 23); } } void LldbTest::testStack() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 21); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QModelIndex tIdx = stackModel->index(0,0); QCOMPARE(stackModel->rowCount(QModelIndex()), 1); QCOMPARE(stackModel->columnCount(QModelIndex()), 3); COMPARE_DATA(tIdx, "#1 at foo()"); QCOMPARE(stackModel->rowCount(tIdx), 4); QCOMPARE(stackModel->columnCount(tIdx), 3); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "foo()"); COMPARE_DATA(stackModel->index(0, 2, tIdx), m_debugeeFileName+":23"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(1, 2, tIdx), m_debugeeFileName+":29"); COMPARE_DATA(stackModel->index(2, 0, tIdx), "2"); COMPARE_DATA(stackModel->index(2, 1, tIdx), "__libc_start_main"); COMPARE_DATA(stackModel->index(3, 0, tIdx), "3"); COMPARE_DATA(stackModel->index(3, 1, tIdx), "_start"); session->stepOut(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); COMPARE_DATA(tIdx, "#1 at main"); QCOMPARE(stackModel->rowCount(tIdx), 3); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(0, 2, tIdx), m_debugeeFileName+":30"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "__libc_start_main"); COMPARE_DATA(stackModel->index(2, 0, tIdx), "2"); COMPARE_DATA(stackModel->index(2, 1, tIdx), "_start"); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testStackFetchMore() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeerecursion"))); QString fileName = findSourceFile("debugeerecursion.cpp"); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 25); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->frameStackModel()->fetchFramesCalled, 1); QModelIndex tIdx = stackModel->index(0,0); QCOMPARE(stackModel->rowCount(QModelIndex()), 1); QCOMPARE(stackModel->columnCount(QModelIndex()), 3); COMPARE_DATA(tIdx, "#1 at foo()"); QCOMPARE(stackModel->rowCount(tIdx), 21); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "foo()"); COMPARE_DATA(stackModel->index(0, 2, tIdx), fileName+":26"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "foo()"); COMPARE_DATA(stackModel->index(1, 2, tIdx), fileName+":24"); COMPARE_DATA(stackModel->index(2, 0, tIdx), "2"); COMPARE_DATA(stackModel->index(2, 1, tIdx), "foo()"); COMPARE_DATA(stackModel->index(2, 2, tIdx), fileName+":24"); COMPARE_DATA(stackModel->index(19, 0, tIdx), "19"); COMPARE_DATA(stackModel->index(20, 0, tIdx), "20"); stackModel->fetchMoreFrames(); WAIT_FOR_A_WHILE(session, 200); QCOMPARE(stackModel->fetchFramesCalled, 2); QCOMPARE(stackModel->rowCount(tIdx), 41); COMPARE_DATA(stackModel->index(20, 0, tIdx), "20"); COMPARE_DATA(stackModel->index(21, 0, tIdx), "21"); COMPARE_DATA(stackModel->index(22, 0, tIdx), "22"); COMPARE_DATA(stackModel->index(39, 0, tIdx), "39"); COMPARE_DATA(stackModel->index(40, 0, tIdx), "40"); stackModel->fetchMoreFrames(); WAIT_FOR_A_WHILE(session, 200); QCOMPARE(stackModel->fetchFramesCalled, 3); QCOMPARE(stackModel->rowCount(tIdx), 121); COMPARE_DATA(stackModel->index(40, 0, tIdx), "40"); COMPARE_DATA(stackModel->index(41, 0, tIdx), "41"); COMPARE_DATA(stackModel->index(42, 0, tIdx), "42"); COMPARE_DATA(stackModel->index(119, 0, tIdx), "119"); COMPARE_DATA(stackModel->index(120, 0, tIdx), "120"); stackModel->fetchMoreFrames(); WAIT_FOR_A_WHILE(session, 200); QCOMPARE(stackModel->fetchFramesCalled, 4); QCOMPARE(stackModel->rowCount(tIdx), 301); COMPARE_DATA(stackModel->index(120, 0, tIdx), "120"); COMPARE_DATA(stackModel->index(121, 0, tIdx), "121"); COMPARE_DATA(stackModel->index(122, 0, tIdx), "122"); COMPARE_DATA(stackModel->index(298, 0, tIdx), "298"); COMPARE_DATA(stackModel->index(298, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(298, 2, tIdx), fileName+":30"); COMPARE_DATA(stackModel->index(299, 0, tIdx), "299"); COMPARE_DATA(stackModel->index(299, 1, tIdx), "__libc_start_main"); COMPARE_DATA(stackModel->index(300, 0, tIdx), "300"); COMPARE_DATA(stackModel->index(300, 1, tIdx), "_start"); stackModel->fetchMoreFrames(); //nothing to fetch, we are at the end WAIT_FOR_A_WHILE(session, 200); QCOMPARE(stackModel->fetchFramesCalled, 4); QCOMPARE(stackModel->rowCount(tIdx), 301); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testStackDeactivateAndActive() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 21); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); QModelIndex tIdx = stackModel->index(0,0); session->stepOut(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); COMPARE_DATA(tIdx, "#1 at main"); QCOMPARE(stackModel->rowCount(tIdx), 3); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(0, 2, tIdx), m_debugeeFileName+":30"); COMPARE_DATA(stackModel->index(1, 0, tIdx), "1"); COMPARE_DATA(stackModel->index(1, 1, tIdx), "__libc_start_main"); COMPARE_DATA(stackModel->index(2, 0, tIdx), "2"); COMPARE_DATA(stackModel->index(2, 1, tIdx), "_start"); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testStackSwitchThread() { QSKIP("Skipping... lldb-mi crashes when break at a location with multiple threads running"); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeethreads"))); QString fileName = findSourceFile("debugeethreads.cpp"); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(stackModel->rowCount(), 4); QModelIndex tIdx = stackModel->index(0,0); COMPARE_DATA(tIdx, "#1 at main"); QCOMPARE(stackModel->rowCount(tIdx), 1); COMPARE_DATA(stackModel->index(0, 0, tIdx), "0"); COMPARE_DATA(stackModel->index(0, 1, tIdx), "main"); COMPARE_DATA(stackModel->index(0, 2, tIdx), fileName+":39"); tIdx = stackModel->index(1,0); QVERIFY(stackModel->data(tIdx).toString().startsWith("#2 at ")); stackModel->setCurrentThread(2); WAIT_FOR_A_WHILE(session, 200); int rows = stackModel->rowCount(tIdx); QVERIFY(rows > 3); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testAttach() { SKIP_IF_ATTACH_FORBIDDEN(); QString fileName = findSourceFile("debugeeslow.cpp"); KProcess debugeeProcess; debugeeProcess << QStringLiteral("nice") << findExecutable(QStringLiteral("debuggee_debugeeslow")).toLocalFile(); debugeeProcess.start(); QVERIFY(debugeeProcess.waitForStarted()); QTest::qWait(100); TestDebugSession *session = new TestDebugSession; session->attachToProcess(debugeeProcess.pid()); WAIT_FOR_A_WHILE(session, 100); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 35); // lldb-mi sliently stops when attaching to a process. Force it continue to run. session->addCommand(MI::ExecContinue, QString(), MI::CmdMaybeStartsRunning); WAIT_FOR_A_WHILE(session, 2000); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 35); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testRemoteDebugging() { KProcess gdbServer; gdbServer << QStringLiteral("lldb-server") << QStringLiteral("gdbserver") << QStringLiteral("*:1234"); gdbServer.start(); QVERIFY(gdbServer.waitForStarted()); TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; cfg.config().writeEntry(Config::LldbRemoteDebuggingEntry, true); cfg.config().writeEntry(Config::LldbRemoteServerEntry, "localhost:1234"); cfg.config().writeEntry(Config::LldbRemotePathEntry, "/tmp"); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 34); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testCoreFile() { QFileInfo f(QStringLiteral("core")); f.setCaching(false); // don't cache information if (f.exists()) { QVERIFY(QFile::remove(f.canonicalFilePath())); } KProcess debugeeProcess; debugeeProcess.setOutputChannelMode(KProcess::MergedChannels); debugeeProcess << QStringLiteral("bash") << QStringLiteral("-c") << "ulimit -c unlimited; " + findExecutable(QStringLiteral("debuggee_crash")).toLocalFile(); debugeeProcess.start(); debugeeProcess.waitForFinished(); qDebug() << debugeeProcess.readAll(); bool coreFileFound = f.exists(); if (!coreFileFound) { // Try to use coredumpctl qDebug() << "try to use coredumpctl"; auto coredumpctl = QStandardPaths::findExecutable(QStringLiteral("coredumpctl")); if (!coredumpctl.isEmpty()) { KProcess::execute(coredumpctl, {"-1", "-o", f.absoluteFilePath(), "dump", "debuggee_crash"}); coreFileFound = f.exists(); } } if (!coreFileFound) QSKIP("no core dump found, check your system configuration (see /proc/sys/kernel/core_pattern).", SkipSingle); TestDebugSession *session = new TestDebugSession; session->examineCoreFile(findExecutable(QStringLiteral("debuggee_crash")), QUrl::fromLocalFile(f.canonicalFilePath())); TestFrameStackModel *stackModel = session->frameStackModel(); WAIT_FOR_STATE(session, DebugSession::StoppedState); QModelIndex tIdx = stackModel->index(0,0); QCOMPARE(stackModel->rowCount(QModelIndex()), 1); QCOMPARE(stackModel->columnCount(QModelIndex()), 3); COMPARE_DATA(tIdx, "#1 at foo()"); session->stopDebugger(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesLocals() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(variableCollection()->rowCount(), 2); QModelIndex i = variableCollection()->index(1, 0); COMPARE_DATA(i, "Locals"); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "j"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); COMPARE_DATA(variableCollection()->index(0, 0, i), "j"); COMPARE_DATA(variableCollection()->index(0, 1, i), "2"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesLocalsStruct() { TestDebugSession *session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestLaunchConfiguration cfg; breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 1000); QModelIndex i = variableCollection()->index(1, 0); QCOMPARE(variableCollection()->rowCount(i), 4); int structIndex = 0; for(int j=0; j<3; ++j) { if (variableCollection()->index(j, 0, i).data().toString() == QLatin1String("ts")) { structIndex = j; } } COMPARE_DATA(variableCollection()->index(structIndex, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(structIndex, 1, i), "{...}"); QModelIndex ts = variableCollection()->index(structIndex, 0, i); COMPARE_DATA(variableCollection()->index(0, 0, ts), "..."); variableCollection()->expanded(ts); WAIT_FOR_A_WHILE(session, 100); COMPARE_DATA(variableCollection()->index(0, 0, ts), "a"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "0"); COMPARE_DATA(variableCollection()->index(1, 0, ts), "b"); COMPARE_DATA(variableCollection()->index(1, 1, ts), "1"); COMPARE_DATA(variableCollection()->index(2, 0, ts), "c"); COMPARE_DATA(variableCollection()->index(2, 1, ts), "2"); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 1000); COMPARE_DATA(variableCollection()->index(structIndex, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(structIndex, 1, i), "{...}"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesWatches() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; m_core->debugController()->variableCollection()->variableWidgetShown(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add(QStringLiteral("ts")); WAIT_FOR_A_WHILE(session, 300); QModelIndex i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(0, 1, i), "{...}"); QModelIndex ts = variableCollection()->index(0, 0, i); COMPARE_DATA(variableCollection()->index(0, 0, ts), "..."); variableCollection()->expanded(ts); WAIT_FOR_A_WHILE(session, 100); COMPARE_DATA(variableCollection()->index(0, 0, ts), "a"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "0"); COMPARE_DATA(variableCollection()->index(1, 0, ts), "b"); COMPARE_DATA(variableCollection()->index(1, 1, ts), "1"); COMPARE_DATA(variableCollection()->index(2, 0, ts), "c"); COMPARE_DATA(variableCollection()->index(2, 1, ts), "2"); session->stepInto(); WAIT_FOR_STATE(session, DebugSession::PausedState); WAIT_FOR_A_WHILE(session, 100); COMPARE_DATA(variableCollection()->index(0, 0, i), "ts"); COMPARE_DATA(variableCollection()->index(0, 1, i), "{...}"); COMPARE_DATA(variableCollection()->index(0, 1, ts), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesWatchesQuotes() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); // the unquoted string (the actual content): t\"t // quoted string (what we would write as a c string): "t\\\"t" // written in source file: R"("t\\\"t")" const QString testString(QStringLiteral("t\\\"t")); // the actual content const QString quotedTestString(QStringLiteral(R"("t\\\"t")")); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, DebugSession::PausedState); variableCollection()->watches()->add(quotedTestString); //just a constant string WAIT_FOR_A_WHILE(session, 3000); QModelIndex i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), quotedTestString); QEXPECT_FAIL("", "LLDB 4.0 cannot deal with string literal in expression when debugging, causing memory access error", Abort); COMPARE_DATA(variableCollection()->index(0, 1, i), quotedTestString); QModelIndex testStr = variableCollection()->index(0, 0, i); COMPARE_DATA(variableCollection()->index(0, 0, testStr), "..."); variableCollection()->expanded(testStr); WAIT_FOR_A_WHILE(session, 100); int len = testString.length(); for (int ind = 0; ind < len; ind++) { COMPARE_DATA(variableCollection()->index(ind, 0, testStr), QStringLiteral("[%0]").arg(ind)); QChar c = testString.at(ind); QString value = QString::number(c.toLatin1()) + " '" + c + "'"; COMPARE_DATA(variableCollection()->index(ind, 1, testStr), value); } COMPARE_DATA(variableCollection()->index(len, 0, testStr), QStringLiteral("[%0]").arg(len)); COMPARE_DATA(variableCollection()->index(len, 1, testStr), "0 '\\0'"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesWatchesTwoSessions() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); variableCollection()->watches()->add(QStringLiteral("ts")); WAIT_FOR_A_WHILE(session, 300); QModelIndex ts = variableCollection()->index(0, 0, variableCollection()->index(0, 0)); variableCollection()->expanded(ts); WAIT_FOR_A_WHILE(session, 100); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //check if variable is marked as out-of-scope QCOMPARE(variableCollection()->watches()->childCount(), 1); auto v = dynamic_cast(watchVariableAt(0)); QVERIFY(v); QVERIFY(!v->inScope()); QCOMPARE(v->childCount(), 3); v = dynamic_cast(v->child(0)); QVERIFY(!v->inScope()); //start a second debug session session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(variableCollection()->watches()->childCount(), 1); ts = variableCollection()->index(0, 0, variableCollection()->index(0, 0)); v = dynamic_cast(watchVariableAt(0)); QVERIFY(v); QVERIFY(v->inScope()); QCOMPARE(v->childCount(), 3); v = dynamic_cast(v->child(0)); QVERIFY(v->inScope()); COMPARE_DATA(variableCollection()->indexForItem(v, 1), QString::number(0)); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //check if variable is marked as out-of-scope v = dynamic_cast(watchVariableAt(0)); QVERIFY(!v->inScope()); QVERIFY(!dynamic_cast(v->child(0))->inScope()); } void LldbTest::testVariablesStopDebugger() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->stopDebugger(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesStartSecondSession() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 38); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesSwitchFrame() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QModelIndex i = variableCollection()->index(1, 0); COMPARE_DATA(i, "Locals"); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "j"); // only non-static variable works COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); stackModel->setCurrentFrame(1); WAIT_FOR_A_WHILE(session, 200); i = variableCollection()->index(1, 0); QCOMPARE(variableCollection()->rowCount(i), 4); COMPARE_DATA(variableCollection()->index(2, 0, i), "argc"); COMPARE_DATA(variableCollection()->index(2, 1, i), "1"); COMPARE_DATA(variableCollection()->index(3, 0, i), "argv"); breakpoints()->removeRow(0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testVariablesQuicklySwitchFrame() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QModelIndex i = variableCollection()->index(1, 0); COMPARE_DATA(i, "Locals"); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "j"); // only non-static variable works COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); stackModel->setCurrentFrame(1); WAIT_FOR_A_WHILE(session, 300); stackModel->setCurrentFrame(0); WAIT_FOR_A_WHILE(session, 1); stackModel->setCurrentFrame(1); WAIT_FOR_A_WHILE(session, 1); stackModel->setCurrentFrame(0); WAIT_FOR_A_WHILE(session, 1); stackModel->setCurrentFrame(1); WAIT_FOR_A_WHILE(session, 500); i = variableCollection()->index(1, 0); QCOMPARE(variableCollection()->rowCount(i), 4); QStringList locs; for (int j = 0; j < variableCollection()->rowCount(i); ++j) { locs << variableCollection()->index(j, 0, i).data().toString(); } QVERIFY(locs.contains("argc")); QVERIFY(locs.contains("argv")); QVERIFY(locs.contains("x")); breakpoints()->removeRow(0); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testSwitchFrameLldbConsole() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; TestFrameStackModel *stackModel = session->frameStackModel(); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 24); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(stackModel->currentFrame(), 0); stackModel->setCurrentFrame(1); QCOMPARE(stackModel->currentFrame(), 1); WAIT_FOR_A_WHILE(session, 500); QCOMPARE(stackModel->currentFrame(), 1); session->addUserCommand(QStringLiteral("print i")); WAIT_FOR_A_WHILE(session, 500); //currentFrame must not reset to 0; Bug 222882 QCOMPARE(stackModel->currentFrame(), 1); } void LldbTest::testSegfaultDebugee() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_crash"))); session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals); QString fileName = findSourceFile("debugeecrash.cpp"); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(fileName), 23); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 23); session->run(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 24); session->stopDebugger(); WAIT_FOR_STATE(session, DebugSession::EndedState); } //Bug 274390 void LldbTest::testCommandOrderFastStepping() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeeqt"))); breakpoints()->addCodeBreakpoint(QStringLiteral("main")); QVERIFY(session->startDebugging(&cfg, m_iface)); for(int i=0; i<20; i++) { session->stepInto(); } WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testRunLldbScript() { TestDebugSession *session = new TestDebugSession; QTemporaryFile runScript; runScript.open(); runScript.write(QStringLiteral("break set --file %1 --line 35\n").arg(findSourceFile("debugee.cpp")).toUtf8()); runScript.close(); TestLaunchConfiguration cfg; KConfigGroup grp = cfg.config(); grp.writeEntry(Config::LldbConfigScriptEntry, QUrl::fromLocalFile(runScript.fileName())); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(session->currentLine(), 35); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testBug301287() { TestDebugSession *session = new TestDebugSession; TestLaunchConfiguration cfg; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 28); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); variableCollection()->watches()->add(QStringLiteral("argc")); WAIT_FOR_A_WHILE(session, 300); QModelIndex i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "argc"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); //start second debug session (same cfg) session = new TestDebugSession; session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateWatches); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); i = variableCollection()->index(0, 0); QCOMPARE(variableCollection()->rowCount(i), 1); COMPARE_DATA(variableCollection()->index(0, 0, i), "argc"); COMPARE_DATA(variableCollection()->index(0, 1, i), "1"); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void LldbTest::testDebugInExternalTerminal() { TestLaunchConfiguration cfg; foreach (const QString & console, QStringList() << "konsole" << "xterm" << "xfce4-terminal" << "gnome-terminal") { if (QStandardPaths::findExecutable(console).isEmpty()) { continue; } TestDebugSession* session = new TestDebugSession(); cfg.config().writeEntry("External Terminal"/*ExecutePlugin::terminalEntry*/, console); cfg.config().writeEntry("Use External Terminal"/*ExecutePlugin::useTerminalEntry*/, true); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QUrl::fromLocalFile(m_debugeeFileName), 28); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->stepInto(); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } } void LldbTest::testSpecialPath() { QSKIP("Skipping... lldb-mi itself can't handle path with space in application dir"); TestDebugSession* session = new TestDebugSession; auto debugee = findExecutable(QStringLiteral("path with space/debuggee_spacedebugee")); TestLaunchConfiguration c(debugee, KIO::upUrl(debugee)); KDevelop::Breakpoint* b = breakpoints()->addCodeBreakpoint(QStringLiteral("spacedebugee.cpp:30")); QCOMPARE(b->state(), KDevelop::Breakpoint::NotStartedState); QVERIFY(session->startDebugging(&c, m_iface)); WAIT_FOR_STATE_AND_IDLE(session, DebugSession::PausedState); QCOMPARE(b->state(), KDevelop::Breakpoint::CleanState); session->run(); WAIT_FOR_STATE(session, DebugSession::EndedState); } void KDevMI::LLDB::LldbTest::testEnvironmentCd() { TestDebugSession *session = new TestDebugSession; QSignalSpy outputSpy(session, &TestDebugSession::inferiorStdoutLines); auto path = KIO::upUrl(findExecutable(QStringLiteral("path with space/debuggee_spacedebugee"))); TestLaunchConfiguration cfg(findExecutable(QStringLiteral("debuggee_debugeepath")), path); QVERIFY(session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE(session, KDevelop::IDebugSession::EndedState); QVERIFY(outputSpy.count() > 0); QStringList outputLines; while (outputSpy.count() > 0) { QList arguments = outputSpy.takeFirst(); for (const auto &item : arguments) { outputLines.append(item.toStringList()); } } QCOMPARE(outputLines, QStringList() << path.toLocalFile()); } QTEST_MAIN(KDevMI::LLDB::LldbTest); #include "test_lldb.moc" diff --git a/plugins/lldb/unittests/test_lldbformatters.cpp b/plugins/lldb/unittests/test_lldbformatters.cpp index 72db3b4a0f..5b4bbf6a60 100644 --- a/plugins/lldb/unittests/test_lldbformatters.cpp +++ b/plugins/lldb/unittests/test_lldbformatters.cpp @@ -1,1031 +1,1036 @@ /* * Unit tests for LLDB debugger plugin * Copyright 2016 Aetf * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "test_lldbformatters.h" #include "controllers/variable.h" #include "controllers/variablecontroller.h" #include "debugsession.h" #include "stringhelpers.h" #include "tests/testhelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WAIT_FOR_STATE(session, state) \ do { if (!KDevMI::waitForState((session), (state), __FILE__, __LINE__)) return; } while (0) #define WAIT_FOR_STATE_AND_IDLE(session, state) \ do { if (!KDevMI::waitForState((session), (state), __FILE__, __LINE__, true)) return; } while (0) #define WAIT_FOR_A_WHILE_AND_IDLE(session, ms) \ do { if (!KDevMI::waitForAWhile((session), (ms), __FILE__, __LINE__)) return; \ if (!KDevMI::waitForState((session), DebugSession::PausedState, __FILE__, __LINE__, true)) \ return; \ } while (0) #define WAIT_FOR(session, condition) \ do { \ KDevMI::TestWaiter w((session), #condition, __FILE__, __LINE__); \ while (w.waitUnless((condition))) /* nothing */ ; \ } while(0) #define COMPARE_DATA(index, expected) \ do { if (!KDevMI::compareData((index), (expected), __FILE__, __LINE__)) return; } while (0) #define VERIFY_LOCAL(row, name, summary, children) \ do { \ if (!verifyVariable((row), (name), (summary), (children), __FILE__, __LINE__)) \ return; \ } while (0) #define VERIFY_WATCH(row, name, summary, children) \ do { \ if (!verifyVariable((row), (name), (summary), (children), __FILE__, __LINE__, false)) \ return; \ } while (0) using namespace KDevelop; using namespace KDevMI::LLDB; using KDevMI::findExecutable; using KDevMI::findSourceFile; using KDevMI::findFile; using KDevMI::compareData; class TestLaunchConfiguration : public ILaunchConfiguration { public: explicit TestLaunchConfiguration(const QString& executable, const QUrl& workingDirectory = QUrl()) { auto execPath = findExecutable(executable); qDebug() << "FIND" << execPath; c = new KConfig(); c->deleteGroup("launch"); cfg = c->group("launch"); cfg.writeEntry("isExecutable", true); cfg.writeEntry("Executable", execPath); cfg.writeEntry("Working Directory", workingDirectory); } ~TestLaunchConfiguration() override { delete c; } const KConfigGroup config() const override { return cfg; } KConfigGroup config() override { return cfg; }; QString name() const override { return QStringLiteral("Test-Launch"); } KDevelop::IProject* project() const override { return nullptr; } KDevelop::LaunchConfigurationType* type() const override { return nullptr; } KConfig *rootConfig() { return c; } private: KConfigGroup cfg; KConfig *c; }; class TestDebugSession : public DebugSession { Q_OBJECT public: TestDebugSession() : DebugSession() { setSourceInitFile(false); // explicit set formatter path to force use in-tree formatters, not the one installed in system. auto formatter = findFile(LLDB_SRC_DIR, "formatters/all.py"); setFormatterPath(formatter); KDevelop::ICore::self()->debugController()->addSession(this); variableController()->setAutoUpdate(IVariableController::UpdateLocals); } }; VariableCollection *LldbFormattersTest::variableCollection() { return m_core->debugController()->variableCollection(); } QModelIndex LldbFormattersTest::watchVariableIndexAt(int i, int col) { auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); return variableCollection()->index(i, col, watchRoot); } QModelIndex LldbFormattersTest::localVariableIndexAt(int i, int col) { auto localRoot = variableCollection()->indexForItem(variableCollection()->locals(), 0); return variableCollection()->index(i, col, localRoot); } // Note: line is zero-based KDevelop::Breakpoint* LldbFormattersTest::addCodeBreakpoint(const QUrl& location, int line) { return m_core->debugController()->breakpointModel()->addCodeBreakpoint(location, line); } // Called before the first testfunction is executed void LldbFormattersTest::initTestCase() { - AutoTestShell::init(); + AutoTestShell::init({QStringLiteral("kdevlldb"), QStringLiteral("kdevexecute")}); m_core = TestCore::initialize(Core::NoUi); m_iface = m_core->pluginController() ->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute")) ->extension(); Q_ASSERT(m_iface); + + const QString lldbMiExecutable = QStandardPaths::findExecutable(QStringLiteral("lldb-mi")); + if (lldbMiExecutable.isEmpty()) { + QSKIP("Skipping, lldb-mi not available"); + } } // Called after the last testfunction was executed void LldbFormattersTest::cleanupTestCase() { TestCore::shutdown(); } // Called before each testfunction is executed void LldbFormattersTest::init() { //remove all breakpoints - so we can set our own in the test KConfigGroup bpCfg = KSharedConfig::openConfig()->group("breakpoints"); bpCfg.writeEntry("number", 0); bpCfg.sync(); auto count = m_core->debugController()->breakpointModel()->rowCount(); m_core->debugController()->breakpointModel()->removeRows(0, count); while (variableCollection()->watches()->childCount() > 0) { auto idx = watchVariableIndexAt(0); auto var = dynamic_cast(variableCollection()->itemForIndex(idx)); if (!var) break; var->die(); } m_session = new TestDebugSession; } void LldbFormattersTest::cleanup() { // Called after every testfunction if (m_session) m_session->stopDebugger(); WAIT_FOR_STATE(m_session, DebugSession::EndedState); m_session.clear(); } bool LldbFormattersTest::verifyVariable(int index, const QString &name, const QString &expectedSummary, QStringList expectedChildren, const char *file, int line, bool isLocal, bool useRE, bool unordered) { QList> childrenPairs; childrenPairs.reserve(expectedChildren.size()); if (unordered) { qDebug() << "useRE set to true when unordered = true"; useRE = true; expectedChildren.sort(); for (auto c : expectedChildren) { childrenPairs << qMakePair(QStringLiteral(R"(^\[\d+\]$)"), c); } } else { for (int i = 0; i != expectedChildren.size(); ++i) { childrenPairs << qMakePair(QStringLiteral("[%0]").arg(i), expectedChildren[i]); } } return verifyVariable(index, name, expectedSummary, childrenPairs, file, line, isLocal, useRE, unordered); } bool LldbFormattersTest::verifyVariable(int index, const QString &name, const QString &expectedSummary, QList> expectedChildren, const char *file, int line, bool isLocal, bool useRE, bool unordered) { QModelIndex varIdx, summaryIdx; if (isLocal) { varIdx = localVariableIndexAt(index, 0); summaryIdx = localVariableIndexAt(index, 1); } else { varIdx = watchVariableIndexAt(index, 0); summaryIdx = watchVariableIndexAt(index, 1); } if (!compareData(varIdx, name, file, line)) { return false; } if (!compareData(summaryIdx, expectedSummary, file, line, useRE)) { return false; } // fetch all children auto var = variableCollection()->itemForIndex(varIdx); auto childCount = 0; while (childCount != variableCollection()->rowCount(varIdx)) { childCount = variableCollection()->rowCount(varIdx); var->fetchMoreChildren(); if (!waitForAWhile(m_session, 50, file, line)) return false; } if (childCount != expectedChildren.length()) { QTest::qFail(qPrintable(QString("'%0' didn't match expected '%1' in %2:%3") .arg(childCount).arg(expectedChildren.length()).arg(file).arg(line)), file, line); return false; } QVector theOrder; theOrder.reserve(childCount); for (int i = 0; i != childCount; ++i) { theOrder.push_back(i); } if (unordered) { qDebug() << "actual list sorted for unordered compare"; std::sort(theOrder.begin(), theOrder.end(), [&](int a, int b){ auto indexA = variableCollection()->index(a, 1, varIdx); auto indexB = variableCollection()->index(b, 1, varIdx); return indexA.model()->data(indexA, Qt::DisplayRole).toString() < indexB.model()->data(indexB, Qt::DisplayRole).toString(); }); std::sort(expectedChildren.begin(), expectedChildren.end(), [](const QPair &a, const QPair &b){ return a.second < b.second; }); qDebug() << "sorted actual order" << theOrder; qDebug() << "sorted expectedChildren" << expectedChildren; } for (int i = 0; i != childCount; ++i) { if (!compareData(variableCollection()->index(theOrder[i], 0, varIdx), expectedChildren[i].first, file, line, useRE)) { return false; } if (!compareData(variableCollection()->index(theOrder[i], 1, varIdx), expectedChildren[i].second, file, line, useRE)) { return false; } } return true; } void LldbFormattersTest::testQChar() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qchar")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qchar.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children << qMakePair(QStringLiteral("ucs"), QStringLiteral("107")); VERIFY_LOCAL(0, "c", "'k'", children); } void LldbFormattersTest::testQString() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qstring")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qstring.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QString expected = QStringLiteral("test最后一个不是特殊字符'\"\\u6211"); QStringList children; for (auto ch : expected) { children << Utils::quote(ch, '\''); } VERIFY_LOCAL(0, "s", Utils::quote(expected), children); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 5); expected.append("x"); children << QStringLiteral("'x'"); VERIFY_LOCAL(0, "s", Utils::quote(expected), children); m_session->run(); WAIT_FOR_STATE(m_session, DebugSession::EndedState); } void LldbFormattersTest::testQByteArray() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qbytearray")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qbytearray.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QStringList charlist { R"(-26 '\xe6')", R"(-104 '\x98')", R"(-81 '\xaf')", R"(39 ''')", R"(34 '"')", R"(92 '\')", R"(117 'u')", R"(54 '6')", R"(50 '2')", R"(49 '1')", R"(49 '1')", }; VERIFY_LOCAL(0, "ba", R"("\xe6\x98\xaf'\"\\u6211")", charlist); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 5); charlist << QStringLiteral("120 'x'"); VERIFY_LOCAL(0, "ba", R"("\xe6\x98\xaf'\"\\u6211x")", charlist); m_session->run(); WAIT_FOR_STATE(m_session, DebugSession::EndedState); } void LldbFormattersTest::testQListContainer_data() { QTest::addColumn("container"); QTest::addColumn("unordered"); QTest::newRow("QList") << "QList" << false; QTest::newRow("QQueue") << "QQueue" << false; QTest::newRow("QVector") << "QVector" << false; QTest::newRow("QStack") << "QStack" << false; QTest::newRow("QLinkedList") << "QLinkedList" << false; QTest::newRow("QSet") << "QSet" << true; } void LldbFormattersTest::testQListContainer() { QFETCH(QString, container); QFETCH(bool, unordered); TestLaunchConfiguration cfg(QStringLiteral("debuggee_qlistcontainer")); cfg.config().writeEntry(KDevMI::Config::BreakOnStartEntry, true); auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); variableCollection()->expanded(watchRoot); variableCollection()->variableWidgetShown(); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); m_session->addUserCommand(QStringLiteral("break set --func doStuff<%1>()").arg(container)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); m_session->run(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 33); // line 34: intList << 10 << 20; auto var = variableCollection()->watches()->add(QStringLiteral("intList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("intList"), QStringLiteral(""), QStringList{}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 34); // line 35: intList << 30; variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("intList"), QStringLiteral(""), QStringList{"10", "20"}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 36); // line 37: Container stringList; if (!verifyVariable(0, QStringLiteral("intList"), QStringLiteral(""), QStringList{"10", "20", "30"}, __FILE__, __LINE__, false, false, unordered)) { return; } var->die(); // m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 37); // line 38: stringList << "a" << "bc"; var = variableCollection()->watches()->add(QStringLiteral("stringList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("stringList"), QStringLiteral(""), QStringList{}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 38); // line 39: stringList << "d"; variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("stringList"), QStringLiteral(""), QStringList{"\"a\"", "\"bc\""}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 40); // line 41: Container structList; if (!verifyVariable(0, QStringLiteral("stringList"), QStringLiteral(""), QStringList{"\"a\"", "\"bc\"", "\"d\""}, __FILE__, __LINE__, false, false, unordered)) { return; } var->die(); // m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 41); // line 42: structList << A(QStringLiteral("a"), QStringLiteral("b"), 100, -200); var = variableCollection()->watches()->add(QStringLiteral("structList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("structList"), QStringLiteral(""), QStringList{}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->runUntil({}, 43); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 42); // line 43: structList << A(); variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("structList"), QStringLiteral(""), QStringList{"{...}"}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 44); // line 45: Container pointerList; if (!verifyVariable(0, QStringLiteral("structList"), QStringLiteral(""), QStringList{"{...}", "{...}"}, __FILE__, __LINE__, false, false, unordered)) { return; } var->die(); // m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 45); // line 46: pointerList << new int(1) << new int(2); var = variableCollection()->watches()->add(QStringLiteral("pointerList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("pointerList"), QStringLiteral(""), QStringList{}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 46); // line 47: pointerList << new int(3); variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("pointerList"), QStringLiteral(""), QStringList{"^0x[0-9A-Fa-f]+$", "^0x[0-9A-Fa-f]+$"}, __FILE__, __LINE__, false, true, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 47); // line 48: qDeleteAll(pointerList); if (!verifyVariable(0, QStringLiteral("pointerList"), QStringLiteral(""), QStringList{"^0x[0-9A-Fa-f]+$", "^0x[0-9A-Fa-f]+$", "^0x[0-9A-Fa-f]+$"}, __FILE__, __LINE__, false, true, unordered)) { return; } var->die(); m_session->stepOver(); // step over qDeleteAll // > m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 50); // line 51: pairList << QPair(1, 2) << qMakePair(2, 3); var = variableCollection()->watches()->add(QStringLiteral("pairList")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_WATCH(0, "pairList", "", QStringList{}); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 51); // line 52: pairList << qMakePair(4, 5); variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update. WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("pairList"), QStringLiteral(""), QStringList{"{...}", "{...}"}, __FILE__, __LINE__, false, false, unordered)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 54); // line 55: int i = 0; if (!verifyVariable(0, QStringLiteral("pairList"), QStringLiteral(""), QStringList{"{...}", "{...}", "{...}"}, __FILE__, __LINE__, false, false, unordered)) { return; } var->die(); } void LldbFormattersTest::testQListPOD() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qlistpod")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qlistpod.cpp")), 30); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); variableCollection()->expanded(watchRoot); variableCollection()->variableWidgetShown(); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); variableCollection()->watches()->add(QStringLiteral("b")); variableCollection()->watches()->add(QStringLiteral("c")); variableCollection()->watches()->add(QStringLiteral("uc")); variableCollection()->watches()->add(QStringLiteral("s")); variableCollection()->watches()->add(QStringLiteral("us")); variableCollection()->watches()->add(QStringLiteral("i")); variableCollection()->watches()->add(QStringLiteral("ui")); variableCollection()->watches()->add(QStringLiteral("l")); variableCollection()->watches()->add(QStringLiteral("ul")); variableCollection()->watches()->add(QStringLiteral("i64")); variableCollection()->watches()->add(QStringLiteral("ui64")); variableCollection()->watches()->add(QStringLiteral("f")); variableCollection()->watches()->add(QStringLiteral("d")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_WATCH(0, "b", "", (QStringList{"false"})); VERIFY_WATCH(1, "c", "", (QStringList{"50 '2'"})); VERIFY_WATCH(2, "uc", "", (QStringList{"50 '2'"})); VERIFY_WATCH(3, "s", "", (QStringList{"50"})); VERIFY_WATCH(4, "us", "", (QStringList{"50"})); VERIFY_WATCH(5, "i", "", (QStringList{"50"})); VERIFY_WATCH(6, "ui", "", (QStringList{"50"})); VERIFY_WATCH(7, "l", "", (QStringList{"50"})); VERIFY_WATCH(8, "ul", "", (QStringList{"50"})); VERIFY_WATCH(9, "i64", "", (QStringList{"50"})); VERIFY_WATCH(10, "ui64", "", (QStringList{"50"})); VERIFY_WATCH(11, "f", "", (QStringList{"50"})); VERIFY_WATCH(12, "d", "", (QStringList{"50"})); } void LldbFormattersTest::testQMapInt() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qmapint")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qmapint.cpp")), 6); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_LOCAL(0, "m", "", (QStringList{"(10, 100)", "(20, 200)"})); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 7); VERIFY_LOCAL(0, "m", "", (QStringList{"(10, 100)", "(20, 200)", "(30, 300)"})); } void LldbFormattersTest::testQMapString() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qmapstring")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qmapstring.cpp")), 7); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 7); // line 8: m[QStringLiteral("30")] = QStringLiteral("300"); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_LOCAL(0, "m", "", (QStringList{"(\"10\", \"100\")", "(\"20\", \"200\")"})); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 8); // line 9: return 0; VERIFY_LOCAL(0, "m", "", (QStringList{"(\"10\", \"100\")", "(\"20\", \"200\")", "(\"30\", \"300\")"})); } void LldbFormattersTest::testQMapStringBool() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qmapstringbool")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qmapstringbool.cpp")), 7); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_LOCAL(0, "m", "", (QStringList{"(\"10\", true)", "(\"20\", false)"})); m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 8); VERIFY_LOCAL(0, "m", "", (QStringList{"(\"10\", true)", "(\"20\", false)", "(\"30\", true)"})); } void LldbFormattersTest::testQHashInt() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qhashint")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qhashint.cpp")), 6); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral(""), {"(10, 100)", "(20, 200)"}, __FILE__, __LINE__, true, false, true)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 7); if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral(""), {"(10, 100)", "(20, 200)", "(30, 300)"}, __FILE__, __LINE__, true, false, true)) { return; } } void LldbFormattersTest::testQHashString() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qhashstring")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qhashstring.cpp")), 7); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral(""), {"(\"10\", \"100\")", "(\"20\", \"200\")"}, __FILE__, __LINE__, true, false, true)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 8); if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral(""), {"(\"10\", \"100\")", "(\"20\", \"200\")", "(\"30\", \"300\")"}, __FILE__, __LINE__, true, false, true)) { return; } } void LldbFormattersTest::testQSetInt() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qsetint")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qsetint.cpp")), 6); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral(""), {"10", "20"}, __FILE__, __LINE__, true, false, true)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 7); if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral(""), {"10", "20", "30"}, __FILE__, __LINE__, true, false, true)) { return; } } void LldbFormattersTest::testQSetString() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qsetstring")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qsetstring.cpp")), 7); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral(""), {"\"10\"", "\"20\""}, __FILE__, __LINE__, true, false, true)) { return; } m_session->stepOver(); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); QCOMPARE(m_session->currentLine(), 8); if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral(""), {"\"10\"", "\"20\"", "\"30\""}, __FILE__, __LINE__, true, false, true)) { return; } } void LldbFormattersTest::testQDate() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qdate")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qdate.cpp")), 5); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children.append({QStringLiteral("jd"), QStringLiteral("2455217")}); children.append({QStringLiteral("(ISO)"), QStringLiteral("\"2010-01-20\"")}); children.append({QStringLiteral("(Locale)"), QStringLiteral("\".+\"")}); // (Locale) and summary are locale dependent if (!verifyVariable(0, QStringLiteral("d"), QStringLiteral(".+"), children, __FILE__, __LINE__, true, true, false)) { return; } } void LldbFormattersTest::testQTime() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qtime")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qtime.cpp")), 5); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children.append({QStringLiteral("mds"), QStringLiteral("55810123")}); children.append({QStringLiteral("(ISO)"), QStringLiteral("\"15:30:10.000123\"")}); children.append({QStringLiteral("(Locale)"), QStringLiteral("\".+\"")}); // (Locale) and summary are locale dependent if (!verifyVariable(0, QStringLiteral("t"), QStringLiteral(".+"), children, __FILE__, __LINE__, true, true, false)) { return; } } void LldbFormattersTest::testQDateTime() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qdatetime")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qdatetime.cpp")), 5); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children.append({QStringLiteral("toTime_t"), QStringLiteral("1264019473")}); children.append({QStringLiteral("(ISO)"), QStringLiteral("\"2010-01-20 20:31:13\"")}); children.append({QStringLiteral("(Locale)"), QStringLiteral("\".+\"")}); // (Locale), (UTC) and summary are locale dependent children.append({QStringLiteral("(UTC)"), QStringLiteral("\".+\"")}); if (!verifyVariable(0, QStringLiteral("dt"), QStringLiteral(".+"), children, __FILE__, __LINE__, true, true, false)) { return; } } void LldbFormattersTest::testQUrl() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_qurl")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qurl.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> children; children.append({QStringLiteral("(port)"), QStringLiteral("-1")}); children.append({QStringLiteral("(scheme)"), QStringLiteral("\"http\"")}); children.append({QStringLiteral("(userName)"), QStringLiteral("")}); children.append({QStringLiteral("(password)"), QStringLiteral("")}); children.append({QStringLiteral("(host)"), QStringLiteral("\"www.kdevelop.org\"")}); children.append({QStringLiteral("(path)"), QStringLiteral("\"/foo\"")}); children.append({QStringLiteral("(query)"), QStringLiteral("")}); children.append({QStringLiteral("(fragment)"), QStringLiteral("")}); VERIFY_LOCAL(0, "u", "\"http://www.kdevelop.org/foo\"", children); } void LldbFormattersTest::testQUuid() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_quuid")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("quuid.cpp")), 4); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); variableCollection()->expanded(localVariableIndexAt(0)); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); VERIFY_LOCAL(0, "id", "QUuid({9ec3b70b-d105-42bf-b3b4-656e44d2e223})", (QStringList{})); } void LldbFormattersTest::testKTextEditorTypes() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_ktexteditortypes")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("ktexteditortypes.cpp")), 8); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); variableCollection()->expanded(watchRoot); variableCollection()->variableWidgetShown(); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); variableCollection()->watches()->add(QStringLiteral("cursor")); variableCollection()->watches()->add(QStringLiteral("range")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> cursorChildren; cursorChildren.append({QStringLiteral("m_line"), QStringLiteral("1")}); cursorChildren.append({QStringLiteral("m_column"), QStringLiteral("1")}); QList> rangeChildren; rangeChildren.append({QStringLiteral("m_start"), QStringLiteral("(1, 1)")}); rangeChildren.append({QStringLiteral("m_end"), QStringLiteral("(2, 2)")}); VERIFY_WATCH(0, "cursor", "(1, 1)", cursorChildren); VERIFY_WATCH(1, "range", "[(1, 1) -> (2, 2)]", rangeChildren); } void LldbFormattersTest::testKDevelopTypes() { TestLaunchConfiguration cfg(QStringLiteral("debuggee_kdeveloptypes")); addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("kdeveloptypes.cpp")), 11); QVERIFY(m_session->startDebugging(&cfg, m_iface)); WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState); // Should be two rows ('auto', 'local') QCOMPARE(variableCollection()->rowCount(), 2); auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0); variableCollection()->expanded(watchRoot); variableCollection()->variableWidgetShown(); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); variableCollection()->watches()->add(QStringLiteral("path1")); variableCollection()->watches()->add(QStringLiteral("path2")); WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50); QList> path1Children; path1Children.append({QStringLiteral("m_data"), QStringLiteral("")}); QList> path2Children; path2Children.append({QStringLiteral("m_data"), QStringLiteral("")}); VERIFY_WATCH(0, "path1", "(\"tmp\", \"foo\")", path1Children); VERIFY_WATCH(1, "path2", "(\"http://www.test.com\", \"tmp\", \"asdf.txt\")", path2Children); } QTEST_MAIN(LldbFormattersTest) #include "test_lldbformatters.moc"