-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
QtTest driver v2 #142
QtTest driver v2 #142
Conversation
@@ -1,4 +1,4 @@ | |||
cmake_minimum_required(VERSION 2.8.12) | |||
cmake_minimum_required(VERSION 3.1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, I think we already needed 3.1 anyway (for both find_dependency()
and Threads::Threads
).
.travis.yml
Outdated
|
||
before_install: | ||
- if [[ "${TRAVIS_OS_NAME}" = "osx" ]]; then brew update && brew install ninja; fi | ||
- if [[ "${TRAVIS_OS_NAME}" = "osx" ]]; then brew update && brew install ninja && brew install qt5; fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: doesn't brew support installing multiple packages at once? E.g. brew install ninja qt5
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are probably right - I have no expirience with OSX
CMakeLists.txt
Outdated
@@ -13,7 +13,9 @@ set(CUKE_ENABLE_EXAMPLES OFF CACHE BOOL "Enable the examples") | |||
set(GMOCK_SRC_DIR "" CACHE STRING "Google Mock framework sources path (otherwise downloaded)") | |||
set(GMOCK_VER "1.7.0" CACHE STRING "Google Mock framework version to be used") | |||
option(VALGRIND_TESTS "Run tests within Valgrind" OFF) | |||
set(CUKE_DISABLE_QT OFF CACHE BOOL "Disable using Qt framework") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For boolean parameters I recommend using option
instead (it's intended to give user options and only supports booleans).
CMakeLists.txt
Outdated
execute_process(COMMAND ${QMAKE_EXECUTABLE} -query QT_VERSION OUTPUT_VARIABLE QT_VERSION) | ||
if(QT_VERSION GREATER 5.6.99) | ||
message(STATUS "C++11 is needed from Qt version 5.7.0, building with c++11 enabled") | ||
set(CMAKE_CXX_STANDARD 11) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't you just build the Qt specific bits with C++11 instead of everything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting this for everything fixed TCPserver tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are they broken? Because they seem to be running fine on Travis.
AFAIK we're doing a lot of work to be C++98 compatible. Just flipping the switch like this throws away our testing of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see issue #141 - we can discuss further there the ServerTests.
CMakeLists.txt
Outdated
#check if c++11 is nedded | ||
find_program(QMAKE_EXECUTABLE NAMES qmake HINTS ${QTDIR} ENV QTDIR PATH_SUFFIXES bin) | ||
execute_process(COMMAND ${QMAKE_EXECUTABLE} -query QT_VERSION OUTPUT_VARIABLE QT_VERSION) | ||
if(QT_VERSION GREATER 5.6.99) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use this instead:
if(NOT QT_VERSION VERSION_LESS 5.7)
That does a proper lexicographic comparison of the version number.
// make stdout & stderr streams unbuffered | ||
// so that we don't need to flush the streams | ||
// before capture and after capture | ||
// (fflush can cause a deadlock if the stream is currently being |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering how this sentence should end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used?
if (m_capturing) | ||
return; | ||
|
||
secure_pipe(m_pipe); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do all these secure_XXX
variants come from? I've never encountered them before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those are private methods in that class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah thanks, I overlooked those.
#define STD_ERR_FD (fileno(stderr)) | ||
#endif | ||
|
||
class qtCapture { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of this code is dependent on a template parameter, so it can all be moved out of this header and into a .cpp file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean everything or just the methods definitions ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method definitions and all the #include
s to support them.
return m_capturing; | ||
} | ||
|
||
static void EndCapture() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this entire class a singleton? It seems to me like the restoring of file descriptors that you're doing here is something that you would want to do in a destructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've converted it to normal class
src/drivers/QtTestDriver.cpp
Outdated
QtTestObject testObject(this); | ||
|
||
qtCapture::Init(); | ||
qtCapture::BeginCapture(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming you're capturing stdout/stderr here because Qt tends to be overly verbose during execution? Is it possible to make Qt write to some alternate log sink instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately only to file or stdout and we need capturing output to get results
tests/CMakeLists.txt
Outdated
@@ -3,7 +3,7 @@ function(cuke_add_driver_test TEST_FILE) | |||
message(STATUS "Adding " ${TEST_NAME}) | |||
add_executable(${TEST_NAME} ${TEST_FILE}.cpp) | |||
target_link_libraries(${TEST_NAME} cucumber-cpp-nomain ${CUKE_EXTRA_LIBRARIES} ${ARGN}) | |||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) | |||
add_test(${TEST_NAME} ${TEST_NAME}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why you make this change. And I do believe this is causing your test failures when running with Valgrind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll revert it
As for the c++ standard: now change to c++11 is triggered by Qt 5.8 in:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The qtCapture
code looks to be written without a complete understanding of how to use syscalls under Unix. This is a non-trivial subject that's sometimes very tricky to get right.
Additionally some 5 minute searching in Qt's documentation gave me the suggestion that a message handler could possibly be installed instead to capture this output. I suggest to look at that first, before trying to go this route of intercepting things: https://doc.qt.io/qt-4.8/qtglobal.html#qInstallMsgHandler
src/drivers/QtTestDriver.cpp
Outdated
do { | ||
ret = close(fd); | ||
fd_blocked = (errno == EINTR); | ||
if (fd_blocked) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EINTR
indicates that the syscall was interrupted by a syscall and can be retried immediately. There's no point in waiting first. (The same applies to other syscalls where EINTR
is returned).
src/drivers/QtTestDriver.cpp
Outdated
bool fd_blocked = false; | ||
do { | ||
ret = dup(src); | ||
fd_blocked = (errno == EINTR || errno == EBUSY); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong, because errno
's content is undefined when the previous syscall succeeded. I.e. in this case errno
is only valid when dup's return value == -1.
src/drivers/QtTestDriver.cpp
Outdated
fd_blocked = (errno == EINTR); | ||
if (fd_blocked) | ||
QThread::msleep(10); | ||
} while (ret < 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This causes you to wait indefinitely for errors that aren't checked for explicitly. Usually you want the converse: only retry for a specific set of known error conditions.
src/drivers/QtTestDriver.cpp
Outdated
#include <QtConcurrent/QtConcurrent> | ||
|
||
#ifndef STD_OUT_FD | ||
#define STD_OUT_FD (fileno(stdout)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't use this macro to hide where you're getting this file descriptor from. You're disguising it as a constant while it isn't.
src/drivers/QtTestDriver.cpp
Outdated
m_oldStdOut = secure_dup(STD_OUT_FD); | ||
m_oldStdErr = secure_dup(STD_ERR_FD); | ||
secure_dup2(m_pipe[WRITE], STD_OUT_FD); | ||
secure_dup2(m_pipe[WRITE], STD_ERR_FD); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has one rather fatal flaw: it won't work if whatever you write more than the pipe's buffer can hold to either of these file descriptors. At that point it will block and you're process will hang.
Unfortunately all QtTestLib does not rely on normal Qt debug output. they use something like this:
Where
|
Please reevaluate temporary file based solution as qInstallHandler won't do the trick, and pipe based solution wasn't as good as I believed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks much better. I've got just a few comments and questions now.
src/drivers/QtTestDriver.cpp
Outdated
int returnValue = QTest::qExec(&testObject, 0, NULL); | ||
capture.EndCapture(); | ||
QTemporaryFile file; | ||
QString fileName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this unused variable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/drivers/QtTestDriver.cpp
Outdated
if(!file.open()) { | ||
return InvokeResult::failure("Unable to open temporary file needed for this test"); | ||
} | ||
file.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this or reopening it later destroy the used name? I also don't think it's necessary to close. As long as you don't read from it the buffer shouldn't be filled so you shouldn't have any problems of it containing stale data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't destroy used name, but it turns out to be unnecessary to close and reopen.
src/drivers/QtTestDriver.cpp
Outdated
|
||
QtTestObject testObject(this); | ||
int returnValue = QTest::qExec(&testObject, QStringList() << "test" << "-o" << file.fileName()); | ||
if(returnValue == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a space between if
and (
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/drivers/QtTestDriver.cpp
Outdated
{ | ||
file.open(); | ||
QTextStream ts(&file); | ||
return InvokeResult::failure(ts.readAll().toLocal8Bit()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're converting this to JSON later on. This should definitely be encoded to UTF-8, not to the local character set. Let cucumber take care of doing that conversion when it wishes to display its output.
Closing this as I've created another revision of this PR #165. |
QtTest based test driver for cucumber-cpp.
Includes also QtTestSteps for both Calc and CalcQt examples.
It is based of PR #140 for Qt in CI
Please take a look at new implementation using pipes for both POSIX and windows.
It doesn't address both your considerations as this is also mostly copied from SO and it's redirecting entire stdout, but for sure it is better than previous implementation. If you still don't like it I'll prepare temporary-file based version.
Only issue I've found is #141 which is here visible as Qt 5.7+ requires C++11.