Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Marco Bubke
flatpak-qt-creator
Commits
d5913658
Commit
d5913658
authored
Nov 04, 2014
by
Christian Stenger
Committed by
Christian Stenger
Dec 04, 2014
Browse files
Perform test execution in separate thread and add progress bar
parent
431d15bd
Changes
3
Hide whitespace changes
Inline
Side-by-side
plugins/autotest/autotestconstants.h
View file @
d5913658
...
...
@@ -26,6 +26,7 @@ const char ACTION_ID[] = "AutoTest.Action";
const
char
MENU_ID
[]
=
"AutoTest.Menu"
;
const
char
AUTOTEST_ID
[]
=
"AutoTest.ATP"
;
const
char
AUTOTEST_CONTEXT
[]
=
"Auto Tests"
;
const
char
TASK_INDEX
[]
=
"AutoTest.Task.Index"
;
}
// namespace Autotest
}
// namespace Constants
...
...
plugins/autotest/testrunner.cpp
View file @
d5913658
...
...
@@ -16,22 +16,32 @@
**
****************************************************************************/
#include "autotestconstants.h"
#include "testresultspane.h"
#include "testrunner.h"
#include <QDebug> // REMOVE
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorersettings.h>
#include <utils/multitask.h>
#include <QFuture>
#include <QFutureInterface>
#include <QTime>
namespace
Autotest
{
namespace
Internal
{
static
TestRunner
*
m_instance
=
0
;
static
TestRunner
*
m_instance
=
0
;
static
QProcess
*
m_runner
=
0
;
static
QFutureInterface
<
void
>
*
m_currentFuture
=
0
;
TestRunner
*
TestRunner
::
instance
()
{
...
...
@@ -44,13 +54,6 @@ TestRunner::TestRunner(QObject *parent) :
QObject
(
parent
),
m_building
(
false
)
{
m_runner
.
setReadChannelMode
(
QProcess
::
MergedChannels
);
m_runner
.
setReadChannel
(
QProcess
::
StandardOutput
);
connect
(
&
m_runner
,
&
QProcess
::
readyReadStandardOutput
,
this
,
&
TestRunner
::
processOutput
,
Qt
::
DirectConnection
);
connect
(
&
m_runner
,
SIGNAL
(
finished
(
int
,
QProcess
::
ExitStatus
)),
this
,
SLOT
(
onRunnerFinished
(
int
,
QProcess
::
ExitStatus
)),
Qt
::
DirectConnection
);
}
TestRunner
::~
TestRunner
()
...
...
@@ -58,6 +61,8 @@ TestRunner::~TestRunner()
qDeleteAll
(
m_selectedTests
);
m_selectedTests
.
clear
();
m_instance
=
0
;
if
(
m_runner
)
delete
m_runner
;
}
void
TestRunner
::
setSelectedTests
(
const
QList
<
TestConfiguration
*>
&
selected
)
...
...
@@ -67,80 +72,6 @@ void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected)
m_selectedTests
=
selected
;
}
void
TestRunner
::
runTests
()
{
if
(
m_selectedTests
.
empty
())
{
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_FATAL
,
tr
(
"*** No tests selected - canceling Test Run ***"
)));
return
;
}
ProjectExplorer
::
Project
*
project
=
m_selectedTests
.
at
(
0
)
->
project
();
if
(
!
project
)
{
// add a warning or info to output? possible at all?
return
;
}
ProjectExplorer
::
ProjectExplorerPlugin
*
pep
=
ProjectExplorer
::
ProjectExplorerPlugin
::
instance
();
ProjectExplorer
::
Internal
::
ProjectExplorerSettings
pes
=
pep
->
projectExplorerSettings
();
if
(
pes
.
buildBeforeDeploy
)
{
if
(
!
project
->
hasActiveBuildSettings
())
{
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_FATAL
,
tr
(
"*** Project is not configured - canceling Test Run ***"
)));
return
;
}
buildProject
(
project
);
while
(
m_building
)
{
qApp
->
processEvents
();
}
if
(
!
m_buildSucceeded
)
{
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_FATAL
,
tr
(
"*** Build failed - canceling Test Run ***"
)));
return
;
}
}
int
testCaseCount
=
0
;
foreach
(
const
TestConfiguration
*
config
,
m_selectedTests
)
testCaseCount
+=
config
->
testCaseCount
();
// clear old log and output pane
m_xmlLog
.
clear
();
TestResultsPane
::
instance
()
->
clearContents
();
emit
testRunStarted
();
foreach
(
TestConfiguration
*
tc
,
m_selectedTests
)
{
QString
cmd
=
tc
->
targetFile
();
QString
workDir
=
tc
->
workingDirectory
();
QStringList
args
;
Utils
::
Environment
env
=
tc
->
environment
();
args
<<
QLatin1String
(
"-xml"
);
if
(
tc
->
testCases
().
count
())
args
<<
tc
->
testCases
();
exec
(
cmd
,
args
,
workDir
,
env
);
}
qDebug
(
"test run finished"
);
emit
testRunFinished
();
}
void
TestRunner
::
stopTestRun
()
{
if
(
m_runner
.
state
()
!=
QProcess
::
NotRunning
)
{
m_runner
.
kill
();
m_runner
.
waitForFinished
();
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_FATAL
,
tr
(
"*** Test Run canceled by user ***"
)));
}
}
/******************** XML line parser helper ********************/
static
bool
xmlStartsWith
(
const
QString
&
code
,
const
QString
&
start
,
QString
&
result
)
...
...
@@ -182,8 +113,10 @@ static bool xmlExtractTypeFileLine(const QString &code, const QString &tagStart,
/****************** XML line parser helper end ******************/
void
TestRunner
::
processOutput
()
void
processOutput
()
{
if
(
!
m_runner
)
return
;
static
QString
className
;
static
QString
testCase
;
static
QString
dataTag
;
...
...
@@ -196,11 +129,9 @@ void TestRunner::processOutput()
static
QString
qtVersion
;
static
QString
qtestVersion
;
while
(
m_runner
.
canReadLine
())
{
while
(
m_runner
->
canReadLine
())
{
// TODO Qt5 uses UTF-8 - while Qt4 uses ISO-8859-1 - could this be a problem?
QString
line
=
QString
::
fromUtf8
(
m_runner
.
readLine
());
line
=
line
.
trimmed
();
m_xmlLog
.
append
(
line
);
const
QString
line
=
QString
::
fromUtf8
(
m_runner
->
readLine
()).
trimmed
();
if
(
line
.
isEmpty
()
||
line
.
startsWith
(
QLatin1String
(
"<?xml version"
)))
{
className
=
QString
();
continue
;
...
...
@@ -233,7 +164,7 @@ void TestRunner::processOutput()
if
(
line
.
endsWith
(
QLatin1String
(
"/>"
)))
{
TestResult
testResult
(
className
,
testCase
,
dataTag
,
result
,
description
);
if
(
!
file
.
isEmpty
())
file
=
QFileInfo
(
m_runner
.
workingDirectory
(),
file
).
canonicalFilePath
();
file
=
QFileInfo
(
m_runner
->
workingDirectory
(),
file
).
canonicalFilePath
();
testResult
.
setFileName
(
file
);
testResult
.
setLine
(
lineNumber
);
TestResultsPane
::
instance
()
->
addTestResult
(
testResult
);
...
...
@@ -243,18 +174,19 @@ void TestRunner::processOutput()
if
(
line
==
QLatin1String
(
"</Message>"
)
||
line
==
QLatin1String
(
"</Incident>"
))
{
TestResult
testResult
(
className
,
testCase
,
dataTag
,
result
,
description
);
if
(
!
file
.
isEmpty
())
file
=
QFileInfo
(
m_runner
.
workingDirectory
(),
file
).
canonicalFilePath
();
file
=
QFileInfo
(
m_runner
->
workingDirectory
(),
file
).
canonicalFilePath
();
testResult
.
setFileName
(
file
);
testResult
.
setLine
(
lineNumber
);
TestResultsPane
::
instance
()
->
addTestResult
(
testResult
);
description
=
QString
();
}
else
if
(
line
==
QLatin1String
(
"</TestFunction>"
)
&&
!
duration
.
isEmpty
())
{
TestResult
testResult
(
className
,
testCase
,
QString
(),
ResultType
::
MESSAGE_INTERNAL
,
tr
(
"execution took %1ms"
).
arg
(
duration
));
QObject
::
tr
(
"execution took %1ms"
).
arg
(
duration
));
TestResultsPane
::
instance
()
->
addTestResult
(
testResult
);
m_currentFuture
->
setProgressValue
(
m_currentFuture
->
progressValue
()
+
1
);
}
else
if
(
line
==
QLatin1String
(
"</TestCase>"
)
&&
!
duration
.
isEmpty
())
{
TestResult
testResult
(
className
,
QString
(),
QString
(),
ResultType
::
MESSAGE_INTERNAL
,
tr
(
"Test execution took %1ms"
).
arg
(
duration
));
QObject
::
tr
(
"Test execution took %1ms"
).
arg
(
duration
));
TestResultsPane
::
instance
()
->
addTestResult
(
testResult
);
}
else
if
(
readingDescription
)
{
if
(
line
.
endsWith
(
QLatin1String
(
"]]></Description>"
)))
{
...
...
@@ -268,44 +200,17 @@ void TestRunner::processOutput()
}
else
if
(
xmlStartsWith
(
line
,
QLatin1String
(
"<QtVersion>"
),
qtVersion
))
{
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_INTERNAL
,
tr
(
"Qt Version: %1"
).
arg
(
qtVersion
)));
QObject
::
tr
(
"Qt Version: %1"
).
arg
(
qtVersion
)));
}
else
if
(
xmlStartsWith
(
line
,
QLatin1String
(
"<QTestVersion>"
),
qtestVersion
))
{
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_INTERNAL
,
tr
(
"QTest Version: %1"
).
arg
(
qtestVersion
)));
QObject
::
tr
(
"QTest Version: %1"
).
arg
(
qtestVersion
)));
}
else
{
// qDebug() << "Unhandled line:" << line; // TODO remove
}
}
}
void
TestRunner
::
onRunnerFinished
(
int
exitCode
,
QProcess
::
ExitStatus
exitStatus
)
{
qDebug
(
"runnerFinished"
);
}
void
TestRunner
::
buildProject
(
ProjectExplorer
::
Project
*
project
)
{
m_building
=
true
;
m_buildSucceeded
=
false
;
ProjectExplorer
::
BuildManager
*
mgr
=
static_cast
<
ProjectExplorer
::
BuildManager
*>
(
ProjectExplorer
::
BuildManager
::
instance
());
ProjectExplorer
::
ProjectExplorerPlugin
*
pep
=
ProjectExplorer
::
ProjectExplorerPlugin
::
instance
();
pep
->
buildProject
(
project
);
connect
(
mgr
,
&
ProjectExplorer
::
BuildManager
::
buildQueueFinished
,
this
,
&
TestRunner
::
buildFinished
);
}
void
TestRunner
::
buildFinished
(
bool
success
)
{
ProjectExplorer
::
BuildManager
*
mgr
=
static_cast
<
ProjectExplorer
::
BuildManager
*>
(
ProjectExplorer
::
BuildManager
::
instance
());
disconnect
(
mgr
,
&
ProjectExplorer
::
BuildManager
::
buildQueueFinished
,
this
,
&
TestRunner
::
buildFinished
);
m_building
=
false
;
m_buildSucceeded
=
success
;
}
static
QString
which
(
const
QString
&
path
,
const
QString
&
cmd
)
{
if
(
path
.
isEmpty
()
||
cmd
.
isEmpty
())
...
...
@@ -319,7 +224,7 @@ static QString which(const QString &path, const QString &cmd)
#endif
foreach
(
const
QString
p
,
paths
)
{
QString
fName
=
p
+
QDir
::
separator
()
+
cmd
;
const
QString
fName
=
p
+
QDir
::
separator
()
+
cmd
;
QFileInfo
fi
(
fName
);
if
(
fi
.
exists
()
&&
fi
.
isExecutable
())
return
fName
;
...
...
@@ -338,13 +243,9 @@ static QString which(const QString &path, const QString &cmd)
return
QString
();
}
bool
TestRunner
::
e
xec
(
const
QString
&
cmd
,
const
QStringList
&
args
,
const
QString
&
workingDir
,
const
Utils
::
Environment
&
env
,
int
timeout
)
bool
performE
xec
(
const
QString
&
cmd
,
const
QStringList
&
args
,
const
QString
&
workingDir
,
const
Utils
::
Environment
&
env
,
int
timeout
=
60000
)
{
if
(
m_runner
.
state
()
!=
QProcess
::
NotRunning
)
{
// kill the runner if it's running already
m_runner
.
kill
();
m_runner
.
waitForFinished
();
}
QString
runCmd
;
if
(
!
QDir
::
toNativeSeparators
(
cmd
).
contains
(
QDir
::
separator
()))
{
if
(
env
.
hasKey
(
QLatin1String
(
"PATH"
)))
...
...
@@ -354,33 +255,154 @@ bool TestRunner::exec(const QString &cmd, const QStringList &args, const QString
}
if
(
runCmd
.
isEmpty
())
{
qDebug
(
"Could not find cmd..."
);
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_FATAL
,
QObject
::
tr
(
"*** Could not find command '%1' ***"
).
arg
(
cmd
)));
return
false
;
}
m_runner
.
setWorkingDirectory
(
workingDir
);
m_runner
.
setProcessEnvironment
(
env
.
toProcessEnvironment
());
m_runner
->
setWorkingDirectory
(
workingDir
);
m_runner
->
setProcessEnvironment
(
env
.
toProcessEnvironment
());
QTime
executionTimer
;
if
(
args
.
count
())
{
m_runner
.
start
(
runCmd
,
args
);
m_runner
->
start
(
runCmd
,
args
);
}
else
{
m_runner
.
start
(
runCmd
);
m_runner
->
start
(
runCmd
);
}
bool
ok
=
m_runner
.
waitForStarted
();
bool
ok
=
m_runner
->
waitForStarted
();
executionTimer
.
start
();
if
(
ok
)
{
while
(
m_runner
.
state
()
==
QProcess
::
Running
&&
executionTimer
.
elapsed
()
<
timeout
)
{
while
(
m_runner
->
state
()
==
QProcess
::
Running
&&
executionTimer
.
elapsed
()
<
timeout
)
{
if
(
m_currentFuture
->
isCanceled
())
{
m_runner
->
kill
();
m_runner
->
waitForFinished
();
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_FATAL
,
QObject
::
tr
(
"*** Test Run canceled by user ***"
)));
}
qApp
->
processEvents
();
}
}
if
(
ok
&&
executionTimer
.
elapsed
()
<
timeout
)
{
return
m_runner
.
exitCode
()
==
0
;
return
m_runner
->
exitCode
()
==
0
;
}
else
{
return
false
;
}
}
void
performTestRun
(
QFutureInterface
<
void
>
&
future
,
const
QList
<
TestConfiguration
*>
selectedTests
)
{
int
testCaseCount
=
0
;
foreach
(
const
TestConfiguration
*
config
,
selectedTests
)
testCaseCount
+=
config
->
testCaseCount
();
m_currentFuture
=
&
future
;
m_runner
=
new
QProcess
;
m_runner
->
setReadChannelMode
(
QProcess
::
MergedChannels
);
m_runner
->
setReadChannel
(
QProcess
::
StandardOutput
);
QObject
::
connect
(
m_runner
,
&
QProcess
::
readyReadStandardOutput
,
&
processOutput
);
future
.
setProgressRange
(
0
,
testCaseCount
);
future
.
setProgressValue
(
0
);
foreach
(
const
TestConfiguration
*
tc
,
selectedTests
)
{
if
(
future
.
isCanceled
())
break
;
QString
cmd
=
tc
->
targetFile
();
QString
workDir
=
tc
->
workingDirectory
();
QStringList
args
;
Utils
::
Environment
env
=
tc
->
environment
();
args
<<
QLatin1String
(
"-xml"
);
if
(
tc
->
testCases
().
count
())
args
<<
tc
->
testCases
();
performExec
(
cmd
,
args
,
workDir
,
env
);
}
future
.
setProgressValue
(
testCaseCount
);
delete
m_runner
;
m_runner
=
0
;
m_currentFuture
=
0
;
}
void
TestRunner
::
runTests
()
{
if
(
m_selectedTests
.
empty
())
{
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_FATAL
,
tr
(
"*** No tests selected - canceling Test Run ***"
)));
return
;
}
ProjectExplorer
::
Project
*
project
=
m_selectedTests
.
at
(
0
)
->
project
();
if
(
!
project
)
// add a warning or info to output? possible at all?
return
;
ProjectExplorer
::
ProjectExplorerPlugin
*
pep
=
ProjectExplorer
::
ProjectExplorerPlugin
::
instance
();
ProjectExplorer
::
Internal
::
ProjectExplorerSettings
pes
=
pep
->
projectExplorerSettings
();
if
(
pes
.
buildBeforeDeploy
)
{
if
(
!
project
->
hasActiveBuildSettings
())
{
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_FATAL
,
tr
(
"*** Project is not configured - canceling Test Run ***"
)));
return
;
}
buildProject
(
project
);
while
(
m_building
)
{
qApp
->
processEvents
();
}
if
(
!
m_buildSucceeded
)
{
TestResultsPane
::
instance
()
->
addTestResult
(
TestResult
(
QString
(),
QString
(),
QString
(),
ResultType
::
MESSAGE_FATAL
,
tr
(
"*** Build failed - canceling Test Run ***"
)));
return
;
}
}
// clear old log and output pane
TestResultsPane
::
instance
()
->
clearContents
();
emit
testRunStarted
();
QFuture
<
void
>
future
=
QtConcurrent
::
run
(
&
performTestRun
,
m_selectedTests
);
Core
::
FutureProgress
*
progress
=
Core
::
ProgressManager
::
addTask
(
future
,
tr
(
"Running Tests"
),
Autotest
::
Constants
::
TASK_INDEX
);
connect
(
progress
,
&
Core
::
FutureProgress
::
finished
,
TestRunner
::
instance
(),
&
TestRunner
::
testRunFinished
);
}
void
TestRunner
::
buildProject
(
ProjectExplorer
::
Project
*
project
)
{
m_building
=
true
;
m_buildSucceeded
=
false
;
ProjectExplorer
::
BuildManager
*
mgr
=
static_cast
<
ProjectExplorer
::
BuildManager
*>
(
ProjectExplorer
::
BuildManager
::
instance
());
ProjectExplorer
::
ProjectExplorerPlugin
*
pep
=
ProjectExplorer
::
ProjectExplorerPlugin
::
instance
();
pep
->
buildProject
(
project
);
connect
(
mgr
,
&
ProjectExplorer
::
BuildManager
::
buildQueueFinished
,
this
,
&
TestRunner
::
buildFinished
);
}
void
TestRunner
::
buildFinished
(
bool
success
)
{
ProjectExplorer
::
BuildManager
*
mgr
=
static_cast
<
ProjectExplorer
::
BuildManager
*>
(
ProjectExplorer
::
BuildManager
::
instance
());
disconnect
(
mgr
,
&
ProjectExplorer
::
BuildManager
::
buildQueueFinished
,
this
,
&
TestRunner
::
buildFinished
);
m_building
=
false
;
m_buildSucceeded
=
success
;
}
void
TestRunner
::
stopTestRun
()
{
if
(
m_runner
&&
m_runner
->
state
()
!=
QProcess
::
NotRunning
&&
m_currentFuture
)
m_currentFuture
->
cancel
();
}
}
// namespace Internal
}
// namespace Autotest
plugins/autotest/testrunner.h
View file @
d5913658
...
...
@@ -51,23 +51,16 @@ public slots:
void
stopTestRun
();
private
slots
:
void
processOutput
();
void
onRunnerFinished
(
int
exitCode
,
QProcess
::
ExitStatus
exitStatus
);
void
buildProject
(
ProjectExplorer
::
Project
*
project
);
void
buildFinished
(
bool
success
);
private:
explicit
TestRunner
(
QObject
*
parent
=
0
);
bool
exec
(
const
QString
&
cmd
,
const
QStringList
&
args
,
const
QString
&
workingDir
=
QString
(),
const
Utils
::
Environment
&
env
=
Utils
::
Environment
(),
int
timeout
=
60000
);
QProcess
m_runner
;
QList
<
TestConfiguration
*>
m_selectedTests
;
bool
m_building
;
bool
m_buildSucceeded
;
QStringList
m_xmlLog
;
// holds complete xml log of the last test run
};
}
// namespace Internal
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment