summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--non-puppet/qtmetrics2/index.php44
-rw-r--r--non-puppet/qtmetrics2/scripts/ajax.js17
-rw-r--r--non-puppet/qtmetrics2/src/Database.php60
-rw-r--r--non-puppet/qtmetrics2/src/Factory.php26
-rw-r--r--non-puppet/qtmetrics2/src/Testfunction.php145
-rw-r--r--non-puppet/qtmetrics2/src/test/DatabaseTest.php41
-rw-r--r--non-puppet/qtmetrics2/src/test/FactoryTest.php25
-rw-r--r--non-puppet/qtmetrics2/src/test/TestfunctionTest.php104
-rw-r--r--non-puppet/qtmetrics2/styles/qtmetrics.css12
-rw-r--r--non-puppet/qtmetrics2/templates/about.html4
-rw-r--r--non-puppet/qtmetrics2/templates/home.html9
-rw-r--r--non-puppet/qtmetrics2/templates/testfunctions_top.html106
-rw-r--r--non-puppet/qtmetrics2/templates/testfunctions_top_data.html130
13 files changed, 709 insertions, 14 deletions
diff --git a/non-puppet/qtmetrics2/index.php b/non-puppet/qtmetrics2/index.php
index fc4e3fb..76465d4 100644
--- a/non-puppet/qtmetrics2/index.php
+++ b/non-puppet/qtmetrics2/index.php
@@ -34,7 +34,7 @@
/**
* Qt Metrics API
- * @since 17-09-2015
+ * @since 18-09-2015
* @author Juha Sippola
*/
@@ -72,6 +72,7 @@ $app->get('/', function() use($app)
'platformRoute' => $buildProjectPlatformRoute,
'topRoute' => Slim\Slim::getInstance()->urlFor('top'),
'flakyRoute' => Slim\Slim::getInstance()->urlFor('flaky'),
+ 'topTestfunctionsRoute' => Slim\Slim::getInstance()->urlFor('toptestfunctions'),
'masterProject' => $ini['master_build_project'],
'masterState' => $ini['master_build_state'],
'branches' => Factory::db()->getBranches(),
@@ -437,6 +438,47 @@ $app->get('/data/test/flaky', function() use($app)
});
/**
+ * UI route: /test/toptestfunctions (GET)
+ */
+
+$app->get('/test/toptestfunctions', function() use($app)
+{
+ $ini = Factory::conf();
+ $dbStatus = Factory::db()->getDbRefreshStatus();
+ $days = intval($ini['top_failures_last_days']) - 1;
+ $since = Factory::getSinceDate($days);
+ $breadcrumb = array(
+ array('name' => 'home', 'link' => Slim\Slim::getInstance()->urlFor('root'))
+ );
+ $app->render('testfunctions_top.html', array(
+ 'root' => Slim\Slim::getInstance()->urlFor('root'),
+ 'dbStatus' => $dbStatus,
+ 'refreshed' => $dbStatus['refreshed'] . ' (GMT)',
+ 'breadcrumb' => $breadcrumb,
+ 'topN' => $ini['top_failures_n'],
+ 'lastDays' => $ini['top_failures_last_days'],
+ 'sinceDate' => $since,
+ 'masterProject' => $ini['master_build_project'],
+ 'masterState' => $ini['master_build_state']
+ ));
+})->name('toptestfunctions');
+
+$app->get('/data/test/toptestfunctions', function() use($app)
+{
+ $ini = Factory::conf();
+ $days = intval($ini['top_failures_last_days']) - 1;
+ $since = Factory::getSinceDate($days);
+ $app->render('testfunctions_top_data.html', array(
+ 'testsetRoute' => Slim\Slim::getInstance()->urlFor('root') . 'testset',
+ 'lastDays' => $ini['top_failures_last_days'],
+ 'sinceDate' => $since,
+ 'testfunctions' => Factory::createTestfunctions(
+ $ini['master_build_project'],
+ $ini['master_build_state']) // managed as objects
+ ));
+});
+
+/**
* UI route: /testset/:testset/:project (GET)
*/
diff --git a/non-puppet/qtmetrics2/scripts/ajax.js b/non-puppet/qtmetrics2/scripts/ajax.js
index b8be6fa..db12ac5 100644
--- a/non-puppet/qtmetrics2/scripts/ajax.js
+++ b/non-puppet/qtmetrics2/scripts/ajax.js
@@ -34,7 +34,7 @@
/**
* Ajax route calls
- * @since 05-08-2015
+ * @since 18-09-2015
* @author Juha Sippola
*/
@@ -74,7 +74,7 @@ $(function () {
});
}
- // Top failures
+ // Top testset failures
if ($.inArray('testsets_top_data', divs) > -1) {
$.ajax({
url: "data/test/top",
@@ -100,4 +100,17 @@ $(function () {
});
}
+ // Top testfunction failures
+ if ($.inArray('testfunctions_top_data', divs) > -1) {
+ $.ajax({
+ url: "data/test/toptestfunctions",
+ dataType: "html",
+ cache: true
+ })
+ .done(function( html ) {
+ console.log(this.url + " done");
+ $('#testfunctions_top_data').html(html);
+ });
+ }
+
});
diff --git a/non-puppet/qtmetrics2/src/Database.php b/non-puppet/qtmetrics2/src/Database.php
index 675779f..5b76d7e 100644
--- a/non-puppet/qtmetrics2/src/Database.php
+++ b/non-puppet/qtmetrics2/src/Database.php
@@ -34,7 +34,7 @@
/**
* Database class
- * @since 17-09-2015
+ * @since 20-09-2015
* @author Juha Sippola
*/
@@ -757,6 +757,64 @@ class Database {
}
/**
+ * Get counts of all passed, failed and skipped runs by testfunction in specified builds since specified date (list length limited)
+ * Only the testfunctions that have failed since the specified date are listed
+ * @param string $runProject
+ * @param string $runState
+ * @param string $date
+ * @param int $limit
+ * @return array (string name, string testset, string project, int passed, int failed, int skipped)
+ */
+ public function getTestfunctionsResultCounts($runProject, $runState, $date, $limit)
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT
+ testfunction.name AS testfunction,
+ testset.name AS testset,
+ project.name AS project,
+ COUNT(CASE WHEN testfunction_run.result IN ('pass', 'xfail', 'bpass', 'bxfail', 'tr_pass') THEN testfunction_run.result END) AS passed,
+ COUNT(CASE WHEN testfunction_run.result IN ('fail', 'xpass', 'bfail', 'bxpass', 'tr_fail') THEN testfunction_run.result END) AS failed,
+ COUNT(CASE WHEN testfunction_run.result LIKE '%skip' THEN testfunction_run.result END) AS skipped
+ FROM testfunction_run
+ INNER JOIN testfunction ON testfunction_run.testfunction_id = testfunction.id
+ INNER JOIN testset_run ON testfunction_run.testset_run_id = testset_run.id
+ INNER JOIN testset ON testset_run.testset_id = testset.id
+ INNER JOIN project ON testset.project_id = project.id
+ INNER JOIN conf_run ON testset_run.conf_run_id = conf_run.id
+ INNER JOIN project_run ON conf_run.project_run_id = project_run.id
+ INNER JOIN branch ON project_run.branch_id = branch.id
+ INNER JOIN state ON project_run.state_id = state.id
+ WHERE
+ project_run.project_id = (SELECT id FROM project WHERE name = ?) AND
+ project_run.state_id = (SELECT id FROM state WHERE name = ?) AND
+ project_run.timestamp >= ? AND
+ branch.archived = 0
+ GROUP BY testfunction.name, testset.name
+ ORDER BY failed DESC, testfunction.name ASC
+ LIMIT ?;
+ ");
+ $query->bindParam(1, $runProject);
+ $query->bindParam(2, $runState);
+ $query->bindParam(3, $date);
+ $query->bindParam(4, $limit, PDO::PARAM_INT); // int data type must be separately set
+ $query->execute();
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ if ($row['failed'] > 0) { // return only those where failures identified
+ $result[] = array(
+ 'name' => $row['testfunction'],
+ 'testset' => $row['testset'],
+ 'project' => $row['project'],
+ 'passed' => $row['passed'],
+ 'failed' => $row['failed'],
+ 'skipped' => $row['skipped']
+ );
+ }
+ }
+ return $result;
+ }
+
+ /**
* Get project run data by branch
* @param string $runProject
* @param string $runState
diff --git a/non-puppet/qtmetrics2/src/Factory.php b/non-puppet/qtmetrics2/src/Factory.php
index ba3d966..34c8655 100644
--- a/non-puppet/qtmetrics2/src/Factory.php
+++ b/non-puppet/qtmetrics2/src/Factory.php
@@ -34,7 +34,7 @@
/**
* Factory class
- * @since 16-09-2015
+ * @since 18-09-2015
* @author Juha Sippola
*/
@@ -46,6 +46,7 @@ require_once 'Conf.php';
require_once 'ConfRun.php';
require_once 'Testset.php';
require_once 'TestsetRun.php';
+require_once 'Testfunction.php';
require_once 'TestfunctionRun.php';
require_once 'TestrowRun.php';
@@ -279,6 +280,29 @@ class Factory {
}
/**
+ * Create Testfunction objects for those in database
+ * List is limited by date (since) and length, and for specified builds only
+ * @param string $runProject
+ * @param string $runState
+ * @return array Testfunction objects
+ */
+ public static function createTestfunctions($runProject, $runState)
+ {
+ $objects = array();
+ $ini = self::conf();
+ $days = intval($ini['top_failures_last_days']) - 1;
+ $since = self::getSinceDate($days);
+ $limit = intval($ini['top_failures_n']);
+ $dbEntries = self::db()->getTestfunctionsResultCounts($runProject, $runState, $since, $limit);
+ foreach($dbEntries as $entry) {
+ $obj = new Testfunction($entry['name'], $entry['testset'], $entry['project']);
+ $obj->setResultCounts($entry['passed'], $entry['failed'], $entry['skipped']);
+ $objects[] = $obj;
+ }
+ return $objects;
+ }
+
+ /**
* Create ProjectRun objects for those in database
* @param string $runProject
* @param string $runState
diff --git a/non-puppet/qtmetrics2/src/Testfunction.php b/non-puppet/qtmetrics2/src/Testfunction.php
new file mode 100644
index 0000000..696e938
--- /dev/null
+++ b/non-puppet/qtmetrics2/src/Testfunction.php
@@ -0,0 +1,145 @@
+<?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$
+##
+#############################################################################
+
+/**
+ * Test function class
+ * @since 18-09-2015
+ * @author Juha Sippola
+ */
+
+class Testfunction {
+
+ /**
+ * If the testfunction name long, a shorter version of the name can be requested
+ */
+ const SHORT_NAME_LENGTH = 50;
+
+ /**
+ * Testfunction name.
+ * @var string
+ */
+ private $name;
+
+ /**
+ * testset name the testfunction belongs to.
+ * @var string
+ */
+ private $testsetName;
+
+ /**
+ * Project name the testset belongs to.
+ * @var string
+ */
+ private $testsetProjectName;
+
+ /**
+ * Count of testfunction results in the Project builds run since the last n days (all configurations).
+ * @var array (int passed, int failed, int skipped)
+ */
+ private $resultCounts;
+
+ /**
+ * Testfunction constructor.
+ * @param string $name
+ * @param string $testsetName
+ * @param string $testsetProjectName
+ */
+ public function __construct($name, $testsetName, $testsetProjectName)
+ {
+ $this->name = $name;
+ $this->testsetName = $testsetName;
+ $this->testsetProjectName = $testsetProjectName;
+ $this->resultCounts = array('passed' => null, 'failed' => null, 'skipped' => null); // not initially set
+ }
+
+ /**
+ * Get name of the testfunction.
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Get short name of the testfunction.
+ * @return string
+ */
+ public function getShortName()
+ {
+ if (strlen($this->name) > self::SHORT_NAME_LENGTH)
+ return substr($this->name, 0, self::SHORT_NAME_LENGTH - 10) . '...' . substr($this->name, -7);
+ else
+ return $this->name;
+ }
+
+ /**
+ * Get testset name of the testfunction.
+ * @return string
+ */
+ public function getTestsetName()
+ {
+ return $this->testsetName;
+ }
+
+ /**
+ * Get project name of the testset.
+ * @return string
+ */
+ public function getTestsetProjectName()
+ {
+ return $this->testsetProjectName;
+ }
+
+ /**
+ * Get count of testfunction results in latest Project builds (all configurations, specified builds only).
+ * @return array (int passed, int failed, int skipped)
+ */
+ public function getResultCounts()
+ {
+ return $this->resultCounts;
+ }
+
+ /**
+ * Set count of testset results in latest Project builds (all configurations, specified builds only).
+ */
+ public function setResultCounts($passed, $failed, $skipped)
+ {
+ $this->resultCounts = array('passed' => $passed, 'failed' => $failed, 'skipped' => $skipped);
+ return;
+ }
+
+}
+
+?>
diff --git a/non-puppet/qtmetrics2/src/test/DatabaseTest.php b/non-puppet/qtmetrics2/src/test/DatabaseTest.php
index e8725f8..9feb61c 100644
--- a/non-puppet/qtmetrics2/src/test/DatabaseTest.php
+++ b/non-puppet/qtmetrics2/src/test/DatabaseTest.php
@@ -38,7 +38,7 @@ require_once(__DIR__.'/../Factory.php');
* Database unit test class
* Some of the tests require the test data as inserted into database with qtmetrics_insert.sql
* @example To run (in qtmetrics root directory): php <path-to-phpunit>/phpunit.phar ./src/test
- * @since 17-09-2015
+ * @since 20-09-2015
* @author Juha Sippola
*/
@@ -600,6 +600,45 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
}
/**
+ * Test getTestfunctionsResultCounts
+ * @dataProvider testGetTestfunctionsResultCountsData
+ */
+ public function testGetTestfunctionsResultCounts($runProject, $runState, $date, $limit, $exp_testfunction, $exp_excluded_testfunction, $exp_testfunction_count_min, $exp_failed_min)
+ {
+ $testfunctions = array();
+ $failed = 0;
+ $db = Factory::db();
+ $result = $db->getTestfunctionsResultCounts($runProject, $runState, $date, $limit);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('name', $row);
+ $this->assertArrayHasKey('testset', $row);
+ $this->assertArrayHasKey('project', $row);
+ $this->assertArrayHasKey('passed', $row);
+ $this->assertArrayHasKey('failed', $row);
+ $this->assertArrayHasKey('skipped', $row);
+ $testfunctions[] = $row['name'];
+ $failed += $row['failed'];
+ }
+ $this->assertGreaterThanOrEqual($exp_testfunction_count_min, count($testfunctions));
+ if ($exp_testfunction_count_min > 0) {
+ $this->assertNotEmpty($result);
+ $this->assertContains($exp_testfunction, $testfunctions);
+ $this->assertNotContains($exp_excluded_testfunction, $testfunctions);
+ $this->assertGreaterThanOrEqual($exp_failed_min, $failed);
+ }
+ }
+ public function testGetTestfunctionsResultCountsData()
+ {
+ return array(
+ array('Qt5', 'state', '2013-05-01', 10, 'exactMatch', 'commandSequence', 2, 1), // in test data exactMatch has failures and commandSequence doesn't
+ array('Qt5', 'state', '2013-05-01', 1, 'defaultFamily', 'commandSequence', 1, 1), // defaultFamily is the first
+ array('Qt5', 'state', '2013-05-28', 10, 'exactMatch', 'commandSequence', 2, 1),
+ array('Qt5', 'state', '2013-05-29', 10, '', '', 0, 0),
+ array('Qt5', 'state', '2999-05-29', 10, '', '', 0, 0)
+ );
+ }
+
+ /**
* Test getProjectBuildsByBranch
* @dataProvider testGetProjectBuildsByBranchData
*/
diff --git a/non-puppet/qtmetrics2/src/test/FactoryTest.php b/non-puppet/qtmetrics2/src/test/FactoryTest.php
index 0987cb2..a84315b 100644
--- a/non-puppet/qtmetrics2/src/test/FactoryTest.php
+++ b/non-puppet/qtmetrics2/src/test/FactoryTest.php
@@ -37,7 +37,7 @@ require_once(__DIR__.'/../Factory.php');
/**
* Factory unit test class
* @example To run (in qtmetrics root directory): php <path-to-phpunit>/phpunit.phar ./src/test
- * @since 09-09-2015
+ * @since 18-09-2015
* @author Juha Sippola
*/
@@ -282,6 +282,29 @@ class FactoryTest extends PHPUnit_Framework_TestCase
}
/**
+ * Test createTestfunctions
+ * @dataProvider testCreateTestfunctionsData
+ */
+ public function testCreateTestfunctions($runProject, $runState)
+ {
+ $testfunctions = Factory::createTestfunctions($runProject, $runState);
+ foreach($testfunctions as $testfunction) {
+ $this->assertTrue($testfunction instanceof Testfunction);
+ $result = $testfunction->getResultCounts();
+ $this->assertNotNull($result);
+ $this->assertArrayHasKey('passed', $result);
+ $this->assertArrayHasKey('failed', $result);
+ $this->assertArrayHasKey('skipped', $result);
+ }
+ }
+ public function testCreateTestfunctionsData()
+ {
+ return array(
+ array('Qt5', 'state')
+ );
+ }
+
+ /**
* Test createProjectRuns
* @dataProvider testCreateProjectRunsData
*/
diff --git a/non-puppet/qtmetrics2/src/test/TestfunctionTest.php b/non-puppet/qtmetrics2/src/test/TestfunctionTest.php
new file mode 100644
index 0000000..2fdbb05
--- /dev/null
+++ b/non-puppet/qtmetrics2/src/test/TestfunctionTest.php
@@ -0,0 +1,104 @@
+<?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$
+##
+#############################################################################
+
+require_once(__DIR__.'/../Factory.php');
+
+/**
+ * Testfunction unit test class
+ * @example To run (in qtmetrics root directory): php <path-to-phpunit>/phpunit.phar ./src/test
+ * @since 18-09-2015
+ * @author Juha Sippola
+ */
+
+class TestfunctionTest extends PHPUnit_Framework_TestCase
+{
+
+ /**
+ * Test getName, getShortName, getTestsetName, getTestsetProjectName
+ * @dataProvider testGetNameData
+ */
+ public function testGetName($name, $shortName, $testset, $project)
+ {
+ $testfunction = new Testfunction($name, $testset, $project);
+ $this->assertEquals($name, $testfunction->getName());
+ $this->assertEquals($shortName, $testfunction->getShortName());
+ $this->assertEquals($testset, $testfunction->getTestsetName());
+ $this->assertEquals($project, $testfunction->getTestsetProjectName());
+ }
+ public function testGetNameData()
+ {
+ return array(
+ array('cleanupTestCase', 'cleanupTestCase', 'tst_qftp', 'QtBase'),
+ array('my_testfunction', 'my_testfunction', 'my_testset', 'my_project'),
+ array('my_long_testfunction_name_that_has_over_50_letters_in_it', 'my_long_testfunction_name_that_has_over_...s_in_it', 'my_testset', 'my_project')
+ );
+ }
+
+ /**
+ * Test setResultCounts and getResultCounts
+ * @dataProvider testGetResultCountsData
+ */
+ public function testGetTestsetResultCounts($name, $testset, $project, $passed, $failed, $skipped)
+ {
+ $testfunction = new Testfunction($name, $testset, $project);
+ // Counts not set
+ $result = $testfunction->getResultCounts();
+ $this->assertArrayHasKey('passed', $result);
+ $this->assertArrayHasKey('failed', $result);
+ $this->assertArrayHasKey('skipped', $result);
+ $this->assertNull($result['passed']);
+ $this->assertNull($result['failed']);
+ $this->assertNull($result['skipped']);
+ // Counts set
+ $testfunction->setResultCounts($passed, $failed, $skipped);
+ $result = $testfunction->getResultCounts();
+ $this->assertArrayHasKey('passed', $result);
+ $this->assertArrayHasKey('failed', $result);
+ $this->assertArrayHasKey('skipped', $result);
+ $this->assertEquals($passed, $result['passed']);
+ $this->assertEquals($failed, $result['failed']);
+ $this->assertEquals($skipped, $result['skipped']);
+ }
+ public function testGetResultCountsData()
+ {
+ return array(
+ array('cleanupTestCase', 'tst_qftp', 'QtBase', 1, 2, 3),
+ array('cleanupTestCase', 'tst_qftp', 'Qt5', 123456, 654321, 111222),
+ array('my_testfunction', 'my_testfunction', 'my_project', 7, 14, 7)
+ );
+ }
+
+}
+
+?>
diff --git a/non-puppet/qtmetrics2/styles/qtmetrics.css b/non-puppet/qtmetrics2/styles/qtmetrics.css
index 72846dc..fa2c3c8 100644
--- a/non-puppet/qtmetrics2/styles/qtmetrics.css
+++ b/non-puppet/qtmetrics2/styles/qtmetrics.css
@@ -35,7 +35,7 @@
/**
* Qt Metrics style sheet
- * @since 06-08-2015
+ * @since 20-09-2015
* @author Juha Sippola
*/
@@ -103,6 +103,13 @@
}
/*
+ * Make space horizontally inside an element
+ */
+.paddingHorizontal {
+ padding: 0 5px 0 5px;
+}
+
+/*
* Animation shown while loading a page from a link
*/
#link_loading {
@@ -182,6 +189,9 @@ thead {
.blueBackground {
background-color: #337AB7;
}
+.grayBackground {
+ background-color: lightgray;
+}
/*
* Placing and sizing
diff --git a/non-puppet/qtmetrics2/templates/about.html b/non-puppet/qtmetrics2/templates/about.html
index 67549d8..bef8584 100644
--- a/non-puppet/qtmetrics2/templates/about.html
+++ b/non-puppet/qtmetrics2/templates/about.html
@@ -34,7 +34,7 @@
/**
* About window content
- * @since 17-09-2015
+ * @since 20-09-2015
* @author Juha Sippola
*/
@@ -52,4 +52,4 @@ and the global Qt developer community are the target audience. For detailed desc
<p>See the <strong><a href="https://wiki.qt.io/Qt_Metrics_2_Backlog" target="_blank">backlog</a></strong>
for development items currently identified or in progress.</p>
-<p><small>Version 0.29 (17-Sep-2015)</small></p>
+<p><small>Version 0.30 (20-Sep-2015)</small></p>
diff --git a/non-puppet/qtmetrics2/templates/home.html b/non-puppet/qtmetrics2/templates/home.html
index 00e3828..24119e0 100644
--- a/non-puppet/qtmetrics2/templates/home.html
+++ b/non-puppet/qtmetrics2/templates/home.html
@@ -34,7 +34,7 @@
/**
* Home page
- * @since 03-08-2015
+ * @since 18-09-2015
* @author Juha Sippola
*/
@@ -127,10 +127,11 @@
</form>
</div>
<br>
-<p>See testset lists:</p>
+<p>See top failure/flaky lists:</p>
<div>
-<a class="btn btn-primary btn-xs" href="{{ topRoute }}" role="button">top failures</a>
-<a class="btn btn-primary btn-xs" href="{{ flakyRoute }}" role="button">flaky testsets</a>
+<a class="btn btn-primary btn-xs" href="{{ topRoute }}" role="button">testsets</a>
+<a class="btn btn-primary btn-xs" href="{{ flakyRoute }}" role="button">flaky</a>
+<a class="btn btn-primary btn-xs" href="{{ topTestfunctionsRoute }}" role="button">test functions</a>
</div>
</div> {# .col-md... #}
diff --git a/non-puppet/qtmetrics2/templates/testfunctions_top.html b/non-puppet/qtmetrics2/templates/testfunctions_top.html
new file mode 100644
index 0000000..f82d3a6
--- /dev/null
+++ b/non-puppet/qtmetrics2/templates/testfunctions_top.html
@@ -0,0 +1,106 @@
+{#
+#############################################################################
+##
+## Copyright (C) 2015 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Quality Assurance module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:LGPL21$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see http://www.qt.io/terms-conditions. For further
+## information use the contact form at http://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 2.1 or version 3 as published by the Free
+## Software Foundation and appearing in the file LICENSE.LGPLv21 and
+## LICENSE.LGPLv3 included in the packaging of this file. Please review the
+## following information to ensure the GNU Lesser General Public License
+## requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+##
+## As a special exception, The Qt Company gives you certain additional
+## rights. These rights are described in The Qt Company LGPL Exception
+## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+/**
+ * Top failures (testfunctions) page
+ * @since 18-09-2015
+ * @author Juha Sippola
+ */
+
+#}
+
+{% include "header.html" %}
+
+<ol class="breadcrumb">
+{% for link in breadcrumb %}
+<li><a href="{{ link.link }}">{{ link.name }}</a></li>
+{% endfor %}
+<li class="active">top test function failures</li>
+</ol>
+
+<div class="container-fluid">
+<div class="row">
+
+<div class="col-sm-12 col-md-12 main">
+
+{##### Title #####}
+
+<h1 class="page-header">
+Top {{ topN }} Test Function Failures
+<button type="button" class="btn btn-xs btn-info" data-toggle="collapse" data-target="#info" aria-expanded="false" aria-controls="info">
+<span class="glyphicon glyphicon-info-sign"></span>
+</button>
+<small>{{ refreshed }}</small>
+</h1>
+
+{##### Info well #####}
+
+<div class="collapse" id="info">
+<div class="well infoWell">
+<span class="glyphicon glyphicon-info-sign"></span> <strong>Top Test Function Failures</strong><br>
+<ul>
+<li>Lists test functions by number of <strong>{{ masterProject }} {{ masterState }}</strong>
+builds where it failed during the last {{ lastDays }} days.</li>
+</ul>
+</div>
+</div>
+
+{##### Top list #####}
+
+<div id="testfunctions_top_data">
+<div class="panel panel-primary">
+<div class="panel-heading">
+<h4 class="panel-title bold">Last {{ lastDays }} days <small>(since {{ sinceDate }})</small></h4>
+</div>
+</div>
+<div class="progress data_loading">
+<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">
+</div>
+</div>
+<div class="alert alert-warning" role="alert">
+<span class="glyphicon glyphicon-time"></span> <strong>Please wait:</strong> Extracting the data will be ready in less than a minute!
+</div>
+</div> {# testfunctions_top_data #}
+
+</div> {# .col... #}
+</div> {# .row #}
+</div> {# .container-fluid #}
+
+{% include "footer.html" %}
+
+{# Local scripts for this page #}
+<script src="scripts/ajax.js"></script>
+<script src="scripts/tooltip.js"></script>
+
+{% include "close.html" %}
diff --git a/non-puppet/qtmetrics2/templates/testfunctions_top_data.html b/non-puppet/qtmetrics2/templates/testfunctions_top_data.html
new file mode 100644
index 0000000..647c4ab
--- /dev/null
+++ b/non-puppet/qtmetrics2/templates/testfunctions_top_data.html
@@ -0,0 +1,130 @@
+{#
+#############################################################################
+##
+## Copyright (C) 2015 The Qt Company Ltd.
+## Contact: http://www.qt.io/licensing/
+##
+## This file is part of the Quality Assurance module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:LGPL21$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see http://www.qt.io/terms-conditions. For further
+## information use the contact form at http://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 2.1 or version 3 as published by the Free
+## Software Foundation and appearing in the file LICENSE.LGPLv21 and
+## LICENSE.LGPLv3 included in the packaging of this file. Please review the
+## following information to ensure the GNU Lesser General Public License
+## requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+##
+## As a special exception, The Qt Company gives you certain additional
+## rights. These rights are described in The Qt Company LGPL Exception
+## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+/**
+ * Top failures (testfunctions) data
+ * @since 20-09-2015
+ * @author Juha Sippola
+ */
+
+#}
+
+{# Failed/passed bar area size in px #}
+{% set BAR_AREA = 120 %}
+
+{# testfunctions as Testfunction objects
+/**
+ * @var Testfunction[] testfunctions
+ */
+#}
+
+{##### Top list #####}
+
+<div class="panel panel-primary">
+<div class="panel-heading">
+<h4 class="panel-title bold">Last {{ lastDays }} days <small>(since {{ sinceDate }})</small></h4>
+</div>
+<div class="panel-body">
+<div class="table-responsive">
+<table class="table table-striped">
+<thead>
+<tr>
+<th>test function</th>
+<th>testset</th>
+<th class="showInLargeDisplay">project</th>
+<th class="leftBorder center">fail <span class ="gray">(total)</span></th>
+<th class="showInLargeDisplay"><small>
+<span class="redBackground paddingHorizontal">fail</span><span class="greenBackground paddingHorizontal">pass</span><span class="grayBackground paddingHorizontal">skip</span>
+</small></th>
+</tr>
+</thead>
+<tbody>
+{# Calculate max result count for the bar #}
+{% set maxCount = 1 %}
+{% for testfunction in testfunctions %}
+{% if (testfunction.getResultCounts.passed + testfunction.getResultCounts.failed + testfunction.getResultCounts.skipped) > maxCount %}
+{% set maxCount = testfunction.getResultCounts.passed + testfunction.getResultCounts.failed + testfunction.getResultCounts.skipped %}
+{% endif %}
+{% endfor %}
+
+{# Print testfunctions #}
+{% for testfunction in testfunctions %}
+<tr>
+{# Testfunction name #}
+{% if testfunction.getName|length > constant('Testfunction::SHORT_NAME_LENGTH') %}
+<td><span class="clickOnTouch" data-toggle="tooltip" data-placement="top" title="{{ testfunction.getName }}">{{ testfunction.getShortName }}</span></td>
+{% else %}
+<td>{{ testfunction.getName }}</td>
+{% endif %}
+
+{# Testset name #}
+<td><a href="{{ testsetRoute }}/{{ testfunction.getTestsetName|url_encode }}/{{ testfunction.getTestsetProjectName|url_encode }}">{{ testfunction.getTestsetName }}</a></td>
+
+{# Project name #}
+<td class="showInLargeDisplay">{{ testfunction.getTestsetProjectName }}</td>
+
+{# Show results as numbers #}
+{% set failed = testfunction.getResultCounts.failed %}
+{% set passed = testfunction.getResultCounts.passed %}
+{% set skipped = testfunction.getResultCounts.skipped %}
+{% set total = passed + failed + skipped %}
+<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 %}
+{% set skippedBar = ((BAR_AREA/maxCount) * skipped)|round(0, 'floor') %}
+{% if (skipped > 0) and (skippedBar == 0) %}
+{% set skippedBar = 1 %}
+{% endif %}
+<td class="center showInLargeDisplay">
+<div>
+<div class="floatLeft redBackground" style="width: {{ failedBar }}px">&nbsp;</div>
+<div class="floatLeft greenBackground" style="width: {{ passedBar }}px">&nbsp;</div>
+<div class="floatLeft grayBackground" style="width: {{ skippedBar }}px">&nbsp;</div>
+</div>
+</td>
+</tr>
+{% endfor %}{# testfunction #}
+</tbody>
+</table>
+</div> {# .table-responsive #}
+</div> {# .panel-body #}
+</div> {# .panel... #}