diff options
-rw-r--r-- | non-puppet/qtmetrics2/index.php | 118 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/project_search.php | 64 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/scripts/project_autocomplete.js | 84 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/src/ConfRun.php | 121 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/src/Database.php | 295 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/src/Factory.php | 63 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/src/TestsetRun.php | 9 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/src/test/DatabaseTest.php | 181 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/src/test/FactoryTest.php | 70 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/templates/about.html | 6 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/templates/build_project.html | 342 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/templates/home.html | 103 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/templates/overview.html | 237 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/templates/testset.html | 464 | ||||
-rw-r--r-- | non-puppet/qtmetrics2/templates/testset_project.html | 346 |
15 files changed, 2169 insertions, 334 deletions
diff --git a/non-puppet/qtmetrics2/index.php b/non-puppet/qtmetrics2/index.php index 746ffbe..811c126 100644 --- a/non-puppet/qtmetrics2/index.php +++ b/non-puppet/qtmetrics2/index.php @@ -34,8 +34,8 @@ /** * Qt Metrics API - * @version 0.5 - * @since 25-06-2015 + * @version 0.7 + * @since 30-06-2015 * @author Juha Sippola */ @@ -67,16 +67,106 @@ $app->get('/', function() use($app) 'platformRoute' => Slim\Slim::getInstance()->urlFor('root') . 'platform', 'testRoute' => Slim\Slim::getInstance()->urlFor('root') . 'test', 'refreshed' => Factory::db()->getDbRefreshed() . ' (GMT)', - 'states' => Factory::db()->getStates(), + 'masterProject' => $ini['master_build_project'], + 'masterState' => $ini['master_build_state'], 'branches' => Factory::db()->getBranches(), - 'projects' => Factory::createProjects( - $ini['master_build_project'], - $ini['master_build_state']), // managed as objects 'platforms' => Factory::db()->getTargetPlatforms() )); })->name('root'); /** + * UI route: /overview (GET) + */ + +$app->get('/overview', function() use($app) +{ + $ini = Factory::conf(); + $breadcrumb = array( + array('name' => 'home', 'link' => Slim\Slim::getInstance()->urlFor('root')) + ); + $app->render('overview.html', array( + 'root' => Slim\Slim::getInstance()->urlFor('root'), + 'breadcrumb' => $breadcrumb, + 'buildProjectRoute' => Slim\Slim::getInstance()->urlFor('root') . 'buildproject', + 'testsetProjectRoute' => Slim\Slim::getInstance()->urlFor('root') . 'testsetproject', + 'refreshed' => Factory::db()->getDbRefreshed() . ' (GMT)', + 'masterProject' => $ini['master_build_project'], + 'masterState' => $ini['master_build_state'], + 'latestProjectRuns' => Factory::db()->getLatestProjectBranchBuildResults( + $ini['master_build_project'], + $ini['master_build_state']), + 'latestTestsetRuns' => Factory::db()->getLatestProjectBranchTestsetResults( + $ini['master_build_project'], + $ini['master_build_state']) + )); +}); + +/** + * UI route: /buildproject (GET) + */ + +$app->get('/buildproject/:project', function($project) use($app) +{ + $project = strip_tags($project); + $ini = Factory::conf(); + $breadcrumb = array( + array('name' => 'home', 'link' => Slim\Slim::getInstance()->urlFor('root')) + ); + $app->render('build_project.html', array( + 'root' => Slim\Slim::getInstance()->urlFor('root'), + 'breadcrumb' => $breadcrumb, + 'refreshed' => Factory::db()->getDbRefreshed() . ' (GMT)', + 'masterProject' => $ini['master_build_project'], + 'masterState' => $ini['master_build_state'], + 'latestProjectRuns' => Factory::db()->getLatestProjectBranchBuildResults( + $project, + $ini['master_build_state']), + 'projectBuilds' => Factory::db()->getProjectBuildsByBranch( + $ini['master_build_project'], + $ini['master_build_state']), + 'project' => Factory::createProject( + $project, + $ini['master_build_project'], + $ini['master_build_state']), // managed as object + 'confRuns' => Factory::createConfRuns( + $ini['master_build_project'], + $ini['master_build_state']) // managed as objects + )); +}); + +/** + * UI route: /testsetproject (GET) + */ + +$app->get('/testsetproject/:project', function($project) use($app) +{ + $project = strip_tags($project); + $ini = Factory::conf(); + $breadcrumb = array( + array('name' => 'home', 'link' => Slim\Slim::getInstance()->urlFor('root')) + ); + $app->render('testset_project.html', array( + 'root' => Slim\Slim::getInstance()->urlFor('root'), + 'breadcrumb' => $breadcrumb, + 'refreshed' => Factory::db()->getDbRefreshed() . ' (GMT)', + 'masterProject' => $ini['master_build_project'], + 'masterState' => $ini['master_build_state'], + 'project' => $project, + 'latestTestsetRuns' => Factory::db()->getLatestTestsetProjectBranchTestsetResults( + $project, + $ini['master_build_project'], + $ini['master_build_state']), + 'projectBuilds' => Factory::db()->getProjectBuildsByBranch( + $ini['master_build_project'], + $ini['master_build_state']), + 'confBuilds' => Factory::db()->getTestsetProjectResultsByBranchConf( + $project, + $ini['master_build_project'], + $ini['master_build_state']) + )); +}); + +/** * UI route: /test/top (GET) */ @@ -183,6 +273,22 @@ $app->run(); /** + * Handle the project name input selection on home page and redirect to testset project page + */ + +if (isset($_POST["projectInputSubmit"])) { + if (empty($_POST["projectInputValue"])) { + header('Location: ' . Slim\Slim::getInstance()->urlFor('root')); + exit(); + } + if (isset($_POST["projectInputValue"])) { + $project = htmlspecialchars($_POST['projectInputValue']); + header('Location: ' . Slim\Slim::getInstance()->urlFor('root') . 'testsetproject/' . $project); + exit(); + } +} + +/** * Handle the testset name input selection on home page and redirect to testset page */ diff --git a/non-puppet/qtmetrics2/project_search.php b/non-puppet/qtmetrics2/project_search.php new file mode 100644 index 0000000..c43c431 --- /dev/null +++ b/non-puppet/qtmetrics2/project_search.php @@ -0,0 +1,64 @@ +<?php +############################################################################# +## +## 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$ +## +############################################################################# + +/** + * Project autocomplete search + * @version 0.1 + * @since 30-06-2015 + * @author Juha Sippola + */ + +header('Content-Type: application/json'); +require 'src/Factory.php'; + +// Get the filter and make database search +if (isset($_GET['term'])) { + $list = array(); + $rows = Factory::getProjectsFiltered($_GET['term']); + foreach ($rows as $row) { + foreach ($row as $key => $value) { + if ($key === 'name') + $name = $value; + } + $list[] = $name; + } + // Return list as json string + echo json_encode($list); + +// Return empty string if filter not set +} else { + echo json_encode([]); +} + +?> diff --git a/non-puppet/qtmetrics2/scripts/project_autocomplete.js b/non-puppet/qtmetrics2/scripts/project_autocomplete.js new file mode 100644 index 0000000..6970c8e --- /dev/null +++ b/non-puppet/qtmetrics2/scripts/project_autocomplete.js @@ -0,0 +1,84 @@ +/* +############################################################################# +## +## 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$ +## +############################################################################# + +/** + * Project autocomplete + * @version 0.1 + * @since 30-06-2015 + * @author Juha Sippola + */ + +$(function() { + var cache = {}; + $( "#projectInput" ).autocomplete({ + minLength: 2, + + // get the filtered list (cached) + source: function( request, response ) { + var term = request.term; + if ( term in cache ) { + response( cache[ term ] ); + return; + } + $.getJSON( "project_search.php", request, function( data, status, xhr ) { + cache[ term ] = data; + response( data ); + }); + }, + + // detect the case without any matches + response: function (event, ui) { + if (ui.content.length === 0) { + ui.content.push({ + 'label': '(no match)', + 'value': '' + }); + } + } + }) + + // fill the list + .data("ui-autocomplete")._renderItem = function (ul, item) { + if (item.value === '') { + // 'no match' case + return $('<li class="ui-state-disabled">'+item.label+'</li>') + .appendTo(ul); + } else { + // list of matches + return $("<li>") + .append("<a>" + item.label + "</a>") + .appendTo(ul); + } + }; +}); diff --git a/non-puppet/qtmetrics2/src/ConfRun.php b/non-puppet/qtmetrics2/src/ConfRun.php new file mode 100644 index 0000000..80089f4 --- /dev/null +++ b/non-puppet/qtmetrics2/src/ConfRun.php @@ -0,0 +1,121 @@ +<?php +############################################################################# +## +## 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$ +## +############################################################################# + +/** + * ConfRun class + * @version 0.1 + * @since 30-06-2015 + * @author Juha Sippola + */ + +class ConfRun extends ProjectRun { + + /** + * Conf build results (these must follow the enumeration in the database) + */ + const RESULT_NOT_SET = NULL; + const RESULT_EMPTY = ""; + const RESULT_SUCCESS = "SUCCESS"; + const RESULT_FAILURE = "FAILURE"; + const RESULT_ABORTED = "ABORTED"; + const RESULT_UNDEF = "undef"; + + /** + * Conf name. + * @var string + */ + private $name; + + /** + * Forcesuccess flag (true = forcesuccess on). + * @var bool + */ + private $forcesuccess; + + /** + * Insignificance flag (true = insignificant). + * @var bool + */ + private $insignificant; + + /** + * ConfRun constructor. + * @param string $name + * @param string $projectName + * @param string $branchName + * @param string $stateName + * @param int $buildKey + * @param string $result + * @param bool $forcesuccess (true = forcesuccess on) + * @param bool $insignificant (true = insignificant) + * @param int $timestamp + * @param int $duration + */ + public function __construct($name, $projectName, $branchName, $stateName, $buildKey, $result, $forcesuccess, $insignificant, $timestamp, $duration) { + parent::__construct($projectName, $branchName, $stateName, $buildKey, $result, $timestamp, $duration); + $this->name = $name; + $this->forcesuccess = $forcesuccess; + $this->insignificant = $insignificant; + } + + /** + * Get name of the conf. + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get forcesuccess flag. + * @return bool (true = forcesuccess on) + */ + public function getForcesuccess() + { + return $this->forcesuccess; + } + + /** + * Get insignificance flag. + * @return bool (true = insignificant) + */ + public function getInsignificant() + { + return $this->insignificant; + } + +} + +?> diff --git a/non-puppet/qtmetrics2/src/Database.php b/non-puppet/qtmetrics2/src/Database.php index efc87c3..5ebf6e1 100644 --- a/non-puppet/qtmetrics2/src/Database.php +++ b/non-puppet/qtmetrics2/src/Database.php @@ -34,8 +34,8 @@ /** * Database class - * @version 0.4 - * @since 22-06-2015 + * @version 0.6 + * @since 30-06-2015 * @author Juha Sippola */ @@ -106,6 +106,31 @@ class Database { } /** + * Get list of projects matching the filter string. + * @param string $filter + * @return array (string name) + */ + public function getProjectsFiltered($filter) + { + $result = array(); + $query = $this->db->prepare(" + SELECT name + FROM project + WHERE name LIKE ? + ORDER BY name; + "); + $query->execute(array( + '%' . $filter . '%' + )); + while($row = $query->fetch(PDO::FETCH_ASSOC)) { + $result[] = array( + 'name' => $row['name'] + ); + } + return $result; + } + + /** * Get list of testsets matching the filter string. * @param string $filter * @return array (string name) @@ -185,12 +210,12 @@ class Database { /** * Get the latest build key for given project, branch and state - * @param string $project - * @param string $branch - * @param string $state + * @param string $runProject + * @param string $runBranch + * @param string $runState * @return string */ - public function getLatestProjectBranchBuildKey($project, $branch, $state) + public function getLatestProjectBranchBuildKey($runProject, $runBranch, $runState) { $result = array(); $query = $this->db->prepare(" @@ -204,9 +229,9 @@ class Database { LIMIT 1 "); $query->execute(array( - $project, - $branch, - $state + $runProject, + $runBranch, + $runState )); while($row = $query->fetch(PDO::FETCH_ASSOC)) { $result= $row['latest_build']; @@ -216,17 +241,17 @@ class Database { /** * Get the latest build keys by branch for given project and state - * @param string $project - * @param string $state + * @param string $runProject + * @param string $runState * @return array (string name, string key) */ - public function getLatestProjectBranchBuildKeys($project, $state) + public function getLatestProjectBranchBuildKeys($runProject, $runState) { $result = array(); $branches = self::getBranches(); foreach ($branches as $branch) { - $key = self::getLatestProjectBranchBuildKey($project, $branch['name'], $state); + $key = self::getLatestProjectBranchBuildKey($runProject, $branch['name'], $runState); if ($key) { $result[] = array( 'name' => $branch['name'], @@ -239,17 +264,22 @@ class Database { /** * Get the latest build result by branch for given project and state - * @param string $project - * @param string $state - * @return array (string name, string result) + * @param string $runProject + * @param string $runState + * @return array (string name, string result, string buildKey, string timestamp, string duration) */ - public function getLatestProjectBranchBuildResults($project, $state) + public function getLatestProjectBranchBuildResults($runProject, $runState) { $result = array(); - $builds = self::getLatestProjectBranchBuildKeys($project, $state); + $builds = self::getLatestProjectBranchBuildKeys($runProject, $runState); foreach ($builds as $build) { $query = $this->db->prepare(" - SELECT branch.name, project_run.result + SELECT + branch.name, + project_run.result, + project_run.build_key, + project_run.timestamp, + project_run.duration FROM project_run INNER JOIN branch ON branch_id = branch.id WHERE @@ -259,15 +289,128 @@ class Database { build_key = ?; "); $query->execute(array( - $project, - $state, + $runProject, + $runState, $build['name'], $build['key'] )); while($row = $query->fetch(PDO::FETCH_ASSOC)) { $result[] = array( 'name' => $row['name'], - 'result' => $row['result'] + 'result' => $row['result'], + 'buildKey' => $row['build_key'], + 'timestamp' => $row['timestamp'], + 'duration' => $row['duration'] + ); + } + } + return $result; + } + + /** + * Get the latest testset result by branch for given project and state + * @param string $runProject + * @param string $runState + * @return array (string project, string branch, string buildKey, string timestamp, int passed, int failed) + */ + public function getLatestProjectBranchTestsetResults($runProject, $runState) + { + $result = array(); + $builds = self::getLatestProjectBranchBuildKeys($runProject, $runState); + foreach ($builds as $build) { + $query = $this->db->prepare(" + SELECT + project.name AS project, + branch.name AS branch, + project_run.build_key, + project_run.timestamp, + COUNT(CASE WHEN testset_run.result LIKE '%passed' THEN testset_run.result END) AS passed, + COUNT(CASE WHEN testset_run.result LIKE '%failed' THEN testset_run.result END) AS failed + 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 + 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.branch_id = (SELECT id from branch WHERE name = ?) AND + project_run.build_key = ? + GROUP BY project.name; + "); + $query->execute(array( + $runProject, + $runState, + $build['name'], + $build['key'] + )); + while($row = $query->fetch(PDO::FETCH_ASSOC)) { + $result[] = array( + 'project' => $row['project'], + 'branch' => $row['branch'], + 'buildKey' => $row['build_key'], + 'timestamp' => $row['timestamp'], + 'passed' => $row['passed'], + 'failed' => $row['failed'] + ); + } + } + return $result; + } + + /** + * Get the latest testset result by branch for given project and state, for selected testset project. + * Similar to getLatestProjectBranchTestsetResults but listing only the selected testset project. + * @param string $testsetProject + * @param string $runProject + * @param string $runState + * @return array (string project, string branch, string buildKey, string timestamp, int passed, int failed) + */ + public function getLatestTestsetProjectBranchTestsetResults($testsetProject, $runProject, $runState) + { + $result = array(); + $builds = self::getLatestProjectBranchBuildKeys($runProject, $runState); + foreach ($builds as $build) { + $query = $this->db->prepare(" + SELECT + project.name AS project, + branch.name AS branch, + project_run.build_key, + project_run.timestamp, + COUNT(CASE WHEN testset_run.result LIKE '%passed' THEN testset_run.result END) AS passed, + COUNT(CASE WHEN testset_run.result LIKE '%failed' THEN testset_run.result END) AS failed + 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 + WHERE + project.name = ? AND + project_run.project_id = (SELECT id FROM project WHERE name = ?) AND + project_run.state_id = (SELECT id FROM state WHERE name = ?) AND + project_run.branch_id = (SELECT id from branch WHERE name = ?) AND + project_run.build_key = ? + GROUP BY project.name; + "); + $query->execute(array( + $testsetProject, + $runProject, + $runState, + $build['name'], + $build['key'] + )); + while($row = $query->fetch(PDO::FETCH_ASSOC)) { + $result[] = array( + 'project' => $row['project'], + 'branch' => $row['branch'], + 'buildKey' => $row['build_key'], + 'timestamp' => $row['timestamp'], + 'passed' => $row['passed'], + 'failed' => $row['failed'] ); } } @@ -288,7 +431,10 @@ class Database { $builds = self::getLatestProjectBranchBuildKeys($runProject, $runState); foreach ($builds as $build) { $query = $this->db->prepare(" - SELECT conf.name AS conf, branch.name AS branch, testset_run.result + SELECT + conf.name AS conf, + branch.name AS branch, + testset_run.result FROM testset_run INNER JOIN conf_run ON testset_run.conf_run_id = conf_run.id INNER JOIN conf ON conf_run.conf_id = conf.id @@ -503,7 +649,7 @@ class Database { } /** - * Get project build keys and timestamps by branch + * Get project run data by branch * @param string $runProject * @param string $runState * @return array (string branch, string build_key, string timestamp) @@ -538,12 +684,59 @@ 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) + */ + public function getConfBuildsByBranch($runProject, $runState) + { + $result = array(); + $query = $this->db->prepare(" + SELECT + branch.name AS branch, + conf.name AS conf, + project_run.build_key, + conf_run.forcesuccess, + conf_run.insignificant, + conf_run.result, + conf_run.timestamp, + conf_run.duration + FROM conf_run + 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 + WHERE + project_run.project_id = (SELECT id FROM project WHERE name = ?) AND + project_run.state_id = (SELECT id FROM state WHERE name = ?) + ORDER BY branch.name, conf, project_run.timestamp DESC; + "); + $query->execute(array( + $runProject, + $runState + )); + while($row = $query->fetch(PDO::FETCH_ASSOC)) { + $result[] = array( + 'branch' => $row['branch'], + 'conf' => $row['conf'], + 'buildKey' => $row['build_key'], + 'forcesuccess' => $row['forcesuccess'], + 'insignificant' => $row['insignificant'], + 'result' => $row['result'], + 'timestamp' => $row['timestamp'], + 'duration' => $row['duration'] + ); + } + return $result; + } + + /** * Get run results for a testset in specified builds by branch and configuration * @param string $testset * @param $testsetProject * @param string $runProject * @param string $runState - * @return array (string branch, string conf, string build_key, string result) + * @return array (string branch, string conf, string build_key, string result, string timestamp, string duration, int run) */ public function getTestsetResultsByBranchConf($testset, $testsetProject, $runProject, $runState) { @@ -592,6 +785,58 @@ class Database { } /** + * Get result counts for a testset project in specified builds by branch and configuration + * @param $testsetProject + * @param string $runProject + * @param string $runState + * @return array (string branch, string conf, string build_key, int passed, int ipassed, int failed, int ifailed) + */ + public function getTestsetProjectResultsByBranchConf($testsetProject, $runProject, $runState) + { + $result = array(); + $query = $this->db->prepare(" + SELECT + branch.name AS branch, + conf.name AS conf, + project_run.build_key, + COUNT(CASE WHEN testset_run.result = 'passed' THEN testset_run.result END) AS passed, + COUNT(CASE WHEN testset_run.result = 'ipassed' THEN testset_run.result END) AS ipassed, + COUNT(CASE WHEN testset_run.result = 'failed' THEN testset_run.result END) AS failed, + COUNT(CASE WHEN testset_run.result = 'ifailed' THEN testset_run.result END) AS ifailed + 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 + WHERE + project.name = ? AND + project_run.project_id = (SELECT id FROM project WHERE name = ?) AND + project_run.state_id = (SELECT id FROM state WHERE name = ?) + GROUP BY branch.name, project_run.build_key, conf.name + ORDER BY branch.name, conf.name, project_run.build_key DESC; + "); + $query->execute(array( + $testsetProject, + $runProject, + $runState + )); + while($row = $query->fetch(PDO::FETCH_ASSOC)) { + $result[] = array( + 'branch' => $row['branch'], + 'conf' => $row['conf'], + 'buildKey' => $row['build_key'], + 'passed' => $row['passed'], + 'ipassed' => $row['ipassed'], + 'failed' => $row['failed'], + 'ifailed' => $row['ifailed'] + ); + } + return $result; + } + + /** * Get the timestamp when database last refreshed * @return string (timestamp) */ diff --git a/non-puppet/qtmetrics2/src/Factory.php b/non-puppet/qtmetrics2/src/Factory.php index 0870f8c..f8e0378 100644 --- a/non-puppet/qtmetrics2/src/Factory.php +++ b/non-puppet/qtmetrics2/src/Factory.php @@ -34,14 +34,15 @@ /** * Factory class - * @version 0.3 - * @since 23-06-2015 + * @version 0.4 + * @since 30-06-2015 * @author Juha Sippola */ require_once 'Database.php'; require_once 'Project.php'; require_once 'ProjectRun.php'; +require_once 'ConfRun.php'; require_once 'Testset.php'; require_once 'TestsetRun.php'; @@ -125,6 +126,17 @@ class Factory { } /** + * Get list of projects matching the filter string. + * @param string $filter + * @return array (string name) + */ + public static function getProjectsFiltered($filter) + { + $result = Factory::db()->getProjectsFiltered($filter); + return $result; + } + + /** * Get list of testsets matching the filter string. * @param string $filter * @return array (string name) @@ -136,21 +148,17 @@ class Factory { } /** - * Create Project objects for those in database + * Create Project object for that in database + * @param string $project * @param string $runProject * @param string $runState - * @return array Project objects + * @return array Project object */ - public static function createProjects($runProject, $runState) + public static function createProject($project, $runProject, $runState) { - $objects = array(); - $dbEntries = self::db()->getProjects(); - foreach($dbEntries as $entry) { - $obj = new Project($entry['name']); - $obj->setStatus($runProject, $runState); - $objects[] = $obj; - } - return $objects; + $obj = new Project($project); + $obj->setStatus($runProject, $runState); + return $obj; } /** @@ -226,6 +234,35 @@ class Factory { return $obj; } +/* NEW */ + /** + * Create ConfRun objects for those in database + * @param string $runProject + * @param string $runState + * @return array ConfRun objects + */ + public static function createConfRuns($runProject, $runState) + { + $objects = array(); + $dbEntries = self::db()->getConfBuildsByBranch($runProject, $runState); + foreach($dbEntries as $entry) { + $obj = new ConfRun( + $entry['conf'], + $runProject, + $entry['branch'], + $runState, + $entry['buildKey'], + $entry['result'], + $entry['forcesuccess'], + $entry['insignificant'], + $entry['timestamp'], + $entry['duration'] + ); + $objects[] = $obj; + } + return $objects; + } + /** * Create TestsetRun objects for those in database * @param string $testset diff --git a/non-puppet/qtmetrics2/src/TestsetRun.php b/non-puppet/qtmetrics2/src/TestsetRun.php index f733712..e90f5cc 100644 --- a/non-puppet/qtmetrics2/src/TestsetRun.php +++ b/non-puppet/qtmetrics2/src/TestsetRun.php @@ -34,8 +34,8 @@ /** * TestsetRun class - * @version 0.3 - * @since 23-06-2015 + * @version 0.4 + * @since 30-06-2015 * @author Juha Sippola */ @@ -81,14 +81,13 @@ class TestsetRun extends ProjectRun { /** * TestsetRun constructor. - * TestsetRun include the result in the project configuration build - * @param string $testsetName + * @param string $name * @param string $testsetProjectName * @param string $projectName * @param string $branchName * @param string $stateName * @param int $buildKey - * @param string $configurationName + * @param string $confName * @param int $run (ordinal number) * @param string $result (plain result without any possible flags) * @param bool $insignificant (true = insignificant) diff --git a/non-puppet/qtmetrics2/src/test/DatabaseTest.php b/non-puppet/qtmetrics2/src/test/DatabaseTest.php index afb6671..e15157f 100644 --- a/non-puppet/qtmetrics2/src/test/DatabaseTest.php +++ b/non-puppet/qtmetrics2/src/test/DatabaseTest.php @@ -38,8 +38,8 @@ 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 - * @version 0.4 - * @since 23-06-2015 + * @version 0.6 + * @since 30-06-2015 * @author Juha Sippola */ @@ -117,6 +117,29 @@ class DatabaseTest extends PHPUnit_Framework_TestCase } /** + * Test getProjectsFiltered + * @dataProvider testGetProjectsFilteredData + */ + public function testGetProjectsFiltered($filter, $exp_match_count_min) + { + $db = Factory::db(); + $result = $db->getProjectsFiltered($filter); + $this->assertGreaterThanOrEqual($exp_match_count_min, count($result)); + foreach($result as $row) { + $this->assertArrayHasKey('name', $row); + } + } + public function testGetProjectsFilteredData() + { + return array( + array('base', 1), + array('ba', 3), + array('qt', 35), + array('invalid-name', 0) + ); + } + + /** * Test getTestsetsFiltered * @dataProvider testGetTestsetsFilteredData */ @@ -265,6 +288,9 @@ class DatabaseTest extends PHPUnit_Framework_TestCase foreach($result as $row) { $this->assertArrayHasKey('name', $row); $this->assertArrayHasKey('result', $row); + $this->assertArrayHasKey('buildKey', $row); + $this->assertArrayHasKey('timestamp', $row); + $this->assertArrayHasKey('duration', $row); $this->assertContains($row['result'], $exp_results); $branches[] = $row['name']; } @@ -278,14 +304,68 @@ class DatabaseTest extends PHPUnit_Framework_TestCase } /** + * Test getLatestProjectBranchTestsetResults + * @dataProvider testGetLatestProjectBranchTestsetResultsData + */ + public function testGetLatestProjectBranchTestsetResults($runProject, $runState, $exp_branches) + { + $confs = array(); + $db = Factory::db(); + $result = $db->getLatestProjectBranchTestsetResults($runProject, $runState); + $this->assertNotEmpty($result); + foreach($result as $row) { + $this->assertArrayHasKey('project', $row); + $this->assertArrayHasKey('branch', $row); + $this->assertArrayHasKey('buildKey', $row); + $this->assertArrayHasKey('timestamp', $row); + $this->assertArrayHasKey('passed', $row); + $this->assertArrayHasKey('failed', $row); + $this->assertContains($row['branch'], $exp_branches); + } + } + public function testGetLatestProjectBranchTestsetResultsData() + { + return array( + array('Qt5', 'state', array('dev', 'stable', 'master')) + ); + } + + /** + * Test getLatestTestsetProjectBranchTestsetResults + * @dataProvider testGetLatestTestsetProjectBranchTestsetResultsData + */ + public function testGetLatestTestsetProjectBranchTestsetResults($testsetProject, $runProject, $runState, $exp_branches) + { + $confs = array(); + $db = Factory::db(); + $result = $db->getLatestTestsetProjectBranchTestsetResults($testsetProject, $runProject, $runState); + $this->assertNotEmpty($result); + foreach($result as $row) { + $this->assertArrayHasKey('project', $row); + $this->assertArrayHasKey('branch', $row); + $this->assertArrayHasKey('buildKey', $row); + $this->assertArrayHasKey('timestamp', $row); + $this->assertArrayHasKey('passed', $row); + $this->assertArrayHasKey('failed', $row); + $this->assertContains($row['branch'], $exp_branches); + } + } + public function testGetLatestTestsetProjectBranchTestsetResultsData() + { + return array( + array('qtbase', 'Qt5', 'state', array('dev', 'stable', 'master')) + ); + } + + /** * Test getLatestTestsetConfBuildResults * @dataProvider testGetLatestTestsetConfBuildResultsData */ - public function testGetLatestTestsetConfBuildResults($testset, $testsetProject, $runProject, $state, $exp_conf, $exp_branches, $exp_results) + public function testGetLatestTestsetConfBuildResults($testset, $testsetProject, $runProject, $runState, $exp_conf, $exp_branches, $exp_results) { $confs = array(); $db = Factory::db(); - $result = $db->getLatestTestsetConfBuildResults($testset, $testsetProject, $runProject, $state); + $result = $db->getLatestTestsetConfBuildResults($testset, $testsetProject, $runProject, $runState); $this->assertNotEmpty($result); foreach($result as $row) { $this->assertArrayHasKey('name', $row); @@ -497,6 +577,54 @@ class DatabaseTest extends PHPUnit_Framework_TestCase } /** + * Test getConfBuildsByBranch + * @dataProvider testGetConfBuildsByBranchData + */ + public function testGetConfBuildsByBranch($runProject, $runState, $exp_branch, $exp_conf, $exp_key, $exp_result, $has_data) + { + $branches = array(); + $confs = array(); + $keys = array(); + $results = array(); + $db = Factory::db(); + $result = $db->getConfBuildsByBranch($runProject, $runState); + foreach($result as $row) { + $this->assertArrayHasKey('branch', $row); + $this->assertArrayHasKey('conf', $row); + $this->assertArrayHasKey('buildKey', $row); + $this->assertArrayHasKey('forcesuccess', $row); + $this->assertArrayHasKey('insignificant', $row); + $this->assertArrayHasKey('result', $row); + $this->assertArrayHasKey('timestamp', $row); + $this->assertArrayHasKey('duration', $row); + $branches[] = $row['branch']; + $confs[] = $row['conf']; + $keys[] = $row['buildKey']; + $results[] = $row['result']; + } + if ($has_data) { + $this->assertNotEmpty($result); + $this->assertContains($exp_branch, $branches); + $this->assertContains($exp_conf, $confs); + $this->assertContains($exp_key, $keys); + $this->assertContains($exp_result, $results); + } else { + $this->assertEmpty($result); + } + } + public function testGetConfBuildsByBranchData() + { + return array( + array('Qt5', 'state', 'dev', 'linux-g++_developer-build_qtnamespace_qtlibinfix_Ubuntu_11.10_x64', '1023', 'FAILURE', 1), + array('Qt5', 'state', 'stable', 'win32-msvc2010_developer-build_angle_Windows_7', '1348', 'SUCCESS', 1), + array('Qt5', 'state', 'stable', 'macx-clang_developer-build_OSX_10.8', '1348', 'SUCCESS', 1), + array('Qt5', 'state', 'stable', 'win32-msvc2010_developer-build_angle_Windows_7', '1348', 'SUCCESS', 1), + array('Qt5', 'state', 'dev', 'linux-g++-32_developer-build_Ubuntu_10.04_x86', 'BuildKeyInStringFormat12345', 'FAILURE', 1), + array('Qt5', 'invalid', '', '', '', '', 0) + ); + } + + /** * Test getTestsetResultsByBranchConf * @dataProvider testGetTestsetResultsByBranchConfData */ @@ -544,6 +672,51 @@ class DatabaseTest extends PHPUnit_Framework_TestCase } /** + * Test getTestsetProjectResultsByBranchConf + * @dataProvider testGetTestsetProjectResultsByBranchConfData + */ + public function testGetTestsetProjectResultsByBranchConf($testsetProject, $runProject, $runState, $exp_branch, $exp_conf, $exp_key, $has_data) + { + $branches = array(); + $confs = array(); + $keys = array(); + $results = array(); + $db = Factory::db(); + $result = $db->getTestsetProjectResultsByBranchConf($testsetProject, $runProject, $runState); + foreach($result as $row) { + $this->assertArrayHasKey('branch', $row); + $this->assertArrayHasKey('conf', $row); + $this->assertArrayHasKey('buildKey', $row); + $this->assertArrayHasKey('passed', $row); + $this->assertArrayHasKey('ipassed', $row); + $this->assertArrayHasKey('failed', $row); + $this->assertArrayHasKey('ifailed', $row); + $branches[] = $row['branch']; + $confs[] = $row['conf']; + $keys[] = $row['buildKey']; + } + if ($has_data) { + $this->assertNotEmpty($result); + $this->assertContains($exp_branch, $branches); + $this->assertContains($exp_conf, $confs); + $this->assertContains($exp_key, $keys); + } else { + $this->assertEmpty($result); + } + } + public function testGetTestsetProjectResultsByBranchConfData() + { + return array( + array('QtBase', 'Qt5', 'state', 'dev', 'linux-g++_developer-build_qtnamespace_qtlibinfix_Ubuntu_11.10_x64', '1023', 1), + array('QtBase', 'Qt5', 'state', 'stable', 'win32-msvc2010_developer-build_angle_Windows_7', '1348', 1), + array('QtBase', 'Qt5', 'state', 'stable', 'macx-clang_developer-build_OSX_10.8', '1348', 1), + array('QtBase', 'Qt5', 'state', 'stable', 'win32-msvc2010_developer-build_angle_Windows_7', '1348', 1), + array('QtBase', 'Qt5', 'state', 'dev', 'linux-g++-32_developer-build_Ubuntu_10.04_x86', 'BuildKeyInStringFormat12345', 1), + array('Qt5', 'Qt5', 'invalid', '', '', '', '', 0) + ); + } + + /** * Test getDbRefreshed */ public function testGetDbRefreshed() diff --git a/non-puppet/qtmetrics2/src/test/FactoryTest.php b/non-puppet/qtmetrics2/src/test/FactoryTest.php index 5f1028a..aa852a6 100644 --- a/non-puppet/qtmetrics2/src/test/FactoryTest.php +++ b/non-puppet/qtmetrics2/src/test/FactoryTest.php @@ -37,8 +37,8 @@ require_once(__DIR__.'/../Factory.php'); /** * Factory unit test class * @example To run (in qtmetrics root directory): php <path-to-phpunit>/phpunit.phar ./src/test - * @version 0.3 - * @since 23-06-2015 + * @version 0.4 + * @since 30-06-2015 * @author Juha Sippola */ @@ -100,6 +100,29 @@ class FactoryTest extends PHPUnit_Framework_TestCase } /** + * Test getProjectsFiltered + * @dataProvider testGetProjectsFilteredData + */ + public function testGetProjectsFiltered($project, $exp_count) + { + $result = Factory::getProjectsFiltered($project); + $this->assertEquals($exp_count, count($result)); + } + public function testGetProjectsFilteredData() + { + return array( + array('', 35), // test data includes 35 projects + array('qt',35), // all + array('ba', 3), + array('bas', 1), + array('base', 1), + array('qtbase', 1), + array('QtBase', 1), + array('invalid-name', 0) + ); + } + + /** * Test getTestsetsFiltered * @dataProvider testGetTestsetsFilteredData */ @@ -125,23 +148,21 @@ class FactoryTest extends PHPUnit_Framework_TestCase } /** - * Test createProjects - * @dataProvider testCreateProjectsData + * Test createProject + * @dataProvider testCreateProjectData */ - public function testCreateProjects($runProject, $runState) + public function testCreateProject($project, $runProject, $runState) { - $projects = Factory::createProjects($runProject, $runState); - foreach($projects as $project) { - $this->assertTrue($project instanceof Project); - if ($project->getName() === $runProject) { // check only the projects with project_run data - $this->assertNotEmpty($project->getStatus()); - } + $project = Factory::createProject($project, $runProject, $runState); + $this->assertTrue($project instanceof Project); + if ($project->getName() === $runProject) { // check only the projects with project_run data + $this->assertNotEmpty($project->getStatus()); } } - public function testCreateProjectsData() + public function testCreateProjectData() { return array( - array('Qt5', 'state',) // project with project_run data + array('Qt5', 'Qt5', 'state',) // project with project_run data ); } @@ -220,12 +241,31 @@ class FactoryTest extends PHPUnit_Framework_TestCase } /** + * Test createConfRuns + * @dataProvider testCreateConfRunsData + */ + public function testCreateConfRuns($name, $projectName, $branchName, $stateName, $buildKey, $result, $insignificant, $forcesuccess, $timestamp, $duration) + { + $runs = Factory::createConfRuns($name, $projectName, $branchName, $stateName, $buildKey, $result, $insignificant, $forcesuccess, $timestamp, $duration); + foreach($runs as $run) { + $this->assertTrue($run instanceof ConfRun); + } + } + public function testCreateConfRunsData() + { + return array( + array('win64-msvc2012_developer-build_qtnamespace_Windows_8', 'Qt5', 'stable', 'state', '1348', 'failed', true, false, '28.5.2013 0:54', 8130), + array('linux-g++-32_developer-build_Ubuntu_10.04_x86', 'Qt5', 'dev', 'state', 'BuildKeyInStringFormat12345', 'failed', false, true, '28.5.2013 0:54', 8130) + ); + } + + /** * Test createTestsetRuns * @dataProvider testCreateTestsetRunsData */ - public function testCreateTestsetRuns($name, $projectName, $projectName, $branchName, $stateName, $buildKey, $confName, $run, $result, $insignificant, $timestamp, $duration) + public function testCreateTestsetRuns($name, $testsetProject, $projectName, $branchName, $stateName, $buildKey, $confName, $run, $result, $insignificant, $timestamp, $duration) { - $runs = Factory::createTestsetRuns($name, $projectName, $projectName, $branchName, $stateName, $buildKey, $confName, $run, $result, $insignificant, $timestamp, $duration); + $runs = Factory::createTestsetRuns($name, $testsetProject, $projectName, $branchName, $stateName, $buildKey, $confName, $run, $result, $insignificant, $timestamp, $duration); foreach($runs as $run) { $this->assertTrue($run instanceof TestsetRun); } diff --git a/non-puppet/qtmetrics2/templates/about.html b/non-puppet/qtmetrics2/templates/about.html index 1ba66fc..6e2ca7d 100644 --- a/non-puppet/qtmetrics2/templates/about.html +++ b/non-puppet/qtmetrics2/templates/about.html @@ -34,8 +34,8 @@ /** * About window content - * @version 0.6 - * @since 26-06-2015 + * @version 0.7 + * @since 30-06-2015 * @author Juha Sippola */ @@ -44,4 +44,4 @@ <p>This is Qt Metrics revision 2 with redesigned UI and database.</p> <p>These pages are still <strong>under construction</strong> and therefore the views and functionality is limited.</p> <p>See the <a href="https://wiki.qt.io/Qt_Metrics_2_Backlog" target="_blank">backlog</a> for development items currently identified or in progress.</p> -<p><small>Version 0.6 (26-Jun-2015)</small></p> +<p><small>Version 0.7 (30-Jun-2015)</small></p> diff --git a/non-puppet/qtmetrics2/templates/build_project.html b/non-puppet/qtmetrics2/templates/build_project.html new file mode 100644 index 0000000..c400e28 --- /dev/null +++ b/non-puppet/qtmetrics2/templates/build_project.html @@ -0,0 +1,342 @@ +{# +############################################################################# +## +## 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$ +## +############################################################################# + +/** + * Build project page + * @version 0.1 + * @since 30-06-2015 + * @author Juha Sippola + */ + +#} + +{% include "header.html" %} + +{# Failed/passed bar area size in px #} +{% set BAR_AREA = 120 %} + +{# project as Project object +/** + * @var Project[] project + */ +#} + +{# confRuns as ConfRun objects +/** + * @var ConfRun[] confRuns + */ +#} + +<ol class="breadcrumb"> + {% for link in breadcrumb %} + <li><a href="{{ link.link }}">{{ link.name }}</a></li> + {% endfor %} + <li class="active">{{ project.getName }}</li> +</ol> + +<div class="container-fluid"> + <div class="row"> + + <div class="col-sm-12 col-md-12 main"> + + {# Check if any runs available #} + {% set runsAvailable = 0 %} + {% for run in latestProjectRuns %} + {% set runsAvailable = 1 %} + {% endfor %} + + {##### Title #####} + + <h1 class="page-header"> + {{ project.getName }} + <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>Build Project</strong><br> + <ul> + <li>In <strong>Latest Status</strong>, the <strong>latest result</strong> shows the overall project status based on the + latest <strong>{{ masterProject }} {{ masterState }}</strong> builds across all branches, + and the <strong>branch</strong> results across all configurations (both show failed if failed + in one or in several builds).</li> + <li><strong>Results in Branches</strong> shows the {{ project.getName }} run results by branch + and their configuration on <strong>{{ masterProject }} {{ masterState }}</strong> builds + (<span class="glyphicon glyphicon-ok green"></span> = {{ constant('ConfRun::RESULT_SUCCESS') }}, + <span class="glyphicon glyphicon-ok-sign green"></span> = {{ constant('ConfRun::RESULT_SUCCESS') }} as forcesuccess, + <span class="glyphicon glyphicon-remove red"></span> = {{ constant('ConfRun::RESULT_FAILURE') }}, + <span class="glyphicon glyphicon-ban-circle gray"></span> = {{ constant('ConfRun::RESULT_UNDEF') }}); + details on the runs are available as tooltip on result icon.</li> + </ul> + </div> + </div> + + {% if runsAvailable %} + + {##### Latest Status #####} + + <div class="panel panel-primary"> + <div class="panel-heading"> + <h4 class="panel-title bold">Latest Status</h4> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th>project</th> + <th class="rightBorder">latest result</th> + {% for branch in latestProjectRuns %} + <th class="center">{{ branch.name }}</th> + {% endfor %} + </tr> + </thead> + <tbody> + <tr> + {# Project name #} + <td>{{ project.getName }}</td> + + {# Testset status according to the latest build results #} + {% if project.getStatus == constant('ConfRun::RESULT_SUCCESS') %} + {% set resultIcon = 'glyphicon glyphicon-ok green' %} + {% elseif project.getStatus == constant('ConfRun::RESULT_FAILURE') %} + {% set resultIcon = 'glyphicon glyphicon-remove red' %} + {% else %} + {% set resultIcon = '' %} + {% endif %} + <td class="rightBorder"><span class="spaceHorizontal {{ resultIcon }}"></span>{{ project.getStatus }}</td> + + {# Result (by branch) #} + {% for run in latestProjectRuns %} + {% if run.result == constant('ConfRun::RESULT_SUCCESS') %} + {% set resultIcon = 'glyphicon glyphicon-ok green' %} + {% elseif run.result == constant('ConfRun::RESULT_FAILURE') %} + {% set resultIcon = 'glyphicon glyphicon-remove red' %} + {% else %} + {% set resultIcon = 'glyphicon glyphicon-ban-circle gray' %} + {% endif %} + <td class="center"> + <span class="spaceHorizontal {{ resultIcon }} clickOnTouch" + data-toggle="tooltip" data-placement="top" data-html="true" + title="<table> + <tr><th>Branch: </td><td>{{ run.name }}</td></tr> + <tr><th>Build key: </td><td>{{ run.buildKey }}</td></tr> + <tr><th>Timestamp: </td><td>{{ run.timestamp }}</td></tr> + <tr><th>Result: </td><td>{{ run.result }}</td></tr> + <tr><th>Duration: </td><td>{{ run.duration}}</td></tr> + </table>"> + </span> + </td> + {% endfor %} + </tr> + </tbody> + </table> + </div> {# /table-responsive #} + </div> {# /panel-body #} + </div> {# /panel... #} + + {##### Results in Branches #####} + + <div class="panel panel-primary"> + <div class="panel-heading"> + <h4 class="panel-title bold">Results in Branches</h4> + </div> + </div> + + {# Get branches #} + {% set branches = [] %} + {% for run in projectBuilds %} + {% if run.branch not in branches %} + {% set branches = branches|merge([run.branch]) %} + {% endif %} + {% endfor %} + + {# Loop all the branches #} + {% for branch in branches %} + + {# Get all build keys #} + {% set buildKey = '' %} + {% set buildKeys = [] %} + {% set dates = [] %} + {% for run in projectBuilds %} + {% if run.branch == branch %} + {% if buildKey != run.buildKey %} + {% set buildKey = run.buildKey %} + {% set buildKeys = buildKeys|merge([run.buildKey]) %} + {% set dates = dates|merge([run.timestamp]) %} + {% endif %} + {% endif %} + {% endfor %} + + {# Check if conf run for this branch #} + {% set confBranch = 0 %} + {% for run in confRuns if run.getBranchName == branch %} + {% set confBranch = 1 %} + {% endfor %} + + {# Show branch if conf run for it #} + {% if confBranch %} + <div class="panel panel-info"> + <div class="panel-heading"> + <h4 class="panel-title bold">{{ branch }}</h4> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th class="bold rightBorder">{{ project.getName }}</th> + {% for key, buildKey in buildKeys %} + <th class="center"> + {% if buildKey|length > 6 %} + <span class="clickOnTouch" data-toggle="tooltip" data-placement="top" + title="{{ buildKey }}">{{ buildKey|slice(0, 4) }}... + </span> + {% else %} + {{ buildKey }} + {% endif %} + <br> + <span class="gray"><small>{{ dates[key]|date("m-d") }}</small></span> + </th> + {% endfor %} + </tr> + </thead> + <tbody> + {% set confPrev = '' %} + {% set buildKeyIndexPrinted = -1 %} + {% set buildKeyFound = 0 %} + {% for run in confRuns if run.getBranchName == branch %} + + {# New row for each conf #} + {% if confPrev != run.getName %} + {# Close previous row #} + {% if confPrev != '' %} + {# Fill empty cells at the end of the row #} + {% for key, buildKey in buildKeys %} + {% if key > buildKeyIndexPrinted %} + <td></td> + {% endif %} + {% endfor %} + </tr> + {% endif %} + <tr> + <td class="rightBorder"><small>{{ run.getName }}</small></td> + {% set buildKeyIndexPrinted = -1 %} + {% endif %} + + {# Result per build key #} + {% set buildKeyFound = 0 %} + {% for key, buildKey in buildKeys %} + {# Print each column only once (checked based on column index key and buildKeyFound flag) #} + {% if key > buildKeyIndexPrinted and not buildKeyFound %} + {% if buildKey == run.getBuildKey %} + {# Print result #} + {% set forcesuccess = '' %} + {% if run.getResult == constant('ConfRun::RESULT_SUCCESS') %} + {% if run.getForcesuccess == 0 %} + {% set resultIcon = 'glyphicon glyphicon-ok green' %} + {% else %} + {# Forcesuccess #} + {% set resultIcon = 'glyphicon glyphicon-ok-sign green' %} + {% set forcesuccess = ' (as forcesuccess)' %} + {% endif %} + {% elseif run.getResult == constant('ConfRun::RESULT_FAILURE') %} + {% set resultIcon = 'glyphicon glyphicon-remove red' %} + {% elseif run.getResult == constant('ConfRun::RESULT_UNDEF') %} + {% set resultIcon = 'glyphicon glyphicon-ban-circle gray' %} + {% else %} + {% set resultIcon = '' %} + {% endif %} + <td class="center"> + <span class="spaceHorizontal {{ resultIcon }} clickOnTouch" + data-toggle="tooltip" data-placement="top" data-html="true" + title="<table> + <tr><th>Build key: </th><td>{{ buildKey }}</td></tr> + <tr><th>Configuration: </th><td>{{ run.getName }}</td></tr> + <tr><th>Timestamp: </th><td>{{ run.getTimestamp }}</td></tr> + <tr><th>Result: </th><td>{{ run.getResult }} {{ forcesuccess }}</td></tr> + <tr><th>Duration: </th><td>{{ run.getDuration }}</td></tr> + <tr><th>Forcesuccess: </th><td>{% if run.getForcesuccess %}yes{% else %}no{% endif %}</td></tr> + <tr><th>Insignificant: </th><td>{% if run.getInsignificant %}yes{% else %}no{% endif %}</td></tr> + </table>"> + </span> + </td> + {% set buildKeyFound = 1 %} + {% else %} + {# Print empty cell #} + <td></td> + {% endif %} + {% set buildKeyIndexPrinted = key %} + {% endif %} + {% endfor %} + {% set confPrev = run.getName %} + {% endfor %} + + {# Close last row (also fill empty cells at the end of the row) #} + {% for key, buildKey in buildKeys %} + {% if key > buildKeyIndexPrinted %} + <td></td> + {% endif %} + {% endfor %} + </tr> + </tbody> + </table> + </div> {# /table-responsive #} + </div> {# /panel-body #} + </div> {# /panel... #} + {% endif %} {# testsetBranch #} + {% endfor %} + + {% else %} {# runsAvailable #} + <div class="alert alert-danger" role="alert"> + No build data available for project {{ project.getName }}! + </div> + {% endif %} + + </div> {# /col... #} + </div> {# /row #} + +</div> {# /container-fluid #} + +{% include "footer.html" %} + +{# Local scripts for this page #} +<script src="scripts/tooltip.js"></script> + +{% include "close.html" %} diff --git a/non-puppet/qtmetrics2/templates/home.html b/non-puppet/qtmetrics2/templates/home.html index ed41f70..b9d3486 100644 --- a/non-puppet/qtmetrics2/templates/home.html +++ b/non-puppet/qtmetrics2/templates/home.html @@ -34,8 +34,8 @@ /** * Home page - * @version 0.2 - * @since 18-06-2015 + * @version 0.4 + * @since 30-06-2015 * @author Juha Sippola */ @@ -59,17 +59,23 @@ <div class="row"> - <div class="col-md-6"> + <div class="col-md-1"> + </div> + + <div class="col-md-4"> + <hr> <h2>Overview</h2> - <p>See the overview of the builds by projects across the latest branches:</p> + <p>See the latest build status across the branches:</p> <div> - {% for state in states %} - <a class="btn btn-default btn-xs" disabled="disabled" href="{{ overviewRoute }}/{{ state.name }}" role="button">{{ state.name }}</a> - {% endfor %} + <a class="btn btn-primary btn-xs" href="{{ overviewRoute }}" role="button">{{ masterProject }} {{ masterState }}</a> </div> </div> - <div class="col-md-6"> + <div class="col-md-1"> + </div> + + <div class="col-md-4"> + <hr> <h2>Platforms</h2> <p>See target platform status:</p> <div> @@ -86,7 +92,9 @@ {% endfor %} {% for name in os %} <div class="btn-group"> - <button type="button" class="btn btn-default btn-xs dropdown-toggle" disabled="disabled" data-toggle="dropdown" aria-expanded="false">{{ name }}<span class="caret"></span></button> + <button type="button" class="btn btn-primary btn-xs dropdown-toggle" disabled="disabled" + data-toggle="dropdown" aria-expanded="false">{{ name }}<span class="caret"></span> + </button> <ul class="dropdown-menu textSmall" role="menu"> {% for platform in platforms %} {% if platform.os == name %} @@ -100,62 +108,80 @@ {% endfor %} </div> </div> + </div> <div class="row"> - <div class="col-md-6"> - <h2>Branches</h2> - <p>See branch status:</p> + <div class="col-md-1"> + </div> + + <div class="col-md-4"> + <hr> + <h2>Projects</h2> + <p>See testset status for a project:</p> <div> - {% for branch in branches %} - <a class="btn btn-default btn-xs" disabled="disabled" href="{{ branchRoute }}/{{ branch.name }}" role="button">{{ branch.name }}</a> - {% endfor %} + <form class="form-horizontal" role="form" method="post"> + <div class="input-group input-group-sm"> + <input id="projectInput" name="projectInputValue" type="text" class="form-control" placeholder="project name..."> + <span class="input-group-btn"> + <input id="projectInputSubmit" name="projectInputSubmit" type="submit" class="btn btn-primary" value="Show"> + </span> + </div> + </form> </div> </div> + <div class="col-md-1"> + </div> + <div class="col-md-4"> + <hr> <h2>Tests</h2> - <p>See top failure lists or single testset results:</p> - <div> - <a class="btn btn-default btn-xs" href="{{ testRoute }}/top" role="button">top failures</a> - <a class="btn btn-default btn-xs" href="{{ testRoute }}/flaky" role="button">flaky testsets</a> - </div> + <p>See results for a testset:</p> <div> <form class="form-horizontal" role="form" method="post"> <div class="input-group input-group-sm"> <input id="testsetInput" name="testsetInputValue" type="text" class="form-control" placeholder="testset name..."> <span class="input-group-btn"> - <input id="testsetInputSubmit" name="testsetInputSubmit" type="submit" class="btn btn-default" value="Show"> + <input id="testsetInputSubmit" name="testsetInputSubmit" type="submit" class="btn btn-primary" value="Show"> </span> - </div> {# /input-group #} + </div> </form> </div> + <br> + <p>See testset lists:</p> + <div> + <a class="btn btn-primary btn-xs" href="{{ testRoute }}/top" role="button">top failures</a> + <a class="btn btn-primary btn-xs" href="{{ testRoute }}/flaky" role="button">flaky testsets</a> + </div> </div> + </div> <div class="row"> - <div class="col-md-6"> - <h2>Projects</h2> - <p>See project status:</p> + <div class="col-md-1"> + </div> + + <div class="col-md-4"> + <hr> + <h2>Branches</h2> + <p>See branch status:</p> <div> - {% for project in projects %} - {# Show button color based on project status (according to the latest build results) #} - {% if project.getStatus == constant('ProjectRun::RESULT_SUCCESS') %} - {% set buttonStatus = 'btn-success' %} - {% elseif project.getStatus == constant('ProjectRun::RESULT_FAILURE') %} - {% set buttonStatus = 'btn-danger' %} - {% else %} - {% set buttonStatus = 'btn-default' %} - {% endif %} - {# Show only valid projects #} - {% if project.getStatus != constant('ProjectRun::RESULT_EMPTY') %} - <a class="btn {{ buttonStatus }} btn-xs" disabled="disabled" href="{{ overviewRoute }}/state/{{ project.getName }}" role="button">{{ project.getName }}</a> - {% endif %} + {% for branch in branches %} + <a class="btn btn-primary btn-xs" disabled="disabled" href="{{ branchRoute }}/{{ branch.name }}" role="button">{{ branch.name }}</a> {% endfor %} </div> </div> + + <div class="col-md-1"> + </div> + + <div class="col-md-4"> + <hr> + </div> + </div> <br> @@ -166,6 +192,7 @@ {% include "footer.html" %} {# Local scripts for this page #} +<script src="scripts/project_autocomplete.js"></script> <script src="scripts/testset_autocomplete.js"></script> {% include "close.html" %} diff --git a/non-puppet/qtmetrics2/templates/overview.html b/non-puppet/qtmetrics2/templates/overview.html new file mode 100644 index 0000000..8ccb955 --- /dev/null +++ b/non-puppet/qtmetrics2/templates/overview.html @@ -0,0 +1,237 @@ +{# +############################################################################# +## +## 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$ +## +############################################################################# + +/** + * Overview page + * @version 0.1 + * @since 30-06-2015 + * @author Juha Sippola + */ + +#} + +{% include "header.html" %} + +{# Failed/passed bar area size in px #} +{% set BAR_AREA = 60 %} + +<ol class="breadcrumb"> + {% for link in breadcrumb %} + <li><a href="{{ link.link }}">{{ link.name }}</a></li> + {% endfor %} + <li class="active">overview</li> +</ol> + +<div class="container-fluid"> + <div class="row"> + + <div class="col-sm-12 col-md-12 main"> + + {##### Title #####} + + <h1 class="page-header"> + Overview: {{ masterProject }} {{ masterState }} + <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>Overview</strong><br> + <ul> + <li><strong>Latest {{ masterProject }} {{ masterState }} Build Status</strong> shows + the overall result of the latest <strong>{{ masterProject }} {{ masterState }}</strong> + builds themselves by branch; details on the runs are available as tooltip on result icon.</li> + <li><strong>Latest Testing Status</strong> shows the status based on testset results + in the latest <strong>{{ masterProject }} {{ masterState }}</strong> builds in any + configuration by testset project across all branches.</li> + </ul> + </div> + </div> + + {##### Latest Build Status #####} + + <div class="panel panel-primary"> + <div class="panel-heading"> + <h4 class="panel-title bold">Latest {{ masterProject }} {{ masterState }} Build Status</h4> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th class="rightBorder">project</th> + {% for branch in latestProjectRuns %} + <th class="center">{{ branch.name }}</th> + {% endfor %} + </tr> + </thead> + <tbody> + <tr> + {# Project name #} + <td class="rightBorder"><a href="{{ buildProjectRoute }}/{{ masterProject }}">{{ masterProject }}</a></td> + + {# Result (by branch) #} + {% for run in latestProjectRuns %} + {% if run.result == constant('projectRun::RESULT_SUCCESS') %} + {% set resultIcon = 'glyphicon glyphicon-ok green' %} + {% elseif run.result == constant('projectRun::RESULT_FAILURE') %} + {% set resultIcon = 'glyphicon glyphicon-remove red' %} + {% else %} + {% set resultIcon = 'glyphicon glyphicon-ban-circle gray' %} + {% endif %} + <td class="center"> + <span class="spaceHorizontal {{ resultIcon }} clickOnTouch" + data-toggle="tooltip" data-placement="top" data-html="true" + title="<table> + <tr><th>Branch: </td><td>{{ run.name }}</td></tr> + <tr><th>Build key: </td><td>{{ run.buildKey }}</td></tr> + <tr><th>Timestamp: </td><td>{{ run.timestamp }}</td></tr> + <tr><th>Result: </td><td>{{ run.result }}</td></tr> + <tr><th>Duration: </td><td>{{ run.duration}}</td></tr> + </table>"> + </span> + </td> + {% endfor %} + </tr> + + </tbody> + </table> + </div> {# /table-responsive #} + </div> {# /panel-body #} + </div> {# /panel... #} + + {##### Latest Testing Status #####} + + <div class="panel panel-primary"> + <div class="panel-heading"> + <h4 class="panel-title bold">Latest Testing Status</h4> + </div> + + {# Get branches and projects #} + {% set branches = [] %} + {% set projects = [] %} + {% for run in latestTestsetRuns %} + {% if run.branch not in branches %} + {% set branches = branches|merge([run.branch]) %} + {% endif %} + {% if run.project not in projects %} + {% set projects = projects|merge([run.project]) %} + {% endif %} + {% endfor %} + + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th class="rightBorder">testset project</th> + {% for branch in branches %} + <th class="center">{{ branch }}<br> + <small>failed <span class ="gray">(total)</span></small> + </th> + <th class="showInLargeDisplay"></th> + {% endfor %} + </tr> + </thead> + <tbody> + {# Calculate max failed result count for the bar #} + {% set maxCount = 1 %} + {% for run in latestTestsetRuns %} + {% if run.failed > maxCount %} + {% set maxCount = run.failed %} + {% endif %} + {% endfor %} + + {% for project in projects %} + <tr> + {# Project name #} + <td class="rightBorder"><a href="{{ testsetProjectRoute }}/{{ project }}">{{ project }}</a></td> + + {# Result (by branch) #} + {% for branch in branches %} + {% for run in latestTestsetRuns if project == run.project and branch == run.branch %} + + {# Show results #} + {% set failed = run.failed %} + {% set passed = run.passed %} + {% set total = passed + failed %} + <td class="center">{{ failed }}<span class ="gray"> ({{ total }})</span></td> + + {# Show results as bars (scaled to BAR_AREA px) #} + {% set failedBar = ((BAR_AREA/maxCount) * failed)|round(0, 'floor') %} + {% if (failed > 0) and (failedBar == 0) %} + {% set failedBar = 1 %} + {% endif %} + {% if failed == 0 %} + {% set failed = '' %} + {% endif %} + {% if (passed > 0) and (failed == 0) %} + {% set passedBar = maxCount %} + {% else %} + {% set passed = '' %} + {% endif %} + <td class="center showInLargeDisplay"> + <div> + <div class="floatLeft redBackground" style="width: {{ failedBar }}px"><small>{{ failed }}</small></div> + <div class="floatLeft greenBackground" style="width: {{ passedBar }}px"><small>{{ passed }}</small></div> + </div> + </td> + {% else %} + <td></td> + {% endfor %} + {% endfor %} + </tr> + {% endfor %} + </tbody> + </table> + </div> {# /table-responsive #} + </div> {# /panel-body #} + </div> {# /panel... #} + + </div> {# /col... #} + </div> {# /row #} + +</div> {# /container-fluid #} + +{% include "footer.html" %} + +{# Local scripts for this page #} +<script src="scripts/tooltip.js"></script> + +{% include "close.html" %} diff --git a/non-puppet/qtmetrics2/templates/testset.html b/non-puppet/qtmetrics2/templates/testset.html index 59f98e4..9df501c 100644 --- a/non-puppet/qtmetrics2/templates/testset.html +++ b/non-puppet/qtmetrics2/templates/testset.html @@ -34,8 +34,8 @@ /** * Testset page - * @version 0.4 - * @since 26-06-2015 + * @version 0.5 + * @since 30-06-2015 * @author Juha Sippola */ @@ -70,6 +70,12 @@ <div class="col-sm-12 col-md-12 main"> + {# Check if any runs available #} + {% set runsAvailable = 0 %} + {% for run in testsetRuns %} + {% set runsAvailable = 1 %} + {% endfor %} + {##### Title #####} <h1 class="page-header"> @@ -97,256 +103,264 @@ (during the last {{ lastDaysFlaky }} days, since {{ sinceDateFlaky }}).</li> <li><strong>Results in Branches</strong> shows the {{ testset.getName }} run results by branch and their configuration on <strong>{{ masterProject }} {{ masterState }}</strong> builds - (<span class="glyphicon glyphicon-ok green"></span> = passed, - <span class="glyphicon glyphicon-ok-sign green"></span> = passed as flaky, - <span class="glyphicon glyphicon-remove red"></span> = failed); details on the runs are - available as tooltip on result icon.</li> + (<span class="glyphicon glyphicon-ok green"></span> = {{ constant('TestsetRun::RESULT_SUCCESS') }}, + <span class="glyphicon glyphicon-ok-sign green"></span> = {{ constant('TestsetRun::RESULT_SUCCESS') }} as flaky, + <span class="glyphicon glyphicon-remove red"></span> = {{ constant('TestsetRun::RESULT_FAILURE') }}); + details on the runs are available as tooltip on result icon.</li> </ul> </div> </div> - {##### Summary #####} - - <div class="panel panel-primary"> - <div class="panel-heading"> - <h4 class="panel-title bold">Summary</h4> - </div> - <div class="panel-body"> - <div class="table-responsive"> - <table class="table table-striped"> - <thead> - <tr> - <th>project</th> - <th>latest result</th> - <th class="leftBorder center"><span class="spaceHorizontal glyphicon glyphicon-remove red"></span>failed <span class ="gray">(total)</span></th> - <th class="showInLargeDisplay"></th> - <th class="leftBorder center"><span class="spaceHorizontal glyphicon glyphicon-ok-sign green"></span>flaky <span class ="gray">(total)</span></th> - <th class="showInLargeDisplay"></th> - </tr> - </thead> - <tbody> - {# Calculate max result count for the bar #} - {% set maxCount = 1 %} - {% if (testset.getTestsetResultCounts.passed + testset.getTestsetResultCounts.failed) > maxCount %} - {% set maxCount = testset.getTestsetResultCounts.passed + testset.getTestsetResultCounts.failed %} - {% endif %} - {% if testset.getTestsetFlakyCounts.flaky > maxCount %} - {% set maxCount = testset.getTestsetFlakyCounts.flaky %} - {% endif %} - - {# Print summary #} - <tr> - {# Project name #} - <td>{{ testset.getProjectName }}</td> - - {# Testset status according to the latest build results #} - {% if testset.getStatus == constant('testsetRun::RESULT_SUCCESS') %} - {% set resultIcon = 'glyphicon glyphicon-ok green' %} - {% elseif testset.getStatus == constant('testsetRun::RESULT_FAILURE') %} - {% set resultIcon = 'glyphicon glyphicon-remove red' %} - {% else %} - {% set resultIcon = '' %} - {% endif %} - <td><span class="spaceHorizontal {{ resultIcon }}"></span>{{ testset.getStatus }}</td> - - {# Show failed #} - {% set failed = testset.getTestsetResultCounts.failed %} - {% set passed = testset.getTestsetResultCounts.passed %} - {% set total = passed + failed %} - <td class="leftBorder center">{{ failed }}<span class ="gray"> ({{ total }})</span></td> - {# Show results as bars (scaled to BAR_AREA px) #} - {% set passedBar = ((BAR_AREA/maxCount) * passed)|round(0, 'floor') %} - {% if (passed > 0) and (passedBar == 0) %} - {% set passedBar = 1 %} - {% endif %} - {% set failedBar = ((BAR_AREA/maxCount)*failed)|round(0, 'floor') %} - {% if (failed > 0) and (failedBar == 0) %} - {% set failedBar = 1 %} + {% if runsAvailable %} + + {##### Latest Status #####} + + <div class="panel panel-primary"> + <div class="panel-heading"> + <h4 class="panel-title bold">Latest Status</h4> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th>project</th> + <th>latest result</th> + <th class="leftBorder center"><span class="spaceHorizontal glyphicon glyphicon-remove red"></span>failed <span class ="gray">(total)</span></th> + <th class="showInLargeDisplay"></th> + <th class="leftBorder center"><span class="spaceHorizontal glyphicon glyphicon-ok-sign green"></span>flaky <span class ="gray">(total)</span></th> + <th class="showInLargeDisplay"></th> + </tr> + </thead> + <tbody> + {# Calculate max result count for the bar #} + {% set maxCount = 1 %} + {% if (testset.getTestsetResultCounts.passed + testset.getTestsetResultCounts.failed) > maxCount %} + {% set maxCount = testset.getTestsetResultCounts.passed + testset.getTestsetResultCounts.failed %} {% endif %} - <td class="center showInLargeDisplay"> - <div> - <div class="floatLeft redBackground" style="width: {{ failedBar }}px">{{ failed }}</div> - <div class="floatLeft greenBackground" style="width: {{ passedBar }}px">{{ passed }}</div> - </div> - </td> - - {# Show flaky #} - {% set flaky = testset.getTestsetFlakyCounts.flaky %} - <td class="leftBorder center">{{ flaky }}<span class ="gray"> ({{ total }})</span></td> - {# Show results as bars (scaled to BAR_AREA px) #} - {% set flakyBar = ((BAR_AREA/maxCount)*flaky)|round(0, 'floor') %} - {% if (flaky > 0) and (flakyBar == 0) %} - {% set flakyBar = 1 %} + {% if testset.getTestsetFlakyCounts.flaky > maxCount %} + {% set maxCount = testset.getTestsetFlakyCounts.flaky %} {% endif %} - <td class="center showInLargeDisplay"> - {% if flaky > 0 %} - <div> - <div class="floatLeft redBackground" style="width: {{ flakyBar }}px">{{ flaky }}</div> - </div> + + {# Print summary #} + <tr> + {# Project name #} + <td>{{ testset.getProjectName }}</td> + + {# Testset status according to the latest build results #} + {% if testset.getStatus == constant('TestsetRun::RESULT_SUCCESS') %} + {% set resultIcon = 'glyphicon glyphicon-ok green' %} + {% elseif testset.getStatus == constant('TestsetRun::RESULT_FAILURE') %} + {% set resultIcon = 'glyphicon glyphicon-remove red' %} + {% else %} + {% set resultIcon = '' %} + {% endif %} + <td><span class="spaceHorizontal {{ resultIcon }}"></span>{{ testset.getStatus }}</td> + + {# Show failed #} + {% set failed = testset.getTestsetResultCounts.failed %} + {% set passed = testset.getTestsetResultCounts.passed %} + {% set total = passed + failed %} + <td class="leftBorder center">{{ failed }}<span class ="gray"> ({{ total }})</span></td> + {# Show results as bars (scaled to BAR_AREA px) #} + {% set passedBar = ((BAR_AREA/maxCount) * passed)|round(0, 'floor') %} + {% if (passed > 0) and (passedBar == 0) %} + {% set passedBar = 1 %} + {% endif %} + {% set failedBar = ((BAR_AREA/maxCount)*failed)|round(0, 'floor') %} + {% if (failed > 0) and (failedBar == 0) %} + {% set failedBar = 1 %} {% endif %} - </td> - </tr> + <td class="center showInLargeDisplay"> + <div> + <div class="floatLeft redBackground" style="width: {{ failedBar }}px"><small>{{ failed }}</small></div> + <div class="floatLeft greenBackground" style="width: {{ passedBar }}px"><small>{{ passed }}</small></div> + </div> + </td> + + {# Show flaky #} + {% set flaky = testset.getTestsetFlakyCounts.flaky %} + <td class="leftBorder center">{{ flaky }}<span class ="gray"> ({{ total }})</span></td> + {# Show results as bars (scaled to BAR_AREA px) #} + {% set flakyBar = ((BAR_AREA/maxCount)*flaky)|round(0, 'floor') %} + {% if (flaky > 0) and (flakyBar == 0) %} + {% set flakyBar = 1 %} + {% endif %} + <td class="center showInLargeDisplay"> + {% if flaky > 0 %} + <div> + <div class="floatLeft redBackground" style="width: {{ flakyBar }}px"><small>{{ flaky }}</small></div> + </div> + {% endif %} + </td> + </tr> - </tbody> - </table> - </div> {# /table-responsive #} - </div> {# /panel-body #} - </div> {# /panel... #} + </tbody> + </table> + </div> {# /table-responsive #} + </div> {# /panel-body #} + </div> {# /panel... #} - {##### Results in Branches #####} + {##### Results in Branches #####} - <div class="panel panel-primary"> - <div class="panel-heading"> - <h4 class="panel-title bold">Results in Branches</h4> + <div class="panel panel-primary"> + <div class="panel-heading"> + <h4 class="panel-title bold">Results in Branches</h4> + </div> </div> - </div> - - {# Get branches #} - {% set branches = [] %} - {% for run in projectBuilds %} - {% if run.branch not in branches %} - {% set branches = branches|merge([run.branch]) %} - {% endif %} - {% endfor %} - {# Loop all the branches #} - {% for branch in branches %} - - {# Get all build keys #} - {% set buildKey = '' %} - {% set buildKeys = [] %} - {% set dates = [] %} + {# Get branches #} + {% set branches = [] %} {% for run in projectBuilds %} - {% if run.branch == branch %} - {% if buildKey != run.buildKey %} - {% set buildKey = run.buildKey %} - {% set buildKeys = buildKeys|merge([run.buildKey]) %} - {% set dates = dates|merge([run.timestamp]) %} - {% endif %} + {% if run.branch not in branches %} + {% set branches = branches|merge([run.branch]) %} {% endif %} {% endfor %} - {# Check if testset run for this branch #} - {% set testsetBranch = 0 %} - {% for run in testsetRuns if run.getBranchName == branch %} - {% set testsetBranch = 1 %} - {% endfor %} - - {# Show branch if testset run for it #} - {% if testsetBranch %} - <div class="panel panel-info"> - <div class="panel-heading"> - <h4 class="panel-title bold">{{ branch }}</h4> - </div> - <div class="panel-body"> - <div class="table-responsive"> - <table class="table table-striped"> - <thead> - <tr> - <th class="bold rightBorder">{{ testset.getName }}</th> - {% for key, buildKey in buildKeys %} - <th class="center"> - {% if buildKey|length > 6 %} - <span class="clickOnTouch" data-toggle="tooltip" data-placement="top" - title="{{ buildKey }}">{{ buildKey|slice(0, 4) }}... - </span> - {% else %} - {{ buildKey }} - {% endif %} - <br> - <span class="gray"><small>{{ dates[key]|date("m-d") }}</small></span> - </th> - {% endfor %} - </tr> - </thead> - <tbody> - {% set confPrev = '' %} - {% set buildKeyIndexPrinted = -1 %} - {% set buildKeyFound = 0 %} - {% for run in testsetRuns if run.getBranchName == branch %} - - {# New row for each conf #} - {% if confPrev != run.getConfName %} - {# Close previous row #} - {% if confPrev != '' %} - {# Fill empty cells at the end of the row #} - {% for key, buildKey in buildKeys %} - {% if key > buildKeyIndexPrinted %} - <td></td> + {# Loop all the branches #} + {% for branch in branches %} + + {# Get all build keys #} + {% set buildKey = '' %} + {% set buildKeys = [] %} + {% set dates = [] %} + {% for run in projectBuilds %} + {% if run.branch == branch %} + {% if buildKey != run.buildKey %} + {% set buildKey = run.buildKey %} + {% set buildKeys = buildKeys|merge([run.buildKey]) %} + {% set dates = dates|merge([run.timestamp]) %} + {% endif %} + {% endif %} + {% endfor %} + + {# Check if testset run for this branch #} + {% set testsetBranch = 0 %} + {% for run in testsetRuns if run.getBranchName == branch %} + {% set testsetBranch = 1 %} + {% endfor %} + + {# Show branch if testset run for it #} + {% if testsetBranch %} + <div class="panel panel-info"> + <div class="panel-heading"> + <h4 class="panel-title bold">{{ branch }}</h4> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th class="bold rightBorder">{{ testset.getName }}</th> + {% for key, buildKey in buildKeys %} + <th class="center"> + {% if buildKey|length > 6 %} + <span class="clickOnTouch" data-toggle="tooltip" data-placement="top" + title="{{ buildKey }}">{{ buildKey|slice(0, 4) }}... + </span> + {% else %} + {{ buildKey }} {% endif %} - {% endfor %} - </tr> + <br> + <span class="gray"><small>{{ dates[key]|date("m-d") }}</small></span> + </th> + {% endfor %} + </tr> + </thead> + <tbody> + {% set confPrev = '' %} + {% set buildKeyIndexPrinted = -1 %} + {% set buildKeyFound = 0 %} + {% for run in testsetRuns if run.getBranchName == branch %} + + {# New row for each conf #} + {% if confPrev != run.getConfName %} + {# Close previous row #} + {% if confPrev != '' %} + {# Fill empty cells at the end of the row #} + {% for key, buildKey in buildKeys %} + {% if key > buildKeyIndexPrinted %} + <td></td> + {% endif %} + {% endfor %} + </tr> + {% endif %} + <tr> + <td class="rightBorder"><small>{{ run.getConfName }}</small></td> + {% set buildKeyIndexPrinted = -1 %} {% endif %} - <tr> - <td class="rightBorder"><small>{{ run.getConfName }}</small></td> - {% set buildKeyIndexPrinted = -1 %} - {% endif %} - {# Result per build key #} - {% set buildKeyFound = 0 %} - {% for key, buildKey in buildKeys %} - {# Print each column only once (checked based on column index key and buildKeyFound flag) #} - {% if key > buildKeyIndexPrinted and not buildKeyFound %} - {% if buildKey == run.getBuildKey %} - {# Print result #} - {% set flaky = '' %} - {% if run.getResult == constant('testsetRun::RESULT_SUCCESS') %} - {% if run.getRun == 1 %} - {% set resultIcon = 'glyphicon glyphicon-ok green' %} + {# Result per build key #} + {% set buildKeyFound = 0 %} + {% for key, buildKey in buildKeys %} + {# Print each column only once (checked based on column index key and buildKeyFound flag) #} + {% if key > buildKeyIndexPrinted and not buildKeyFound %} + {% if buildKey == run.getBuildKey %} + {# Print result #} + {% set flaky = '' %} + {% if run.getResult == constant('TestsetRun::RESULT_SUCCESS') %} + {% if run.getRun == 1 %} + {% set resultIcon = 'glyphicon glyphicon-ok green' %} + {% else %} + {# Flaky #} + {% set resultIcon = 'glyphicon glyphicon-ok-sign green' %} + {% set flaky = ' (on run ' ~ run.getRun ~ ' as flaky)' %} + {% endif %} + {% elseif run.getResult == constant('TestsetRun::RESULT_FAILURE') %} + {% set resultIcon = 'glyphicon glyphicon-remove red' %} {% else %} - {# Flaky #} - {% set resultIcon = 'glyphicon glyphicon-ok-sign green' %} - {% set flaky = ' (on run ' ~ run.getRun ~ ' as flaky)' %} + {% set resultIcon = '' %} {% endif %} - {% elseif run.getResult == constant('testsetRun::RESULT_FAILURE') %} - {% set resultIcon = 'glyphicon glyphicon-remove red' %} - {% else %} - {% set resultIcon = '' %} - {% endif %} - {% if (run.getDuration / 10) > 60 %} - {% set durationFormatted = ' (00:' ~ (run.getDuration/10)|date("i:s") ~ ')' %} + {% if (run.getDuration / 10) > 60 %} + {% set durationFormatted = ' (00:' ~ (run.getDuration/10)|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> + <tr><th>Build key: </th><td>{{ buildKey }}</td></tr> + <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>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 %} {% else %} - {% set durationFormatted = '' %} + {# Print empty cell #} + <td></td> {% endif %} - <td class="center"> - <span class="spaceHorizontal {{ resultIcon }} clickOnTouch" - data-toggle="tooltip" data-placement="top" data-html="true" - title="<table> - <tr><th>Build key: </th><td>{{ buildKey }}</td></tr> - <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>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 %} - {% else %} - {# Print empty cell #} - <td></td> + {% set buildKeyIndexPrinted = key %} {% endif %} - {% set buildKeyIndexPrinted = key %} + {% endfor %} + {% set confPrev = run.getConfName %} + {% endfor %} + + {# Close last row (also fill empty cells at the end of the row) #} + {% for key, buildKey in buildKeys %} + {% if key > buildKeyIndexPrinted %} + <td></td> {% endif %} {% endfor %} - {% set confPrev = run.getConfName %} - {% endfor %} + </tr> + </tbody> + </table> + </div> {# /table-responsive #} + </div> {# /panel-body #} + </div> {# /panel... #} + {% endif %} {# testsetBranch #} + {% endfor %} - {# Close last row (also fill empty cells at the end of the row) #} - {% for key, buildKey in buildKeys %} - {% if key > buildKeyIndexPrinted %} - <td></td> - {% endif %} - {% endfor %} - </tr> - </tbody> - </table> - </div> {# /table-responsive #} - </div> {# /panel-body #} - </div> {# /panel... #} - {% endif %} {# testsetBranch #} - {% endfor %} + {% else %} {# runsAvailable #} + <div class="alert alert-danger" role="alert"> + No test result data available for testset {{ testset.getName }} in project {{ testset.getProjectName }}! + </div> + {% endif %} </div> {# /col... #} </div> {# /row #} diff --git a/non-puppet/qtmetrics2/templates/testset_project.html b/non-puppet/qtmetrics2/templates/testset_project.html new file mode 100644 index 0000000..a4cf0fb --- /dev/null +++ b/non-puppet/qtmetrics2/templates/testset_project.html @@ -0,0 +1,346 @@ +{# +############################################################################# +## +## 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$ +## +############################################################################# + +/** + * Testset project page + * @version 0.1 + * @since 30-06-2015 + * @author Juha Sippola + */ + +#} + +{% include "header.html" %} + +{# Failed/passed bar area size in px #} +{% set BAR_AREA = 60 %} + +<ol class="breadcrumb"> + {% for link in breadcrumb %} + <li><a href="{{ link.link }}">{{ link.name }}</a></li> + {% endfor %} + <li class="active">{{ project }}</li> +</ol> + +<div class="container-fluid"> + <div class="row"> + + <div class="col-sm-12 col-md-12 main"> + + {# Check if any runs available #} + {% set runsAvailable = 0 %} + {% for run in confBuilds %} + {% set runsAvailable = 1 %} + {% endfor %} + + {##### Title #####} + + <h1 class="page-header"> + {{ project }} + <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>Testset project</strong><br> + <ul> + <li><strong>Latest Status</strong> shows the status based on the results of {{ project }} + testsets in the latest <strong>{{ masterProject }} {{ masterState }}</strong> builds + in any configuration across all branches.</li> + <li><strong>Results in Branches</strong> shows the results of {{ project }} testsets by branch + and their configuration in <strong>{{ masterProject }} {{ masterState }}</strong> builds + ( <button type="button" class="btn btn-xs btn-success"><span class="badge">n</span></button> = all n testsets passed, + <button type="button" class="btn btn-xs btn-danger"><span class="badge">n</span></button> = n testsets failed); + details are available as tooltip on result icon.</li> + </ul> + </div> + </div> + + {% if runsAvailable %} + + {##### Latest Status #####} + + <div class="panel panel-primary"> + <div class="panel-heading"> + <h4 class="panel-title bold">Latest Status</h4> + </div> + + {# Get branches #} + {% set branches = [] %} + {% for run in latestTestsetRuns %} + {% if run.branch not in branches %} + {% set branches = branches|merge([run.branch]) %} + {% endif %} + {% endfor %} + + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th class="rightBorder">testset project</th> + {% for branch in branches %} + <th class="center">{{ branch }}<br> + <small>failed <span class ="gray">(total)</span></small> + </th> + <th class="showInLargeDisplay"></th> + {% endfor %} + </tr> + </thead> + <tbody> + {# Calculate max failed result count for the bar #} + {% set maxCount = 1 %} + {% for run in latestTestsetRuns %} + {% if run.failed > maxCount %} + {% set maxCount = run.failed %} + {% endif %} + {% endfor %} + + <tr> + {# Project name #} + <td class="rightBorder">{{ project }}</td> + + {# Result (by branch) #} + {% for branch in branches %} + {% for run in latestTestsetRuns if project == run.project and branch == run.branch %} + + {# Show results #} + {% set failed = run.failed %} + {% set passed = run.passed %} + {% set total = passed + failed %} + <td class="center">{{ failed }}<span class ="gray"> ({{ total }})</span></td> + + {# Show results as bars (scaled to BAR_AREA px) #} + {% set failedBar = ((BAR_AREA/maxCount) * failed)|round(0, 'floor') %} + {% if (failed > 0) and (failedBar == 0) %} + {% set failedBar = 1 %} + {% endif %} + {% if failed == 0 %} + {% set failed = '' %} + {% endif %} + {% if (passed > 0) and (failed == 0) %} + {% set passedBar = BAR_AREA %} + {% else %} + {% set passed = '' %} + {% endif %} + <td class="center showInLargeDisplay"> + <div> + <div class="floatLeft redBackground" style="width: {{ failedBar }}px">{{ failed }}</div> + <div class="floatLeft greenBackground" style="width: {{ passedBar }}px">{{ passed }}</div> + </div> + </td> + {% else %} + <td></td> + {% endfor %} + {% endfor %} + </tr> + </tbody> + </table> + </div> {# /table-responsive #} + </div> {# /panel-body #} + </div> {# /panel... #} + + {##### Results in Branches #####} + + <div class="panel panel-primary"> + <div class="panel-heading"> + <h4 class="panel-title bold">Results in Branches</h4> + </div> + </div> + + {# Get branches #} + {% set branches = [] %} + {% for run in projectBuilds %} + {% if run.branch not in branches %} + {% set branches = branches|merge([run.branch]) %} + {% endif %} + {% endfor %} + + {# Loop all the branches #} + {% for branch in branches %} + + {# Get all build keys #} + {% set buildKey = '' %} + {% set buildKeys = [] %} + {% set dates = [] %} + {% for run in projectBuilds %} + {% if run.branch == branch %} + {% if buildKey != run.buildKey %} + {% set buildKey = run.buildKey %} + {% set buildKeys = buildKeys|merge([run.buildKey]) %} + {% set dates = dates|merge([run.timestamp]) %} + {% endif %} + {% endif %} + {% endfor %} + + {# Check if testsets run for this branch #} + {% set testsetBranch = 0 %} + {% for run in confBuilds if run.branch == branch %} + {% set testsetBranch = 1 %} + {% endfor %} + + {# Show branch if testsets run for it #} + {% if testsetBranch %} + <div class="panel panel-info"> + <div class="panel-heading"> + <h4 class="panel-title bold">{{ branch }}</h4> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th class="bold rightBorder">{{ project }}</th> + {% for key, buildKey in buildKeys %} + <th class="center"> + {% if buildKey|length > 6 %} + <span class="clickOnTouch" data-toggle="tooltip" data-placement="top" + title="{{ buildKey }}">{{ buildKey|slice(0, 4) }}... + </span> + {% else %} + {{ buildKey }} + {% endif %} + <br> + <span class="gray"><small>{{ dates[key]|date("m-d") }}</small></span> + </th> + {% endfor %} + </tr> + </thead> + <tbody> + {% set confPrev = '' %} + {% set buildKeyIndexPrinted = -1 %} + {% set buildKeyFound = 0 %} + {% for run in confBuilds if run.branch == branch %} + + {# New row for each conf #} + {% if confPrev != run.conf %} + {# Close previous row #} + {% if confPrev != '' %} + {# Fill empty cells at the end of the row #} + {% for key, buildKey in buildKeys %} + {% if key > buildKeyIndexPrinted %} + <td></td> + {% endif %} + {% endfor %} + </tr> + {% endif %} + <tr> + <td class="rightBorder"><small>{{ run.conf }}</small></td> + {% set buildKeyIndexPrinted = -1 %} + {% endif %} + + {# Result per build key #} + {% set buildKeyFound = 0 %} + {% for key, buildKey in buildKeys %} + {# Print each column only once (checked based on column index key and buildKeyFound flag) #} + {% if key > buildKeyIndexPrinted and not buildKeyFound %} + {% if buildKey == run.buildKey %} + {# Print results #} + <td class="center"> + {% if (run.failed + run.ifailed) > 0 %} + <button type="button" class="btn btn-xs btn-danger clickOnTouch" + data-toggle="tooltip" data-placement="top" data-html="true" + title="<table> + <tr><th>Build key: </th><td>{{ buildKey }}</td></tr> + <tr><th>Configuration: </th><td>{{ run.conf }}</td></tr> + <tr><th>Testsets passed: </th><td>{{ run.passed }}</td></tr> + <tr><th>Testsets passed (insignificant): </th><td>{{ run.ipassed }}</td></tr> + <tr><th>Testsets failed: </th><td>{{ run.failed }}</td></tr> + <tr><th>Testsets failed (insignificant): </th><td>{{ run.ifailed }}</td></tr> + </table>"> + <span class="badge">{{ run.failed + run.ifailed }}</span> + </button> + {% elseif (run.passed + run.ipassed) > 0 %} + <button type="button" class="btn btn-xs btn-success clickOnTouch" + data-toggle="tooltip" data-placement="top" data-html="true" + title="<table> + <tr><th>Build key: </th><td>{{ buildKey }}</td></tr> + <tr><th>Configuration: </th><td>{{ run.conf }}</td></tr> + <tr><th>Testsets passed: </th><td>{{ run.passed }}</td></tr> + <tr><th>Testsets passed (insignificant): </th><td>{{ run.ipassed }}</td></tr> + <tr><th>Testsets failed: </th><td>{{ run.failed }}</td></tr> + <tr><th>Testsets failed (insignificant): </th><td>{{ run.ifailed }}</td></tr> + </table>"> + <span class="badge"><small>{{ run.passed + run.ipassed }}</small></span> + </button> + {% endif %} + </td> + {% set buildKeyFound = 1 %} + {% else %} + {# Print empty cell #} + <td></td> + {% endif %} + {% set buildKeyIndexPrinted = key %} + {% endif %} + {% endfor %} + {% set confPrev = run.conf %} + {% endfor %} + + {# Close last row (also fill empty cells at the end of the row) #} + {% for key, buildKey in buildKeys %} + {% if key > buildKeyIndexPrinted %} + <td></td> + {% endif %} + {% endfor %} + </tr> + </tbody> + </table> + </div> {# /table-responsive #} + </div> {# /panel-body #} + </div> {# /panel... #} + {% endif %} {# testsetBranch #} + {% endfor %} + + {% else %} {# runsAvailable #} + <div class="alert alert-danger" role="alert"> + No test result data available for project {{ project }}! + </div> + {% endif %} + + </div> {# /col... #} + </div> {# /row #} + +</div> {# /container-fluid #} + +{% include "footer.html" %} + +{# Local scripts for this page #} +<script src="scripts/tooltip.js"></script> + +{% include "close.html" %} |