summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuha Sippola <juhasippola@outlook.com>2015-09-25 14:14:22 +0300
committerTony Sarajärvi <tony.sarajarvi@theqtcompany.com>2015-09-29 08:59:34 +0000
commit146f150d772b0f5950d322094ad140a5feba527f (patch)
tree6bc1252ffde10722dc9be95acdeb148f42c65df0
parent2317417e27fbfbe319d5f5cc43d71528f6f1619d (diff)
Qt Metrics 2 (v0.35): Top testset/testfunction duration
New pages to list top testset durations and testfunction durations (for one testset) since a specific date including the runs that take longer than a specific limit. The testset duration list can be opened from the button on home page, and the testfunction list from a button on the testset page. Change-Id: I19d2fe1a8cc06f68cb78c7672148687b0fdff681 Reviewed-by: Tony Sarajärvi <tony.sarajarvi@theqtcompany.com>
-rw-r--r--non-puppet/qtmetrics2/index.php112
-rw-r--r--non-puppet/qtmetrics2/scripts/ajax.js31
-rw-r--r--non-puppet/qtmetrics2/src/Database.php214
-rw-r--r--non-puppet/qtmetrics2/src/Factory.php97
-rw-r--r--non-puppet/qtmetrics2/src/TestsetRun.php19
-rw-r--r--non-puppet/qtmetrics2/src/test/DatabaseTest.php136
-rw-r--r--non-puppet/qtmetrics2/src/test/FactoryTest.php60
-rw-r--r--non-puppet/qtmetrics2/templates/about.html4
-rw-r--r--non-puppet/qtmetrics2/templates/conf.html13
-rw-r--r--non-puppet/qtmetrics2/templates/home.html7
-rw-r--r--non-puppet/qtmetrics2/templates/testfunction.html7
-rw-r--r--non-puppet/qtmetrics2/templates/testfunctions_duration.html108
-rw-r--r--non-puppet/qtmetrics2/templates/testset.html16
-rw-r--r--non-puppet/qtmetrics2/templates/testset_testfunctions.html8
-rw-r--r--non-puppet/qtmetrics2/templates/testsets_duration.html110
-rw-r--r--non-puppet/qtmetrics2/templates/testsets_duration_data.html183
16 files changed, 1073 insertions, 52 deletions
diff --git a/non-puppet/qtmetrics2/index.php b/non-puppet/qtmetrics2/index.php
index 4156976..a3a9e26 100644
--- a/non-puppet/qtmetrics2/index.php
+++ b/non-puppet/qtmetrics2/index.php
@@ -34,7 +34,7 @@
/**
* Qt Metrics API
- * @since 23-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -73,6 +73,7 @@ $app->get('/', function() use($app)
'topRoute' => Slim\Slim::getInstance()->urlFor('top'),
'flakyRoute' => Slim\Slim::getInstance()->urlFor('flaky'),
'topTestfunctionsRoute' => Slim\Slim::getInstance()->urlFor('toptestfunctions'),
+ 'durationTestsetsRoute' => Slim\Slim::getInstance()->urlFor('durationTestsets'),
'bpassedTestfunctionsRoute' => Slim\Slim::getInstance()->urlFor('bpassed'),
'masterProject' => $ini['master_build_project'],
'masterState' => $ini['master_build_state'],
@@ -650,6 +651,113 @@ $app->get('/data/test/bpassed/testrows/:testset/:project', function($testset, $p
});
/**
+ * UI route: /test/duration/testsets (GET)
+ */
+
+$app->get('/test/duration/testsets', function() use($app)
+{
+ $ini = Factory::conf();
+ $dbStatus = Factory::db()->getDbRefreshStatus();
+ $days = intval($ini['top_duration_last_days']) - 1;
+ $since = Factory::getSinceDate($days);
+ $breadcrumb = array(
+ array('name' => 'home', 'link' => Slim\Slim::getInstance()->urlFor('root'))
+ );
+ $app->render('testsets_duration.html', array(
+ 'root' => Slim\Slim::getInstance()->urlFor('root'),
+ 'dbStatus' => $dbStatus,
+ 'refreshed' => $dbStatus['refreshed'] . ' (GMT)',
+ 'breadcrumb' => $breadcrumb,
+ 'lastDays' => $ini['top_duration_last_days'],
+ 'sinceDate' => $since,
+ 'durationLimitSec' => $ini['testset_top_duration_limit_sec'],
+ 'masterProject' => $ini['master_build_project'],
+ 'masterState' => $ini['master_build_state']
+ ));
+})->name('durationTestsets');
+
+$app->get('/data/test/duration/testsets', function() use($app)
+{
+ $ini = Factory::conf();
+ $days = intval($ini['top_duration_last_days']) - 1;
+ $since = Factory::getSinceDate($days);
+ $app->render('testsets_duration_data.html', array(
+ 'testsetRoute' => Slim\Slim::getInstance()->urlFor('root') . 'testset',
+ 'lastDays' => $ini['top_duration_last_days'],
+ 'sinceDate' => $since,
+ 'durationLimitSec' => $ini['testset_top_duration_limit_sec'],
+ 'list' => 'testsets',
+ 'testset' => '',
+ 'project' => '',
+ 'runs' => Factory::createTestsetRunsMaxDuration(
+ $ini['master_build_project'],
+ $ini['master_build_state']) // managed as objects
+ ));
+});
+
+/**
+ * UI route: /test/duration/testfunctions/:testset/:project (GET)
+ */
+
+$app->get('/test/duration/testfunctions/:testset/:project', function($testset, $project) use($app)
+{
+ $ini = Factory::conf();
+ $dbStatus = Factory::db()->getDbRefreshStatus();
+ if (Factory::checkTestset($testset)) {
+ $days = intval($ini['top_duration_last_days']) - 1;
+ $since = Factory::getSinceDate($days);
+ $testsetRoute = str_replace('/:testset/:project', '', Slim\Slim::getInstance()->urlFor('testset'));
+ $breadcrumb = array(
+ array('name' => 'home', 'link' => Slim\Slim::getInstance()->urlFor('root')),
+ array('name' => $testset, 'link' => $testsetRoute . '/' . $testset . '/' . $project)
+ );
+ $app->render('testfunctions_duration.html', array(
+ 'root' => Slim\Slim::getInstance()->urlFor('root'),
+ 'dbStatus' => $dbStatus,
+ 'refreshed' => $dbStatus['refreshed'] . ' (GMT)',
+ 'breadcrumb' => $breadcrumb,
+ 'lastDays' => $ini['top_duration_last_days'],
+ 'sinceDate' => $since,
+ 'durationLimitSec' => $ini['testfunction_top_duration_limit_sec'],
+ 'testset' => $testset,
+ 'project' => $project,
+ 'masterProject' => $ini['master_build_project'],
+ 'masterState' => $ini['master_build_state']
+ ));
+ } else {
+ $app->render('empty.html', array(
+ 'root' => Slim\Slim::getInstance()->urlFor('root'),
+ 'dbStatus' => $dbStatus,
+ 'message' => '404 Not Found'
+ ));
+ $app->response()->status(404);
+ }
+})->name('durationTestfunctions');
+
+$app->get('/data/test/duration/testfunctions/:testset/:project', function($testset, $project) use($app)
+{
+ $ini = Factory::conf();
+ $days = intval($ini['top_duration_last_days']) - 1;
+ $since = Factory::getSinceDate($days);
+ $testsetTestfunctionsRoute = str_replace('/:testset/:project/:conf', '', Slim\Slim::getInstance()->urlFor('testset_testfunctions'));
+ $app->render('testsets_duration_data.html', array(
+ 'testsetRoute' => Slim\Slim::getInstance()->urlFor('root') . 'testset',
+ 'testsetTestfunctionsRoute' => $testsetTestfunctionsRoute,
+ 'lastDays' => $ini['top_duration_last_days'],
+ 'sinceDate' => $since,
+ 'durationLimitSec' => $ini['testfunction_top_duration_limit_sec'],
+ 'list' => 'testfunctions',
+ 'testset' => $testset,
+ 'project' => $project,
+ 'runs' => Factory::createTestfunctionRunsMaxDuration(
+ $testset,
+ $project,
+ $ini['master_build_project'],
+ $ini['master_build_state']) // managed as objects
+ ));
+});
+
+/**
* UI route: /testset/:testset/:project (GET)
*/
@@ -667,6 +775,7 @@ $app->get('/testset/:testset/:project', function($testset, $project) use($app)
$testsetProjectRoute = str_replace('/:project', '', Slim\Slim::getInstance()->urlFor('testsetproject'));
$bpassedTestsetRoute = str_replace('/:testset/:project', '', Slim\Slim::getInstance()->urlFor('bpassedtestset'));
$bpassedtestsetTestrowsRoute = str_replace('/:testset/:project', '', Slim\Slim::getInstance()->urlFor('bpassedtestsetTestrows'));
+ $durationTestfunctionsRoute = str_replace('/:testset/:project', '', Slim\Slim::getInstance()->urlFor('durationTestfunctions'));
$app->render('testset.html', array(
'root' => Slim\Slim::getInstance()->urlFor('root'),
'dbStatus' => $dbStatus,
@@ -676,6 +785,7 @@ $app->get('/testset/:testset/:project', function($testset, $project) use($app)
'testsetProjectRoute' => $testsetProjectRoute,
'bpassedTestsetRoute' => $bpassedTestsetRoute,
'bpassedtestsetTestrowsRoute' => $bpassedtestsetTestrowsRoute,
+ 'durationTestfunctionsRoute' => $durationTestfunctionsRoute,
'lastDaysFailures' => $ini['top_failures_last_days'],
'lastDaysFlaky' => $ini['flaky_testsets_last_days'],
'sinceDateFailures' => Factory::getSinceDate(intval($ini['top_failures_last_days']) - 1),
diff --git a/non-puppet/qtmetrics2/scripts/ajax.js b/non-puppet/qtmetrics2/scripts/ajax.js
index 294194b..f63695e 100644
--- a/non-puppet/qtmetrics2/scripts/ajax.js
+++ b/non-puppet/qtmetrics2/scripts/ajax.js
@@ -34,7 +34,7 @@
/**
* Ajax route calls
- * @since 23-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -152,4 +152,33 @@ $(function () {
});
}
+ // Top testset duration
+ if ($.inArray('testsets_duration', divs) > -1) {
+ $.ajax({
+ url: "data/test/duration/testsets",
+ dataType: "html",
+ cache: true
+ })
+ .done(function( html ) {
+ console.log(this.url + " done");
+ $('#testsets_duration').html(html);
+ });
+ }
+
+ // Top testfunction duration
+ if ($.inArray('testfunctions_duration', divs) > -1) {
+ testset = $('#testset').html();
+ project = $('#project').html();
+ url = "data/test/duration/testfunctions/" + testset + "/" + project;
+ $.ajax({
+ url: url,
+ dataType: "html",
+ cache: true
+ })
+ .done(function( html ) {
+ console.log(this.url + " done");
+ $('#testfunctions_duration').html(html);
+ });
+ }
+
});
diff --git a/non-puppet/qtmetrics2/src/Database.php b/non-puppet/qtmetrics2/src/Database.php
index a3b7f1b..beabfff 100644
--- a/non-puppet/qtmetrics2/src/Database.php
+++ b/non-puppet/qtmetrics2/src/Database.php
@@ -34,7 +34,7 @@
/**
* Database class
- * @since 23-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -133,9 +133,33 @@ class Database {
}
/**
+ * Get list of testsets.
+ * @return array (int id, string name, string project)
+ */
+ public function getTestsets()
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT testset.id AS id, testset.name AS testset, project.name AS project
+ FROM testset
+ INNER JOIN project ON testset.project_id = project.id
+ ORDER BY testset.name;
+ ");
+ $query->execute(array());
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'id' => $row['id'],
+ 'name' => $row['testset'],
+ 'project' => $row['project']
+ );
+ }
+ return $result;
+ }
+
+ /**
* Get list of testsets matching the filter string.
* @param string $filter
- * @return array (string name)
+ * @return array (string name, string project)
*/
public function getTestsetsFiltered($filter)
{
@@ -188,6 +212,38 @@ class Database {
}
/**
+ * Get list of testfunctions for a testset
+ * @param string $testset
+ * @param string $project
+ * @return array (int id, int testsetId, string name)
+ */
+ public function getTestfunctionsTestset($testset, $project)
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT testfunction.id AS id, testfunction.testset_id AS testset_id, testfunction.name AS testset
+ FROM testfunction
+ WHERE testfunction.testset_id =
+ (SELECT testset.id
+ FROM testset INNER JOIN project ON testset.project_id = project.id
+ WHERE testset.name = ? AND project.name = ?)
+ ORDER BY testfunction.name;
+ ");
+ $query->execute(array(
+ $testset,
+ $project
+ ));
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'id' => $row['id'],
+ 'testsetId' => $row['testset_id'],
+ 'name' => $row['testset']
+ );
+ }
+ return $result;
+ }
+
+ /**
* Get list of target platform os's
* @return array (string os)
*/
@@ -757,6 +813,132 @@ class Database {
}
/**
+ * Get the longest testset run duration in specified builds since specified date
+ * @param string $runProject
+ * @param string $runState
+ * @param string $date
+ * @param string $durationLimitSec
+ * @return array (string testset, string project, string branch, string conf, string buildKey, string timestamp, string result, int duration)
+ */
+ public function getTestsetMaxDuration($testsetId, $runProject, $runState, $date, $durationLimitSec)
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT
+ testset.name AS testset,
+ project.name AS project,
+ branch.name AS branch,
+ conf.name AS conf,
+ project_run.build_key,
+ project_run.timestamp,
+ testset_run.result,
+ testset_run.duration
+ FROM testset_run
+ INNER JOIN testset ON testset_run.testset_id = testset.id
+ INNER JOIN project ON testset.project_id = project.id
+ INNER JOIN conf_run ON testset_run.conf_run_id = conf_run.id
+ INNER JOIN conf ON conf_run.conf_id = conf.id
+ INNER JOIN project_run ON conf_run.project_run_id = project_run.id
+ INNER JOIN branch ON project_run.branch_id = branch.id
+ INNER JOIN state ON project_run.state_id = state.id
+ WHERE
+ project_run.project_id = (SELECT id FROM project WHERE name = ?) AND
+ project_run.state_id = (SELECT id FROM state WHERE name = ?) AND
+ project_run.timestamp >= ? AND
+ testset_run.testset_id = ? AND
+ testset_run.duration >= ?
+ ORDER BY testset_run.duration DESC
+ LIMIT 1;
+ ");
+ $durationLimitDsec = $durationLimitSec * 10; // duration is in deciseconds in the database
+ $query->bindParam(1, $runProject);
+ $query->bindParam(2, $runState);
+ $query->bindParam(3, $date);
+ $query->bindParam(4, $testsetId, PDO::PARAM_INT); // int data type must be separately set
+ $query->bindParam(5, $durationLimitDsec, PDO::PARAM_INT);
+ $query->execute();
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'testset' => $row['testset'],
+ 'project' => $row['project'],
+ 'branch' => $row['branch'],
+ 'conf' => $row['conf'],
+ 'buildKey' => $row['build_key'],
+ 'timestamp' => $row['timestamp'],
+ 'result' => $row['result'],
+ 'duration' => round($row['duration']/10, 1) // convert deciseconds to seconds
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Get the longest testfunction run duration in specified builds since specified date
+ * @param string $runProject
+ * @param string $runState
+ * @param string $date
+ * @param string $durationLimitSec
+ * @return array (string testfunction, string testset, string project, string branch, string conf, string buildKey, string timestamp, string result, int duration)
+ */
+ public function getTestfunctionMaxDuration($testfunctionId, $testsetId, $runProject, $runState, $date, $durationLimitSec)
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT
+ testfunction.name AS testfunction,
+ testset.name AS testset,
+ project.name AS project,
+ branch.name AS branch,
+ conf.name AS conf,
+ project_run.build_key,
+ project_run.timestamp,
+ testfunction_run.result,
+ testfunction_run.duration
+ FROM testfunction_run
+ INNER JOIN testfunction ON testfunction_run.testfunction_id = testfunction.id
+ INNER JOIN testset_run ON testfunction_run.testset_run_id = testset_run.id
+ INNER JOIN testset ON testset_run.testset_id = testset.id
+ INNER JOIN project ON testset.project_id = project.id
+ INNER JOIN conf_run ON testset_run.conf_run_id = conf_run.id
+ INNER JOIN conf ON conf_run.conf_id = conf.id
+ INNER JOIN project_run ON conf_run.project_run_id = project_run.id
+ INNER JOIN branch ON project_run.branch_id = branch.id
+ INNER JOIN state ON project_run.state_id = state.id
+ WHERE
+ project_run.project_id = (SELECT id FROM project WHERE name = ?) AND
+ project_run.state_id = (SELECT id FROM state WHERE name = ?) AND
+ project_run.timestamp >= ? AND
+ testset_run.testset_id = ? AND
+ testfunction_run.testfunction_id = ? AND
+ testfunction_run.duration >= ?
+ ORDER BY testfunction_run.duration DESC
+ LIMIT 1;
+ ");
+ $durationLimitDsec = $durationLimitSec * 10; // duration is in deciseconds in the database
+ $query->bindParam(1, $runProject);
+ $query->bindParam(2, $runState);
+ $query->bindParam(3, $date);
+ $query->bindParam(4, $testsetId, PDO::PARAM_INT); // int data type must be separately set
+ $query->bindParam(5, $testfunctionId, PDO::PARAM_INT); // int data type must be separately set
+ $query->bindParam(6, $durationLimitDsec, PDO::PARAM_INT);
+ $query->execute();
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'testfunction' => $row['testfunction'],
+ 'testset' => $row['testset'],
+ 'project' => $row['project'],
+ 'branch' => $row['branch'],
+ 'conf' => $row['conf'],
+ 'buildKey' => $row['build_key'],
+ 'timestamp' => $row['timestamp'],
+ 'result' => $row['result'],
+ 'duration' => round($row['duration']/10, 1) // convert deciseconds to seconds
+ );
+ }
+ return $result;
+ }
+
+ /**
* Get counts of all passed, failed and skipped runs by testfunction in specified builds since specified date (list length limited)
* Only the testfunctions that have failed since the specified date are listed
* @param string $runProject
@@ -1002,7 +1184,7 @@ class Database {
* Get project run data by branch
* @param string $runProject
* @param string $runState
- * @return array (string branch, string build_key, string timestamp)
+ * @return array (string branch, string buildKey, string timestamp)
*/
public function getProjectBuildsByBranch($runProject, $runState)
{
@@ -1038,7 +1220,7 @@ class Database {
* Get conf run data by branch
* @param string $runProject
* @param string $runState
- * @return array (string branch, string conf, string build_key, bool forcesuccess, bool insignificant, string result, string timestamp, string duration)
+ * @return array (string branch, string conf, string buildKey, bool forcesuccess, bool insignificant, string result, string timestamp, string duration)
*/
public function getConfBuildsByBranch($runProject, $runState)
{
@@ -1087,7 +1269,7 @@ class Database {
* @param string $runProject
* @param string $runState
* @param string $targetOs
- * @return array (string branch, string conf, string build_key, bool forcesuccess, bool insignificant, string result, string timestamp, string duration)
+ * @return array (string branch, string conf, string buildKey, bool forcesuccess, bool insignificant, string result, string timestamp, string duration)
*/
public function getConfOsBuildsByBranch($runProject, $runState, $targetOs)
{
@@ -1138,7 +1320,7 @@ class Database {
* @param string $runProject
* @param string $runState
* @param string $conf
- * @return array (string branch, string conf, string build_key, bool forcesuccess, bool insignificant, string result, string timestamp, string duration)
+ * @return array (string branch, string conf, string buildKey, bool forcesuccess, bool insignificant, string result, string timestamp, string duration)
*/
public function getConfBuildByBranch($runProject, $runState, $conf)
{
@@ -1190,7 +1372,7 @@ class Database {
* @param string $testsetProject
* @param string $runProject
* @param string $runState
- * @return array (string branch, string conf, string build_key, string result, string timestamp, string duration, int run)
+ * @return array (string branch, string conf, string buildKey, string result, string timestamp, string duration, int run)
*/
public function getTestsetResultsByBranchConf($testset, $testsetProject, $runProject, $runState)
{
@@ -1232,7 +1414,7 @@ class Database {
'buildKey' => $row['build_key'],
'result' => $row['result'],
'timestamp' => $row['timestamp'],
- 'duration' => $row['duration'],
+ 'duration' => round($row['duration']/10, 1), // convert deciseconds to seconds
'run' => $row['run']
);
}
@@ -1244,7 +1426,7 @@ class Database {
* @param string $testsetProject
* @param string $runProject
* @param string $runState
- * @return array (string branch, string conf, string build_key, int passed, int ipassed, int failed, int ifailed)
+ * @return array (string branch, string conf, string buildKey, int passed, int ipassed, int failed, int ifailed)
*/
public function getTestsetProjectResultsByBranchConf($testsetProject, $runProject, $runState)
{
@@ -1298,7 +1480,7 @@ class Database {
* @param string $conf
* @param string $runProject
* @param string $runState
- * @return array (string branch, string build_key, string testset, string project, string result, string timestamp, string duration, int run)
+ * @return array (string branch, string buildKey, string testset, string project, string result, string timestamp, string duration, int run)
*/
public function getTestsetConfResultsByBranch($conf, $runProject, $runState)
{
@@ -1341,7 +1523,7 @@ class Database {
'project' => $row['project'],
'result' => $row['result'],
'timestamp' => $row['timestamp'],
- 'duration' => $row['duration'],
+ 'duration' => round($row['duration']/10, 1), // convert deciseconds to seconds
'run' => $row['run']
);
}
@@ -1355,7 +1537,7 @@ class Database {
* @param string $testsetProject
* @param string $runProject
* @param string $runState
- * @return array (string branch, string build_key, string testset, string project, string result, string timestamp, string duration, int run)
+ * @return array (string branch, string buildKey, string testset, string project, string result, string timestamp, string duration, int run)
*/
public function getTestsetConfProjectResultsByBranch($conf, $testsetProject, $runProject, $runState)
{
@@ -1400,7 +1582,7 @@ class Database {
'project' => $row['project'],
'result' => $row['result'],
'timestamp' => $row['timestamp'],
- 'duration' => $row['duration'],
+ 'duration' => round($row['duration']/10, 1), // convert deciseconds to seconds
'run' => $row['run']
);
}
@@ -1415,7 +1597,7 @@ class Database {
* @param string $conf
* @param string $runProject
* @param string $runState
- * @return array (string branch, string build_key, string testfunction, string result, string timestamp, string duration)
+ * @return array (string branch, string buildKey, string testfunction, string result, string timestamp, string duration)
*/
public function getTestfunctionConfResultsByBranch($testset, $testsetProject, $conf, $runProject, $runState)
{
@@ -1461,7 +1643,7 @@ class Database {
'testfunction' => $row['testfunction'],
'result' => $row['result'],
'timestamp' => $row['timestamp'],
- 'duration' => $row['duration']
+ 'duration' => round($row['duration']/10, 1) // convert deciseconds to seconds
);
}
return $result;
@@ -1476,7 +1658,7 @@ class Database {
* @param string $conf
* @param string $runProject
* @param string $runState
- * @return array (string branch, string build_key, string testrow, string result, string timestamp)
+ * @return array (string branch, string buildKey, string testrow, string result, string timestamp)
*/
public function getTestrowConfResultsByBranch($testfunction, $testset, $testsetProject, $conf, $runProject, $runState)
{
diff --git a/non-puppet/qtmetrics2/src/Factory.php b/non-puppet/qtmetrics2/src/Factory.php
index 7bce306..fedf90b 100644
--- a/non-puppet/qtmetrics2/src/Factory.php
+++ b/non-puppet/qtmetrics2/src/Factory.php
@@ -34,7 +34,7 @@
/**
* Factory class
- * @since 23-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -477,6 +477,101 @@ class Factory {
}
/**
+ * Create TestsetRun objects with the longest duration for each testset
+ * Object list is sorted descending by duration
+ * @param string $runProject
+ * @param string $runState
+ * @return array TestsetRun objects
+ */
+ public static function createTestsetRunsMaxDuration($runProject, $runState)
+ {
+ $objects = array();
+ $ini = self::conf();
+ $days = intval($ini['top_duration_last_days']) - 1;
+ $since = self::getSinceDate($days);
+ $durationLimitSec = intval($ini['testset_top_duration_limit_sec']);
+ $testsets = self::db()->getTestsets();
+ foreach($testsets as $testset) {
+ $dbEntries = self::db()->getTestsetMaxDuration($testset['id'], $runProject, $runState, $since, $durationLimitSec);
+ foreach($dbEntries as $entry) {
+ $obj = new TestsetRun(
+ $entry['testset'],
+ $entry['project'],
+ $runProject,
+ $entry['branch'],
+ $runState,
+ $entry['buildKey'],
+ $entry['conf'],
+ 0,
+ TestsetRun::stripResult($entry['result']),
+ TestsetRun::isInsignificant($entry['result']),
+ $entry['timestamp'],
+ $entry['duration']
+ );
+ $objects[] = $obj;
+ }
+ }
+ // Sort descending by duration
+ usort($objects, function($a, $b)
+ {
+ if ($a->getDuration() === $b->getDuration())
+ return 0;
+ else
+ return ($a->getDuration() > $b->getDuration() ? -1 : 1);
+ });
+ return $objects;
+ }
+
+ /**
+ * Create TestfunctionRun objects with the longest duration for each testfunction
+ * Object list is sorted descending by duration
+ * @param string $testset
+ * @param string $testsetProject
+ * @param string $runProject
+ * @param string $runState
+ * @return array TestfunctionRun objects
+ */
+ public static function createTestfunctionRunsMaxDuration($testset, $project, $runProject, $runState)
+ {
+ $objects = array();
+ $ini = self::conf();
+ $days = intval($ini['top_duration_last_days']) - 1;
+ $since = self::getSinceDate($days);
+ $durationLimitSec = intval($ini['testfunction_top_duration_limit_sec']);
+ $testfunctions = self::db()->getTestfunctionsTestset($testset, $project);
+ foreach($testfunctions as $testfunction) {
+ $dbEntries = self::db()->getTestfunctionMaxDuration($testfunction['id'], $testfunction['testsetId'], $runProject, $runState, $since, $durationLimitSec);
+ foreach($dbEntries as $entry) {
+ $obj = new TestfunctionRun(
+ $entry['testfunction'],
+ $entry['testset'],
+ $entry['project'],
+ $runProject,
+ $entry['branch'],
+ $runState,
+ $entry['buildKey'],
+ $entry['conf'],
+ TestfunctionRun::stripResult($entry['result']),
+ TestfunctionRun::isBlacklisted($entry['result']),
+ TestfunctionRun::hasChildren($entry['result']),
+ $entry['timestamp'],
+ $entry['duration']
+ );
+ $objects[] = $obj;
+ }
+ }
+ // Sort descending by duration
+ usort($objects, function($a, $b)
+ {
+ if ($a->getDuration() === $b->getDuration())
+ return 0;
+ else
+ return ($a->getDuration() > $b->getDuration() ? -1 : 1);
+ });
+ return $objects;
+ }
+
+ /**
* Create TestfunctionRun objects in a configuration for those in database
* @param string $testset
* @param string $testsetProject
diff --git a/non-puppet/qtmetrics2/src/TestsetRun.php b/non-puppet/qtmetrics2/src/TestsetRun.php
index 1203276..696014b 100644
--- a/non-puppet/qtmetrics2/src/TestsetRun.php
+++ b/non-puppet/qtmetrics2/src/TestsetRun.php
@@ -34,7 +34,7 @@
/**
* TestsetRun class
- * @since 17-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -49,6 +49,11 @@ class TestsetRun extends ProjectRun {
const RESULT_FAILURE = "failed";
/**
+ * If the tesset name is long, a shorter version of the name can be requested
+ */
+ const SHORT_NAME_LENGTH = 25;
+
+ /**
* Testset name.
* @var string
*/
@@ -112,6 +117,18 @@ class TestsetRun extends ProjectRun {
}
/**
+ * Get short name.
+ * @return string
+ */
+ public function getShortName()
+ {
+ if (strlen($this->name) > self::SHORT_NAME_LENGTH)
+ return substr($this->name, 0, self::SHORT_NAME_LENGTH - 10) . '...' . substr($this->name, -7);
+ else
+ return $this->name;
+ }
+
+ /**
* Get name of the testset project.
* @return string
*/
diff --git a/non-puppet/qtmetrics2/src/test/DatabaseTest.php b/non-puppet/qtmetrics2/src/test/DatabaseTest.php
index 942cf38..8605365 100644
--- a/non-puppet/qtmetrics2/src/test/DatabaseTest.php
+++ b/non-puppet/qtmetrics2/src/test/DatabaseTest.php
@@ -38,7 +38,7 @@ require_once(__DIR__.'/../Factory.php');
* Database unit test class
* Some of the tests require the test data as inserted into database with qtmetrics_insert.sql
* @example To run (in qtmetrics root directory): php <path-to-phpunit>/phpunit.phar ./src/test
- * @since 23-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -144,6 +144,31 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
}
/**
+ * Test getTestsets
+ * @dataProvider testGetTestsetsData
+ */
+ public function testGetTestsets($exp_testset)
+ {
+ $items = array();
+ $db = Factory::db();
+ $result = $db->getTestsets();
+ $this->assertNotEmpty($result);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('id', $row);
+ $this->assertArrayHasKey('name', $row);
+ $this->assertArrayHasKey('project', $row);
+ $items[] = $row['name'];
+ }
+ $this->assertContains($exp_testset, $items);
+ }
+ public function testGetTestsetsData()
+ {
+ return array(
+ array('tst_qftp')
+ );
+ }
+
+ /**
* Test getTestsetsFiltered
* @dataProvider testGetTestsetsFilteredData
*/
@@ -198,6 +223,33 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
}
/**
+ * Test getTestfunctionsTestset
+ * @dataProvider testGetTestfunctionsTestsetData
+ */
+ public function testGetTestfunctionsTestset($testset, $project, $exp_testfunction)
+ {
+ $items = array();
+ $db = Factory::db();
+ $result = $db->getTestfunctionsTestset($testset, $project);
+ $this->assertNotEmpty($result);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('id', $row);
+ $this->assertArrayHasKey('testsetId', $row);
+ $this->assertArrayHasKey('name', $row);
+ $items[] = $row['name'];
+ }
+ $this->assertContains($exp_testfunction, $items);
+ }
+ public function testGetTestfunctionsTestsetData()
+ {
+ return array(
+ array('tst_networkselftest', 'qtbase', 'cleanupTestCase'),
+ array('tst_qfont', 'qtbase', 'defaultFamily'),
+ array('tst_qftp', 'qtbase', 'binaryAscii')
+ );
+ }
+
+ /**
* Test getTargetPlatformOs
* @dataProvider testGetTargetPlatformOsData
*/
@@ -600,6 +652,88 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
}
/**
+ * Test getTestsetMaxDuration
+ * @dataProvider testGetTestsetMaxDurationData
+ */
+ public function testGetTestsetMaxDuration($testsetId, $runProject, $runState, $date, $durationLimitSec, $exp_testset, $exp_durationSec, $has_data)
+ {
+ $testsets = array();
+ $db = Factory::db();
+ $result = $db->getTestsetMaxDuration($testsetId, $runProject, $runState, $date, $durationLimitSec);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('testset', $row);
+ $this->assertArrayHasKey('project', $row);
+ $this->assertArrayHasKey('branch', $row);
+ $this->assertArrayHasKey('conf', $row);
+ $this->assertArrayHasKey('buildKey', $row);
+ $this->assertArrayHasKey('timestamp', $row);
+ $this->assertArrayHasKey('result', $row);
+ $this->assertArrayHasKey('duration', $row);
+ $testsets[] = $row['testset'];
+ }
+ if ($has_data) {
+ $this->assertNotEmpty($result);
+ $this->assertContains($exp_testset, $testsets);
+ $this->assertEquals(1, count($testsets));
+ $this->assertEquals($exp_durationSec, $row['duration']);
+ } else {
+ $this->assertEmpty($result);
+ }
+ }
+ public function testGetTestsetMaxDurationData()
+ {
+ return array(
+ array(3, 'Qt5', 'state', '2013-05-01', 90, 'tst_qftp', 813, 1), // duration is in seconds in the interface
+ array(3, 'Qt5', 'state', '2013-05-28', 90, 'tst_qftp', 813, 1),
+ array(3, 'Qt5', 'state', '2013-05-28', 900, 'tst_qftp', 0, 0),
+ array(3, 'Qt5', 'state', '2013-05-29', 90, 'tst_qftp', 0, 0),
+ array(999, 'Qt5', 'state', '2013-05-29', 90, 'invalid', 0, 0)
+ );
+ }
+
+ /**
+ * Test getTestfunctionMaxDuration
+ * @dataProvider testGetTestfunctionMaxDurationData
+ */
+ public function testGetTestfunctionMaxDuration($testfunctionId, $testsetId, $runProject, $runState, $date, $durationLimitSec, $exp_testfunction, $exp_durationSec, $has_data)
+ {
+ $testfunctions = array();
+ $db = Factory::db();
+ $result = $db->getTestfunctionMaxDuration($testfunctionId, $testsetId, $runProject, $runState, $date, $durationLimitSec);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('testfunction', $row);
+ $this->assertArrayHasKey('testset', $row);
+ $this->assertArrayHasKey('project', $row);
+ $this->assertArrayHasKey('branch', $row);
+ $this->assertArrayHasKey('conf', $row);
+ $this->assertArrayHasKey('buildKey', $row);
+ $this->assertArrayHasKey('timestamp', $row);
+ $this->assertArrayHasKey('result', $row);
+ $this->assertArrayHasKey('duration', $row);
+ $testfunctions[] = $row['testfunction'];
+ }
+ if ($has_data) {
+ $this->assertNotEmpty($result);
+ $this->assertContains($exp_testfunction, $testfunctions);
+ $this->assertEquals(1, count($testfunctions));
+ $this->assertEquals($exp_durationSec, $row['duration']);
+ } else {
+ $this->assertEmpty($result);
+ }
+ }
+ public function testGetTestfunctionMaxDurationData()
+ {
+ return array(
+ array(39, 3, 'Qt5', 'state', '2013-05-01', 5, 'binaryAscii', 31.1, 1), // duration is in seconds in the interface
+ array(39, 3, 'Qt5', 'state', '2013-05-28', 5, 'binaryAscii', 0, 0),
+ array(39, 3, 'Qt5', 'state', '2013-05-01', 900, 'binaryAscii', 0, 0),
+ array(31, 2, 'Qt5', 'state', '2013-05-01', 5, 'resetFont', 6.1, 1),
+ array(18, 1, 'Qt5', 'state', '2013-05-01', 0.2, 'socks5Proxy', 0.2, 1),
+ array(999, 999, 'Qt5', 'state', '2013-05-29', 5, 'invalid', 0, 0)
+ );
+ }
+
+ /**
* Test getTestfunctionsResultCounts
* @dataProvider testGetTestfunctionsResultCountsData
*/
diff --git a/non-puppet/qtmetrics2/src/test/FactoryTest.php b/non-puppet/qtmetrics2/src/test/FactoryTest.php
index 72271b2..9940d4a 100644
--- a/non-puppet/qtmetrics2/src/test/FactoryTest.php
+++ b/non-puppet/qtmetrics2/src/test/FactoryTest.php
@@ -37,7 +37,7 @@ require_once(__DIR__.'/../Factory.php');
/**
* Factory unit test class
* @example To run (in qtmetrics root directory): php <path-to-phpunit>/phpunit.phar ./src/test
- * @since 23-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -479,6 +479,64 @@ class FactoryTest extends PHPUnit_Framework_TestCase
}
/**
+ * Test createTestsetRunsMaxDuration
+ * @dataProvider testCreateTestsetRunsMaxDurationData
+ */
+ public function testCreateTestsetRunsMaxDuration($runProject, $runState, $exp_testset, $exp_build_key, $exp_duration, $max_imaginary_duration)
+ {
+ $runs = Factory::createTestsetRunsMaxDuration($runProject, $runState);
+ $this->assertNotEmpty($runs);
+ $prevDuration = $max_imaginary_duration;
+ foreach($runs as $run) {
+ $this->assertTrue($run instanceof TestsetRun);
+ $this->assertLessThanOrEqual($prevDuration, $run->getDuration());
+ $prevDuration = $run->getDuration();
+ if ($run->getName() === $exp_testset) {
+ $this->assertEquals($exp_build_key, $run->getBuildKey());
+ $this->assertEquals($exp_duration, $run->getDuration());
+ }
+ }
+ }
+ public function testCreateTestsetRunsMaxDurationData()
+ {
+ return array(
+ array('Qt5', 'state', 'tst_qftp', '1348', 813, 999),
+ array('Qt5', 'state', 'tst_qfont', 'BuildKeyInStringFormat12345', 814, 999),
+ array('Qt5', 'state', 'tst_networkselftest', '1348', 729, 999),
+ array('Qt5', 'state', 'invalid', '', 0, 999)
+ );
+ }
+
+ /**
+ * Test createTestfunctionRunsMaxDuration
+ * @dataProvider testCreateTestfunctionRunsMaxDurationData
+ */
+ public function testCreateTestfunctionRunsMaxDuration($testset, $testsetProject, $runProject, $runState, $exp_testfunction, $exp_build_key, $exp_duration, $max_imaginary_duration)
+ {
+ $runs = Factory::createTestfunctionRunsMaxDuration($testset, $testsetProject, $runProject, $runState);
+ $this->assertNotEmpty($runs);
+ $prevDuration = $max_imaginary_duration;
+ foreach($runs as $run) {
+ $this->assertTrue($run instanceof TestfunctionRun);
+ $this->assertLessThanOrEqual($prevDuration, $run->getDuration());
+ $prevDuration = $run->getDuration();
+ if ($run->getName() === $exp_testfunction) {
+ $this->assertEquals($exp_build_key, $run->getBuildKey());
+ $this->assertEquals($exp_duration, $run->getDuration());
+ }
+ }
+ }
+ public function testCreateTestfunctionRunsMaxDurationData()
+ {
+ return array(
+ array('tst_qftp', 'qtbase', 'Qt5', 'state', 'binaryAscii', '1023', 31.1, 999),
+ array('tst_qfont', 'qtbase', 'Qt5', 'state', 'resetFont', '1346', 6.1, 999),
+ array('tst_networkselftest', 'qtbase', 'Qt5', 'state', 'socks5Proxy', '1348', 0.2, 999),
+ array('tst_networkselftest', 'qtbase', 'Qt5', 'state', 'invalid', '', 0, 999)
+ );
+ }
+
+ /**
* Test createTestfunctionRunsInConf
* @dataProvider testCreateTestfunctionRunsInConfData
*/
diff --git a/non-puppet/qtmetrics2/templates/about.html b/non-puppet/qtmetrics2/templates/about.html
index 044d95a..715df9b 100644
--- a/non-puppet/qtmetrics2/templates/about.html
+++ b/non-puppet/qtmetrics2/templates/about.html
@@ -34,7 +34,7 @@
/**
* About window content
- * @since 23-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -52,4 +52,4 @@ and the global Qt developer community are the target audience. For detailed desc
<p>See the <strong><a href="https://wiki.qt.io/Qt_Metrics_2_Backlog" target="_blank">backlog</a></strong>
for development items currently identified or in progress.</p>
-<p><small>Version 0.34 (23-Sep-2015)</small></p>
+<p><small>Version 0.35 (24-Sep-2015)</small></p>
diff --git a/non-puppet/qtmetrics2/templates/conf.html b/non-puppet/qtmetrics2/templates/conf.html
index f5dab3a..16015e8 100644
--- a/non-puppet/qtmetrics2/templates/conf.html
+++ b/non-puppet/qtmetrics2/templates/conf.html
@@ -34,7 +34,7 @@
/**
* Configuration page
- * @since 17-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -368,11 +368,6 @@ title="insignificant">i</span>
{% else %}
{% set resultIcon = '' %}
{% endif %}
-{% if (run.getDuration / 10) > 60 %}
-{% set durationFormatted = ' (00:' ~ (run.getDuration/10)|date("i:s") ~ ')' %}
-{% else %}
-{% set durationFormatted = '' %}
-{% endif %}
{# Print result #}
<td class="center">
<span class="spaceHorizontal {{ resultIcon }} clickOnTouch" data-toggle="tooltip" data-placement="top" data-html="true"
@@ -558,8 +553,8 @@ title="<table>
{% else %}
{% set resultIcon = '' %}
{% endif %}
-{% if (run.getDuration / 10) > 60 %}
-{% set durationFormatted = ' (00:' ~ (run.getDuration/10)|date("i:s") ~ ')' %}
+{% if run.getDuration > 60 %}
+{% set durationFormatted = ' (00:' ~ (run.getDuration|round)|date("i:s") ~ ')' %}
{% else %}
{% set durationFormatted = '' %}
{% endif %}
@@ -570,7 +565,7 @@ title="<table>
<tr><th>Testset: </th><td>{{ run.getName }}</td></tr>
<tr><th>Timestamp: </th><td>{{ run.getTimestamp }}</td></tr>
<tr><th>Result: </th><td>{{ run.getResult }} {{ flaky }}</td></tr>
-<tr><th>Duration: </th><td>{{ run.getDuration / 10 }} s {{ durationFormatted }}</td></tr>
+<tr><th>Duration: </th><td>{{ run.getDuration }} s {{ durationFormatted }}</td></tr>
<tr><th>Run #: </th><td>{{ run.getRun }}</td></tr>
<tr><th>Insignificant: </th><td>{% if run.getInsignificant %}yes{% else %}no{% endif %}</td></tr></table>"></span></td>
{% set buildKeyFound = 1 %}
diff --git a/non-puppet/qtmetrics2/templates/home.html b/non-puppet/qtmetrics2/templates/home.html
index 185e257..da90858 100644
--- a/non-puppet/qtmetrics2/templates/home.html
+++ b/non-puppet/qtmetrics2/templates/home.html
@@ -34,7 +34,7 @@
/**
* Home page
- * @since 21-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -127,14 +127,15 @@
</form>
</div>
<br>
-<p>See top failure/flaky lists:</p>
+<p>See top lists:</p>
<div>
<a class="btn btn-primary btn-xs" href="{{ topRoute }}" role="button">testsets</a>
<a class="btn btn-primary btn-xs" href="{{ flakyRoute }}" role="button">flaky</a>
<a class="btn btn-primary btn-xs" href="{{ topTestfunctionsRoute }}" role="button">test functions</a>
+<a class="btn btn-primary btn-xs" href="{{ durationTestsetsRoute }}" role="button">duration</a>
</div>
<br>
-<p>See blacklisted tests where tagging could be removed:</p>
+<p>See blacklisted but passed tests:</p>
<div>
<a class="btn btn-primary btn-xs" href="{{ bpassedTestfunctionsRoute }}" role="button">test functions</a>
</div>
diff --git a/non-puppet/qtmetrics2/templates/testfunction.html b/non-puppet/qtmetrics2/templates/testfunction.html
index 14d285b..1338d02 100644
--- a/non-puppet/qtmetrics2/templates/testfunction.html
+++ b/non-puppet/qtmetrics2/templates/testfunction.html
@@ -34,7 +34,7 @@
/**
* Test function page (list of test rows)
- * @since 23-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -247,11 +247,6 @@
{% else %}
{% set resultIcon = '' %}
{% endif %}
-{% if (run.getDuration / 10) > 60 %}
-{% set durationFormatted = ' (00:' ~ ((run.getDuration/10)|round)|date("i:s") ~ ')' %}
-{% else %}
-{% set durationFormatted = '' %}
-{% endif %}
<td class="center">
<span class="spaceHorizontal {{ resultIcon }} clickOnTouch" data-toggle="tooltip" data-placement="top" data-html="true"
title="<table>
diff --git a/non-puppet/qtmetrics2/templates/testfunctions_duration.html b/non-puppet/qtmetrics2/templates/testfunctions_duration.html
new file mode 100644
index 0000000..c09f7cd
--- /dev/null
+++ b/non-puppet/qtmetrics2/templates/testfunctions_duration.html
@@ -0,0 +1,108 @@
+{#
+#############################################################################
+##
+## Copyright (C) 2015 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Quality Assurance module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:LGPL21$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see http://www.qt.io/terms-conditions. For further
+## information use the contact form at http://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 2.1 or version 3 as published by the Free
+## Software Foundation and appearing in the file LICENSE.LGPLv21 and
+## LICENSE.LGPLv3 included in the packaging of this file. Please review the
+## following information to ensure the GNU Lesser General Public License
+## requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+##
+## As a special exception, The Qt Company gives you certain additional
+## rights. These rights are described in The Qt Company LGPL Exception
+## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+/**
+ * Top duration (testsets) page
+ * @since 24-09-2015
+ * @author Juha Sippola
+ */
+
+#}
+
+{% include "header.html" %}
+
+<ol class="breadcrumb">
+{% for link in breadcrumb %}
+<li><a href="{{ link.link }}">{{ link.name }}</a></li>
+{% endfor %}
+<li class="active">top test function duration</li>
+</ol>
+
+<div class="container-fluid">
+<div class="row">
+
+<div class="col-sm-12 col-md-12 main">
+
+{##### Title #####}
+
+<h1 class="page-header">
+<span id="testset">{{ testset }}</span>
+<span id="project" class="hidden">{{ project }}</span>
+Top Test Function Duration
+<button type="button" class="btn btn-xs btn-info" data-toggle="collapse" data-target="#info" aria-expanded="false" aria-controls="info">
+<span class="glyphicon glyphicon-info-sign"></span>
+</button>
+<small>{{ refreshed }}</small>
+</h1>
+
+{##### Info well #####}
+
+<div class="collapse" id="info">
+<div class="well infoWell">
+<span class="glyphicon glyphicon-info-sign"></span> <strong>Top Test Function Duration</strong><br>
+<ul>
+<li>Lists test functions for the testset {{ testset }} by their duration in
+<strong>{{ masterProject }} {{ masterState }}</strong> builds during the last {{ lastDays }} days.
+The list includes only the test function runs that took over {{ durationLimitSec }} seconds.</li>
+<li>Each test function is listed here only once showing the test run that took the longest time.
+All test function runs in different configurations across all branches can be seen on the linked
+testset page, where the differences between configurations and branches can be studied as well.</li>
+</ul>
+</div>
+</div>
+
+{##### List #####}
+
+<div id="testfunctions_duration">
+<div class="panel panel-primary">
+<div class="panel-heading">
+<h4 class="panel-title bold">Last {{ lastDays }} days <small>(since {{ sinceDate }}, duration over {{ durationLimitSec }}s)</small></h4>
+</div>
+</div>
+<div class="progress data_loading">
+<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">
+</div>
+</div>
+</div> {# testfunctions_duration #}
+
+</div> {# .col... #}
+</div> {# .row #}
+</div> {# .container-fluid #}
+
+{% include "footer.html" %}
+
+{# Local scripts for this page #}
+<script src="scripts/ajax.js"></script>
+
+{% include "close.html" %}
diff --git a/non-puppet/qtmetrics2/templates/testset.html b/non-puppet/qtmetrics2/templates/testset.html
index 9fd4900..3cb54e6 100644
--- a/non-puppet/qtmetrics2/templates/testset.html
+++ b/non-puppet/qtmetrics2/templates/testset.html
@@ -34,7 +34,7 @@
/**
* Testset page (list of configurations)
- * @since 23-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -124,11 +124,15 @@ and their configuration on <strong>{{ masterProject }} {{ masterState }}</strong
<div>
<div class="btn-group">
{% set link = bpassedTestsetRoute ~ '/' ~ testset.getName|url_encode ~ '/' ~ testset.getProjectName|url_encode %}
-<a class="btn btn-primary btn-xs" href="{{ link }}" role="button">blacklisted passes for<br>test functions</a>
+<a class="btn btn-primary btn-xs" href="{{ link }}" role="button"><span class="badge">test functions</span><br>blacklisted passes</a>
</div>
<div class="btn-group">
{% set link = bpassedtestsetTestrowsRoute ~ '/' ~ testset.getName|url_encode ~ '/' ~ testset.getProjectName|url_encode %}
-<a class="btn btn-primary btn-xs" href="{{ link }}" role="button">blacklisted passes for<br>test rows</a>
+<a class="btn btn-primary btn-xs" href="{{ link }}" role="button"><span class="badge">test rows</span><br>blacklisted passes</a>
+</div>
+<div class="btn-group">
+{% set link = durationTestfunctionsRoute ~ '/' ~ testset.getName|url_encode ~ '/' ~ testset.getProjectName|url_encode %}
+<a class="btn btn-primary btn-xs" href="{{ link }}" role="button"><span class="badge">test functions</span><br>duration</a>
</div>
</div>
<hr>
@@ -341,8 +345,8 @@ and their configuration on <strong>{{ masterProject }} {{ masterState }}</strong
{% else %}
{% set resultIcon = '' %}
{% endif %}
-{% if (run.getDuration / 10) > 60 %}
-{% set durationFormatted = ' (00:' ~ (run.getDuration/10)|date("i:s") ~ ')' %}
+{% if run.getDuration > 60 %}
+{% set durationFormatted = ' (00:' ~ (run.getDuration|round)|date("i:s") ~ ')' %}
{% else %}
{% set durationFormatted = '' %}
{% endif %}
@@ -353,7 +357,7 @@ title="<table>
<tr><th>Configuration: </th><td>{{ run.getConfName }}</td></tr>
<tr><th>Timestamp: </th><td>{{ run.getTimestamp }}</td></tr>
<tr><th>Result: </th><td>{{ run.getResult }} {{ flaky }}</td></tr>
-<tr><th>Duration: </th><td>{{ run.getDuration / 10 }} s {{ durationFormatted }}</td></tr>
+<tr><th>Duration: </th><td>{{ run.getDuration }} s {{ durationFormatted }}</td></tr>
<tr><th>Run #: </th><td>{{ run.getRun }}</td></tr>
<tr><th>Insignificant: </th><td>{% if run.getInsignificant %}yes{% else %}no{% endif %}</td></tr></table>">
</span></td>
diff --git a/non-puppet/qtmetrics2/templates/testset_testfunctions.html b/non-puppet/qtmetrics2/templates/testset_testfunctions.html
index d2ee081..d5c2d44 100644
--- a/non-puppet/qtmetrics2/templates/testset_testfunctions.html
+++ b/non-puppet/qtmetrics2/templates/testset_testfunctions.html
@@ -34,7 +34,7 @@
/**
* Testset page (list of test functions)
- * @since 21-09-2015
+ * @since 24-09-2015
* @author Juha Sippola
*/
@@ -261,8 +261,8 @@
{% else %}
{% set resultIcon = '' %}
{% endif %}
-{% if (run.getDuration / 10) > 60 %}
-{% set durationFormatted = ' (00:' ~ ((run.getDuration/10)|round)|date("i:s") ~ ')' %}
+{% if run.getDuration > 60 %}
+{% set durationFormatted = ' (00:' ~ (run.getDuration|round)|date("i:s") ~ ')' %}
{% else %}
{% set durationFormatted = '' %}
{% endif %}
@@ -278,7 +278,7 @@ title="<table>
<tr><th>Configuration: </th><td>{{ run.getConfName }}</td></tr>
<tr><th>Timestamp: </th><td>{{ run.getTimestamp }}</td></tr>
<tr><th>Result: </th><td>{{ run.getResult }} {{ testrowResult }}</td></tr>
-<tr><th>Duration: </th><td>{{ run.getDuration / 10 }} s {{ durationFormatted }}</td></tr>
+<tr><th>Duration: </th><td>{{ run.getDuration }} s {{ durationFormatted }}</td></tr>
<tr><th>Blacklisted: </th><td>{% if run.getBlacklisted %}yes{% else %}no{% endif %}</td></tr></table>">
</span></td>
{% set buildKeyFound = 1 %}
diff --git a/non-puppet/qtmetrics2/templates/testsets_duration.html b/non-puppet/qtmetrics2/templates/testsets_duration.html
new file mode 100644
index 0000000..8fd953a
--- /dev/null
+++ b/non-puppet/qtmetrics2/templates/testsets_duration.html
@@ -0,0 +1,110 @@
+{#
+#############################################################################
+##
+## Copyright (C) 2015 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Quality Assurance module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:LGPL21$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see http://www.qt.io/terms-conditions. For further
+## information use the contact form at http://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 2.1 or version 3 as published by the Free
+## Software Foundation and appearing in the file LICENSE.LGPLv21 and
+## LICENSE.LGPLv3 included in the packaging of this file. Please review the
+## following information to ensure the GNU Lesser General Public License
+## requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+##
+## As a special exception, The Qt Company gives you certain additional
+## rights. These rights are described in The Qt Company LGPL Exception
+## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+/**
+ * Top duration (testsets) page
+ * @since 24-09-2015
+ * @author Juha Sippola
+ */
+
+#}
+
+{% include "header.html" %}
+
+<ol class="breadcrumb">
+{% for link in breadcrumb %}
+<li><a href="{{ link.link }}">{{ link.name }}</a></li>
+{% endfor %}
+<li class="active">top testset duration</li>
+</ol>
+
+<div class="container-fluid">
+<div class="row">
+
+<div class="col-sm-12 col-md-12 main">
+
+{##### Title #####}
+
+<h1 class="page-header">
+Top Testset Duration
+<button type="button" class="btn btn-xs btn-info" data-toggle="collapse" data-target="#info" aria-expanded="false" aria-controls="info">
+<span class="glyphicon glyphicon-info-sign"></span>
+</button>
+<small>{{ refreshed }}</small>
+</h1>
+
+{##### Info well #####}
+
+<div class="collapse" id="info">
+<div class="well infoWell">
+<span class="glyphicon glyphicon-info-sign"></span> <strong>Top Testset Duration</strong><br>
+<ul>
+<li>Lists testsets by their duration in <strong>{{ masterProject }} {{ masterState }}</strong>
+builds during the last {{ lastDays }} days. The list includes only the testset runs that took
+over {{ durationLimitSec }} seconds.</li>
+<li>Each testset is listed here only once showing the test run that took the longest time.
+All testset runs in different configurations across all branches can be seen on the linked
+testset page in order to check the differences between configurations and branches, as well
+as to show the test function duration list.</li>
+</ul>
+</div>
+</div>
+
+{##### List #####}
+
+<div id="testsets_duration">
+<div class="panel panel-primary">
+<div class="panel-heading">
+<h4 class="panel-title bold">Last {{ lastDays }} days <small>(since {{ sinceDate }}, duration over {{ durationLimitSec }}s)</small></h4>
+</div>
+</div>
+<div class="progress data_loading">
+<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">
+</div>
+</div>
+<div class="alert alert-warning" role="alert">
+<span class="glyphicon glyphicon-time"></span> <strong>Please wait:</strong> Extracting the data will be ready in less than a minute!
+</div>
+</div> {# testsets_duration #}
+
+</div> {# .col... #}
+</div> {# .row #}
+</div> {# .container-fluid #}
+
+{% include "footer.html" %}
+
+{# Local scripts for this page #}
+<script src="scripts/ajax.js"></script>
+
+{% include "close.html" %}
diff --git a/non-puppet/qtmetrics2/templates/testsets_duration_data.html b/non-puppet/qtmetrics2/templates/testsets_duration_data.html
new file mode 100644
index 0000000..59497d9
--- /dev/null
+++ b/non-puppet/qtmetrics2/templates/testsets_duration_data.html
@@ -0,0 +1,183 @@
+{#
+#############################################################################
+##
+## Copyright (C) 2015 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Quality Assurance module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:LGPL21$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see http://www.qt.io/terms-conditions. For further
+## information use the contact form at http://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 2.1 or version 3 as published by the Free
+## Software Foundation and appearing in the file LICENSE.LGPLv21 and
+## LICENSE.LGPLv3 included in the packaging of this file. Please review the
+## following information to ensure the GNU Lesser General Public License
+## requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+##
+## As a special exception, The Qt Company gives you certain additional
+## rights. These rights are described in The Qt Company LGPL Exception
+## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+/**
+ * Top duration (testsets and testfunctions) data
+ * @since 24-09-2015
+ * @author Juha Sippola
+ */
+
+#}
+
+{# Failed/passed bar area size in px #}
+{% set BAR_AREA = 120 %}
+
+{# runs as TestsetRun objects
+/**
+ * @var TestsetRun[] runs
+ */
+#}
+{# runs as TestfunctionRun objects
+/**
+ * @var TestfunctionRun[] runs
+ */
+#}
+
+{##### List #####}
+
+{# Calculate max duration for the bar #}
+{% set runCount = 0 %}
+{% set maxDuration = 1 %}
+{% for run in runs %}
+{% set runCount = runCount + 1 %}
+{% if run.getDuration > maxDuration %}
+{% set maxDuration = run.getDuration %}
+{% endif %}
+{% endfor %}
+
+<div class="panel panel-primary">
+<div class="panel-heading">
+<h4 class="panel-title bold">Last {{ lastDays }} days <small>(since {{ sinceDate }}, duration over {{ durationLimitSec }}s)</small></h4>
+</div>
+
+{% if runCount > 0 %}
+
+<div class="panel-body">
+<div class="table-responsive">
+<table class="table table-striped">
+<thead>
+<tr>
+{% if list == 'testsets' %}
+<th>testset</th>
+{% else %}
+<th>test function</th>
+{% endif %}
+<th class="leftBorder center">duration</th>
+<th class="showInLargeDisplay"></th>
+<th class="leftBorder">build information</th>
+</tr>
+</thead>
+<tbody>
+
+{# Print testsets #}
+{% for run in runs %}
+<tr>
+{# Testset and project name#}
+{% if list == 'testsets' %}
+{% set link = testsetRoute ~ '/' ~ run.getName|url_encode ~ '/' ~ run.getTestsetProjectName|url_encode %}
+<td><a href="{{ link }}">
+{% if run.getName|length > constant('TestsetRun::SHORT_NAME_LENGTH') %}
+<span class="clickOnTouch" data-toggle="tooltip" data-placement="top" title="{{ run.getName }}">{{ run.getShortName }}</span>
+{% else %}
+{{ run.getShortName }}
+{% endif %}
+</a><br>in {{ run.getTestsetProjectName }}</td>
+
+{# Testfunction name #}
+{% else %}
+<td>
+{% if run.getName|length > constant('TestsetRun::SHORT_NAME_LENGTH') %}
+<span class="clickOnTouch" data-toggle="tooltip" data-placement="top" title="{{ run.getName }}">{{ run.getShortName }}</span>
+{% else %}
+{{ run.getShortName }}
+{% endif %}
+</td>
+{% endif %}
+
+{# Duration #}
+{% if run.getDuration > 60 %}
+{% set durationFormatted = ' (00:' ~ (run.getDuration|round)|date("i:s") ~ ')' %}
+{% else %}
+{% set durationFormatted = '' %}
+{% endif %}
+<td class="leftBorder center">{{ run.getDuration }}s<br>{{ durationFormatted }}</td>
+
+{# Show results as bars (scaled to BAR_AREA px) #}
+{% set bar = ((BAR_AREA/maxDuration) * run.getDuration)|round(0, 'floor') %}
+{% if (run.getDuration > 0) and (bar == 0) %}
+{% set bar = 1 %}
+{% endif %}
+<td class="center showInLargeDisplay">
+<div>
+<div class="floatLeft blueBackground" style="width: {{ bar }}px">&nbsp;</div>
+</div>
+</td>
+{# Build info #}
+{% if run.getResult == constant('testsetRun::RESULT_SUCCESS') %}
+{% set resultIcon = 'glyphicon glyphicon-ok green' %}
+{% elseif run.getResult == constant('testsetRun::RESULT_FAILURE') %}
+{% set resultIcon = 'glyphicon glyphicon-remove red' %}
+{% elseif run.getResult == constant('TestfunctionRun::RESULT_SUCCESS') %}
+{% set resultIcon = 'glyphicon glyphicon-ok green' %}
+{% elseif run.getResult == constant('TestfunctionRun::RESULT_FAILURE') %}
+{% set resultIcon = 'glyphicon glyphicon-remove red' %}
+{% elseif run.getResult == constant('TestfunctionRun::RESULT_FAILURE_EXPECTED') %}
+{% set resultIcon = 'glyphicon glyphicon-remove-sign green' %}
+{% elseif run.getResult == constant('TestfunctionRun::RESULT_SUCCESS_UNEXPECTED') %}
+{% set resultIcon = 'glyphicon glyphicon-ok-sign red' %}
+{% elseif run.getResult == constant('TestfunctionRun::RESULT_SKIP') %}
+{% set resultIcon = 'glyphicon glyphicon-ban-circle gray' %}
+{% else %}
+{% set resultIcon = '' %}
+{% endif %}
+{% if list == 'testsets' %}
+{% set resultTitle = 'testset result: ' ~ run.getResult %}
+{% else %}
+{% set resultTitle = 'test function result: ' ~ run.getResult %}
+{% endif %}
+<td class="leftBorder"><small>
+<span class="clickOnTouch" data-toggle="tooltip" data-placement="top" title="{{ resultTitle }}"><span class="{{ resultIcon }}"></span></span>
+<a href="{{ run.getBuildLink }}" target="_blank"><span class="spaceHorizontal glyphicon glyphicon-folder-open"></span></a>
+{{ run.getBranchName }} build on {{ run.getTimestamp }}
+<br>{{ run.getConfName }}</small></td>
+</tr>
+{% endfor %}{# run #}
+</tbody>
+</table>
+</div> {# .table-responsive #}
+</div> {# .panel-body #}
+
+{% endif %}{# runCount #}
+</div> {# .panel... #}
+
+{% if runCount == 0 %}
+<div class="alert alert-info" role="alert">
+{% if list == 'testsets' %}
+There are not any testsets with duration less than {{ durationLimitSec }}s since {{ sinceDate }}!
+{% else %}
+The testset {{ testset }} ({{ project }}) either does not have any {{ list }},
+or the duration of all {{ list }} since {{ sinceDate }} is less than {{ durationLimitSec }}s!
+{% endif %}
+</div>
+{% endif %}{# runCount #}