summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuha Sippola <juhasippola@outlook.com>2015-09-21 14:46:07 +0300
committerTony Sarajärvi <tony.sarajarvi@theqtcompany.com>2015-09-23 09:37:22 +0000
commit3f74a568f9a7149cf32e71c79efe727a8684e2e5 (patch)
tree53ef302f3dc4d1acd129125a7a644255af07234b
parentc11438479b5ae3da4ba7d46e5d88c3c000028b59 (diff)
Qt Metrics 2 (v0.20): Admin page
New pages to show table sizes, to list and remove selected branches, and to list and remove build data for selected date. Pages are authenticated with HTTP basic authentication (credentials in ini file). Change-Id: I52b8bb8b1dc6882156f3fd97fceb256679732a8d Reviewed-by: Tony Sarajärvi <tony.sarajarvi@theqtcompany.com>
-rw-r--r--non-puppet/qtmetrics2/index.php114
-rw-r--r--non-puppet/qtmetrics2/scripts/admin.js90
-rw-r--r--non-puppet/qtmetrics2/src/DatabaseAdmin.php468
-rw-r--r--non-puppet/qtmetrics2/src/Factory.php23
-rw-r--r--non-puppet/qtmetrics2/src/HttpBasicAuth.php109
-rw-r--r--non-puppet/qtmetrics2/src/HttpBasicAuthRoute.php69
-rw-r--r--non-puppet/qtmetrics2/src/test/DatabaseAdminTest.php516
-rw-r--r--non-puppet/qtmetrics2/src/test/FactoryTest.php12
-rw-r--r--non-puppet/qtmetrics2/styles/qtmetrics.css10
-rw-r--r--non-puppet/qtmetrics2/templates/about.html4
-rw-r--r--non-puppet/qtmetrics2/templates/admin.html137
-rw-r--r--non-puppet/qtmetrics2/templates/admin_branches.html167
-rw-r--r--non-puppet/qtmetrics2/templates/admin_data.html202
13 files changed, 1911 insertions, 10 deletions
diff --git a/non-puppet/qtmetrics2/index.php b/non-puppet/qtmetrics2/index.php
index 104a819..a7d0290 100644
--- a/non-puppet/qtmetrics2/index.php
+++ b/non-puppet/qtmetrics2/index.php
@@ -34,15 +34,18 @@
/**
* Qt Metrics API
- * @since 05-08-2015
+ * @since 10-08-2015
* @author Juha Sippola
*/
-require_once 'src/Factory.php';
require_once 'lib/Slim/Slim/Slim.php';
require_once 'lib/Slim/Slim/View.php';
+require_once 'lib/Slim/Slim/Middleware.php';
require_once 'lib/Slim/Slim/Views/Twig.php';
require_once 'lib/Twig/lib/Twig/Autoloader.php';
+require_once 'src/Factory.php';
+require_once 'src/HttpBasicAuth.php';
+require_once 'src/HttpBasicAuthRoute.php';
\Slim\Slim::registerAutoloader();
Twig_Autoloader::register();
@@ -483,6 +486,113 @@ $app->get('/sitemap', function() use($app)
));
})->name('sitemap');
+/**
+ * UI route: /admin (GET) - authenticated
+ */
+
+$app->add(new HttpBasicAuthRoute('Protected Area', 'admin'));
+$app->get('/admin', function() use($app)
+{
+ $breadcrumb = array(
+ array('name' => 'home', 'link' => Slim\Slim::getInstance()->urlFor('root'))
+ );
+ $app->render('admin.html', array(
+ 'root' => Slim\Slim::getInstance()->urlFor('root'),
+ 'breadcrumb' => $breadcrumb,
+ 'adminRoute' => Slim\Slim::getInstance()->urlFor('admin'),
+ 'adminBranchesRoute' => Slim\Slim::getInstance()->urlFor('admin_branches'),
+ 'adminDataRoute' => Slim\Slim::getInstance()->urlFor('admin_data'),
+ 'tables' => Factory::dbAdmin()->getTablesStatistics()
+ ));
+})->name('admin');
+
+/**
+ * UI route: /admin/branches (GET) - authenticated
+ */
+
+$app->add(new HttpBasicAuthRoute('Protected Area', 'admin/branches'));
+$app->get('/admin/branches', function() use($app)
+{
+ $breadcrumb = array(
+ array('name' => 'home', 'link' => Slim\Slim::getInstance()->urlFor('root')),
+ array('name' => 'admin', 'link' => Slim\Slim::getInstance()->urlFor('admin'))
+ );
+ $app->render('admin_branches.html', array(
+ 'root' => Slim\Slim::getInstance()->urlFor('root'),
+ 'breadcrumb' => $breadcrumb,
+ 'adminRoute' => Slim\Slim::getInstance()->urlFor('admin'),
+ 'adminBranchesRoute' => Slim\Slim::getInstance()->urlFor('admin_branches'),
+ 'adminDataRoute' => Slim\Slim::getInstance()->urlFor('admin_data'),
+ 'branches' => Factory::dbAdmin()->getBranchesStatistics()
+ ));
+})->name('admin_branches');
+
+/**
+ * UI route: /admin/data (GET) - authenticated
+ */
+
+$app->add(new HttpBasicAuthRoute('Protected Area', 'admin/data'));
+$app->get('/admin/data', function() use($app)
+{
+ $breadcrumb = array(
+ array('name' => 'home', 'link' => Slim\Slim::getInstance()->urlFor('root')),
+ array('name' => 'admin', 'link' => Slim\Slim::getInstance()->urlFor('admin'))
+ );
+ $app->render('admin_data.html', array(
+ 'root' => Slim\Slim::getInstance()->urlFor('root'),
+ 'breadcrumb' => $breadcrumb,
+ 'adminRoute' => Slim\Slim::getInstance()->urlFor('admin'),
+ 'adminBranchesRoute' => Slim\Slim::getInstance()->urlFor('admin_branches'),
+ 'adminDataRoute' => Slim\Slim::getInstance()->urlFor('admin_data'),
+ 'projectRuns' => Factory::dbAdmin()->getProjectRunsStatistics()
+ ));
+})->name('admin_data');
+
+/**
+ * API route: /api/branch (DELETE) - authenticated
+ */
+
+$app->add(new HttpBasicAuthRoute('Protected Area', 'api/branch'));
+$app->delete('/api/branch/:branch', function($branch) use($app)
+{
+ $branch = strip_tags($branch);
+ $branches = array();
+ $query = Factory::db()->getBranches();
+ foreach($query as $item) {
+ $branches[] = $item['name'];
+ }
+ if (in_array($branch, $branches)) {
+ $result = Factory::dbAdmin()->deleteBranch($branch);
+ if ($result)
+ $app->response()->status(200);
+ else
+ $app->response()->status(404);
+ } else {
+ $app->response()->status(404);
+ }
+})->name('delete_branch');
+
+/**
+ * API route: /api/data (DELETE) - authenticated
+ */
+
+$app->add(new HttpBasicAuthRoute('Protected Area', 'api/data'));
+$app->delete('/api/data/:state/:date', function($state, $date) use($app)
+{
+ $state = strip_tags($state);
+ $date = strip_tags($date);
+ $result = Factory::dbAdmin()->deleteRunsData($state, $date);
+ if ($result)
+ $app->response()->status(200);
+ else
+ $app->response()->status(404);
+})->name('delete_data');
+
+
+/**
+ * Start Slim
+ */
+
$app->run();
diff --git a/non-puppet/qtmetrics2/scripts/admin.js b/non-puppet/qtmetrics2/scripts/admin.js
new file mode 100644
index 0000000..090ae9c
--- /dev/null
+++ b/non-puppet/qtmetrics2/scripts/admin.js
@@ -0,0 +1,90 @@
+/*
+#############################################################################
+##
+## 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$
+##
+#############################################################################
+
+/**
+ * Admin functions
+ * @since 19-08-2015
+ * @author Juha Sippola
+ */
+
+// Catch action confirmation from the modal
+$('.modal .modal-footer button').on('click', function (e) {
+ var buttonDiv;
+ var $target = $(e.target);
+ $(this).closest('.modal').on('hidden.bs.modal', function (e) {
+
+ // Remove branch
+ if ($target[0].id.search("confirm_branch_remove") === 0) {
+ var branchTag = $target[0].id.substring($target[0].id.lastIndexOf('_') + 1);
+ var branchName = $target[0].name;
+ buttonDiv = '#' + branchTag + 'Button';
+ $(buttonDiv).html("Removing...");
+ // Send delete request and handle the response
+ $.ajax({
+ url: "api/branch/" + branchName,
+ type: 'DELETE',
+ success: function(result) {
+ console.log(this.type + ": " + this.url + " done");
+ $(buttonDiv).html("Removed");
+ },
+ error: function (request, status, error) {
+ console.log(this.type + ": " + this.url + " error (" + error + ")");
+ $(buttonDiv).html("Error!");
+ }
+ });
+ }
+
+ // Remove data
+ if ($target[0].id.search("confirm_data_remove") === 0) {
+ var dataDate = $target[0].id.substring($target[0].id.lastIndexOf('_') + 1);
+ var dataState = $target[0].name;
+ buttonDiv = '#' + dataState + '-' + dataDate + 'Button';
+ $(buttonDiv).html("Removing...");
+ // Send delete request and handle the response
+ $.ajax({
+ url: "api/data/" + dataState + "/" + dataDate,
+ type: 'DELETE',
+ success: function(result) {
+ console.log(this.type + ": " + this.url + " done");
+ $(buttonDiv).html("Removed");
+ },
+ error: function (request, status, error) {
+ console.log(this.type + ": " + this.url + " error (" + error + ")");
+ $(buttonDiv).html("Error!");
+ }
+ });
+ }
+
+ });
+});
diff --git a/non-puppet/qtmetrics2/src/DatabaseAdmin.php b/non-puppet/qtmetrics2/src/DatabaseAdmin.php
new file mode 100644
index 0000000..a5965ad
--- /dev/null
+++ b/non-puppet/qtmetrics2/src/DatabaseAdmin.php
@@ -0,0 +1,468 @@
+<?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$
+##
+#############################################################################
+
+/**
+ * DatabaseAdmin class
+ * @since 17-08-2015
+ * @author Juha Sippola
+ */
+
+class DatabaseAdmin {
+
+ /**
+ * Database instance
+ * @var PDO
+ */
+ private $db;
+
+ /**
+ * Database constructor
+ */
+ public function __construct()
+ {
+ $ini = Factory::conf();
+ $this->db = new PDO(
+ $ini['dsn'],
+ $ini['username_admin'],
+ $ini['password_admin']
+ );
+ }
+
+ /**
+ * Get list of tables with statistics
+ * @return array (string name, int rowCount)
+ */
+ public function getTablesStatistics()
+ {
+ $result = array();
+ // Tables to check (listed manually because database may contain additional tables)
+ $tables = array();
+ $tables[] = 'branch';
+ $tables[] = 'compiler';
+ $tables[] = 'conf';
+ $tables[] = 'conf_run';
+ $tables[] = 'db_status';
+ $tables[] = 'phase';
+ $tables[] = 'phase_run';
+ $tables[] = 'platform';
+ $tables[] = 'project';
+ $tables[] = 'project_run';
+ $tables[] = 'state';
+ $tables[] = 'testfunction';
+ $tables[] = 'testfunction_run';
+ $tables[] = 'testrow';
+ $tables[] = 'testrow_run';
+ $tables[] = 'testset';
+ $tables[] = 'testset_run';
+ // Row counts
+ foreach ($tables as $table) {
+ $query = $this->db->prepare("SELECT COUNT(*) AS rowCount FROM $table");
+ $query->execute(array());
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'name' => $table,
+ 'rowCount' => $row['rowCount']
+ );
+ }
+ }
+ // Sort
+ $count = array();
+ foreach ($result as $key => $row)
+ {
+ $count[$key] = $row['rowCount'];
+ }
+ array_multisort($count, SORT_DESC, $result);
+ return $result;
+ }
+
+ /**
+ * Get list of branches with statistics
+ * @return array (string name, int runCount, timestamp latestRun)
+ */
+ public function getBranchesStatistics()
+ {
+ $result = array();
+ $branches = Factory::db()->getBranches();
+ foreach ($branches as $branch) {
+ $query = $this->db->prepare("
+ SELECT
+ COUNT(*) AS runCount,
+ MAX(timestamp) AS latestRun
+ FROM project_run
+ WHERE
+ branch_id = (SELECT id FROM branch WHERE name = ?);
+ ");
+ $query->execute(array(
+ $branch['name']
+ ));
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'name' => $branch['name'],
+ 'runCount' => $row['runCount'],
+ 'latestRun' => $row['latestRun']
+ );
+ }
+ }
+ // Sort
+ $date = array();
+ foreach ($result as $key => $row)
+ {
+ $date[$key] = $row['latestRun'];
+ }
+ array_multisort($date, SORT_DESC, $result);
+ return $result;
+ }
+
+ /**
+ * Get project_run statistics
+ * @return array (string state, int year, int month, int day, int runCount)
+ */
+ public function getProjectRunsStatistics()
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT
+ state.name as state,
+ Year(timestamp) AS year,
+ Month(timestamp) AS month,
+ Day(timestamp) AS day,
+ Count(*) as runCount
+ FROM project_run
+ INNER JOIN state ON project_run.state_id = state.id
+ GROUP BY state, year, month, day
+ ORDER BY state, year DESC, month DESC, day DESC;
+ ");
+ $query->execute(array());
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'state' => $row['state'],
+ 'year' => $row['year'],
+ 'month' => $row['month'],
+ 'day' => $row['day'],
+ 'runCount' => $row['runCount']
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Get project_runs for branch
+ * @param string $branch
+ * @return array (int id)
+ */
+ public function getProjectRunIdsBranch($branch)
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT id FROM project_run WHERE branch_id = (SELECT id FROM branch WHERE name = ?);
+ ");
+ $query->execute(array(
+ $branch
+ ));
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'id' => $row['id']
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Get project_runs for state on a date
+ * @param string $month
+ * @return array (int id)
+ */
+ public function getProjectRunIdsDate($state, $date)
+ {
+ $result = array();
+ $year = substr($date, 0, strpos($date, '-'));
+ $month = substr($date, strpos($date, '-') + 1);
+ $day = substr($month, strpos($month, '-') + 1);
+ $month = substr($month, 0, strpos($month, '-'));
+ $query = $this->db->prepare("
+ SELECT project_run.id
+ FROM project_run INNER JOIN state ON project_run.state_id = state.id
+ WHERE state.name = ? AND Year(timestamp) = ? AND Month(timestamp) = ? AND Day(timestamp) = ?;
+ ");
+ $query->execute(array(
+ $state,
+ $year,
+ $month,
+ $day
+ ));
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'id' => $row['id']
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Get conf_runs for project_run
+ * @param int $projectRunId
+ * @return array (int id)
+ */
+ public function getConfRunIds($projectRunId)
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT id FROM conf_run WHERE project_run_id = ?;
+ ");
+ $query->bindParam(1, $projectRunId, PDO::PARAM_INT);
+ $query->execute();
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'id' => $row['id']
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Get testset_runs for conf_run
+ * @param int $confRunId
+ * @return array (int id)
+ */
+ public function getTestsetRunIds($confRunId)
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT id FROM testset_run WHERE conf_run_id = ?;
+ ");
+ $query->bindParam(1, $confRunId, PDO::PARAM_INT);
+ $query->execute();
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'id' => $row['id']
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Get testfunction_runs for testset_run
+ * @param int $testsetRunId
+ * @return array (int id)
+ */
+ public function getTestfunctionRunIds($testsetRunId)
+ {
+ $result = array();
+ $query = $this->db->prepare("
+ SELECT id FROM testfunction_run WHERE testset_run_id = ?;
+ ");
+ $query->bindParam(1, $testsetRunId, PDO::PARAM_INT);
+ $query->execute();
+ while($row = $query->fetch(PDO::FETCH_ASSOC)) {
+ $result[] = array(
+ 'id' => $row['id']
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Delete project_run
+ * @param int $projectRunId
+ * @return bool
+ */
+ public function deleteProjectRun($projectRunId)
+ {
+ $query = $this->db->prepare("
+ DELETE FROM project_run WHERE id = ?;
+ ");
+ $query->bindParam(1, $projectRunId, PDO::PARAM_INT);
+ $result = $query->execute();
+ return $result;
+ }
+
+ /**
+ * Delete conf_runs for project_run
+ * @param int $projectRunId
+ * @return bool
+ */
+ public function deleteConfRuns($projectRunId)
+ {
+ $query = $this->db->prepare("
+ DELETE FROM conf_run WHERE project_run_id = ?;
+ ");
+ $query->bindParam(1, $projectRunId, PDO::PARAM_INT);
+ $result = $query->execute();
+ return $result;
+ }
+
+ /**
+ * Delete phase_runs for conf_run
+ * @param int $confRunId
+ * @return bool
+ */
+ public function deletePhaseRuns($confRunId)
+ {
+ $query = $this->db->prepare("
+ DELETE FROM phase_run WHERE conf_run_id = ?;
+ ");
+ $query->bindParam(1, $confRunId, PDO::PARAM_INT);
+ $result = $query->execute();
+ return $result;
+ }
+
+ /**
+ * Delete testset_runs for conf_run
+ * @param int $confRunId
+ * @return bool
+ */
+ public function deleteTestsetRuns($confRunId)
+ {
+ $query = $this->db->prepare("
+ DELETE FROM testset_run WHERE conf_run_id = ?;
+ ");
+ $query->bindParam(1, $confRunId, PDO::PARAM_INT);
+ $result = $query->execute();
+ return $result;
+ }
+
+ /**
+ * Delete testfunction_runs for testset_run
+ * @param int $testsetRunId
+ * @return bool
+ */
+ public function deleteTestfunctionRuns($testsetRunId)
+ {
+ $query = $this->db->prepare("
+ DELETE FROM testfunction_run WHERE testset_run_id = ?;
+ ");
+ $query->bindParam(1, $testsetRunId, PDO::PARAM_INT);
+ $result = $query->execute();
+ return $result;
+ }
+
+ /**
+ * Delete testrow_runs for testfunction_run
+ * @param int $testfunctionRunId
+ * @return bool
+ */
+ public function deleteTestrowRuns($testfunctionRunId)
+ {
+ $query = $this->db->prepare("
+ DELETE FROM testrow_run WHERE testfunction_run_id = ?;
+ ");
+ $query->bindParam(1, $testfunctionRunId, PDO::PARAM_INT);
+ $result = $query->execute();
+ return $result;
+ }
+
+ /**
+ * Delete project_run and all its linked data
+ * @param int $projectRunId
+ * @return bool
+ */
+ public function deleteProjectRunData($projectRunId)
+ {
+ $result = true;
+ $confRuns = self::getConfRunIds($projectRunId);
+ foreach ($confRuns as $confRun) {
+ $testsetRuns = self::getTestsetRunIds($confRun['id']);
+ foreach ($testsetRuns as $testsetRun) {
+ $testfunctionRuns = self::getTestfunctionRunIds($testsetRun['id']);
+ foreach ($testfunctionRuns as $testfunctionRun) {
+ // Delete related testrow_runs
+ if (!self::deleteTestrowRuns($testfunctionRun['id']))
+ $result = false;
+ }
+ // Delete related testfunction_runs
+ if (!self::deleteTestfunctionRuns($testsetRun['id']))
+ $result = false;
+ }
+ // Delete related testset_runs
+ if (!self::deleteTestsetRuns($confRun['id']))
+ $result = false;
+ // Delete related phase_runs
+ if (!self::deletePhaseRuns($confRun['id']))
+ $result = false;
+ }
+ // Delete related conf_runs
+ if (!self::deleteConfRuns($projectRunId))
+ $result = false;
+ // Delete project_run
+ if (!self::deleteProjectRun($projectRunId))
+ $result = false;
+ return $result;
+ }
+
+ /**
+ * Delete branch and all its linked data
+ * @param string $branch
+ * @return bool
+ */
+ public function deleteBranch($branch)
+ {
+ $result = true;
+ // Delete data from xxx_run tables
+ $projectRuns = self::getProjectRunIdsBranch($branch);
+ foreach ($projectRuns as $projectRun) {
+ if (!self::deleteProjectRunData($projectRun['id']))
+ $result = false;
+ }
+ // Delete branch
+ $query = $this->db->prepare("
+ DELETE FROM branch WHERE name = ?
+ ");
+ $result2 = $query->execute(array(
+ $branch
+ ));
+ if (!$result2)
+ $result = false;
+ return $result;
+ }
+
+ /**
+ * Delete all build runs from selected date in selected state
+ * @param string $state
+ * @param string $date
+ * @return bool
+ */
+ public function deleteRunsData($state, $date)
+ {
+ $result = true;
+ $projectRuns = self::getProjectRunIdsDate($state, $date);
+ foreach ($projectRuns as $projectRun) {
+ if (!self::deleteProjectRunData($projectRun['id']))
+ $result = false;
+ }
+ return $result;
+ }
+
+}
+
+?>
diff --git a/non-puppet/qtmetrics2/src/Factory.php b/non-puppet/qtmetrics2/src/Factory.php
index 754168c..8429852 100644
--- a/non-puppet/qtmetrics2/src/Factory.php
+++ b/non-puppet/qtmetrics2/src/Factory.php
@@ -34,12 +34,12 @@
/**
* Factory class
- * @version 0.8
- * @since 23-07-2015
+ * @since 17-08-2015
* @author Juha Sippola
*/
require_once 'Database.php';
+require_once 'DatabaseAdmin.php';
require_once 'Project.php';
require_once 'ProjectRun.php';
require_once 'Conf.php';
@@ -68,6 +68,12 @@ class Factory {
private $db;
/**
+ * DatabaseAdmin instance.
+ * @var DatabaseAdmin
+ */
+ private $dbAdmin;
+
+ /**
* Create Factory instance
* @return Factory
*/
@@ -105,6 +111,19 @@ class Factory {
}
/**
+ * Get databaseAdmin instance
+ * @return DatabaseAdmin
+ */
+ public static function dbAdmin()
+ {
+ $instance = self::singleton();
+ if (!$instance->dbAdmin) {
+ $instance->dbAdmin = new DatabaseAdmin;
+ }
+ return $instance->dbAdmin;
+ }
+
+ /**
* Manipulate configuration settings runtime (for unit testing purposes)
* @param string $key
* @param string $value
diff --git a/non-puppet/qtmetrics2/src/HttpBasicAuth.php b/non-puppet/qtmetrics2/src/HttpBasicAuth.php
new file mode 100644
index 0000000..cfe9caf
--- /dev/null
+++ b/non-puppet/qtmetrics2/src/HttpBasicAuth.php
@@ -0,0 +1,109 @@
+<?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$
+##
+#############################################################################
+
+/**
+ * Basic HTTP authentication
+ * @since 06-08-2015
+ * @author Juha Sippola
+ */
+
+class HttpBasicAuth extends \Slim\Middleware
+{
+ /**
+ * @var string
+ */
+ protected $realm;
+
+ /**
+ * Constructor
+ * @param string $realm The HTTP Authentication realm
+ */
+ public function __construct($realm = 'Protected Area')
+ {
+ $this->realm = $realm;
+ }
+
+ /**
+ * Deny Access
+ */
+ public function deny_access() {
+ $res = $this->app->response();
+ $res->status(401);
+ $res->header('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realm));
+ }
+
+ /**
+ * Authenticate
+ * @param string $username The HTTP Authentication username
+ * @param string $password The HTTP Authentication password
+ */
+ public function authenticate($username, $password) {
+ $ini = Factory::conf();
+
+ // The username must be alphanumeric
+ if(!ctype_alnum($username))
+ return false;
+
+ // Check username and password
+ if(isset($username) && isset($password)) {
+ if (($username == $ini['admin_username'] AND $password == $ini['admin_password'])) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Call
+ * This method will check the HTTP request headers for previous authentication. If
+ * the request has already authenticated, the next middleware is called. Otherwise,
+ * a 401 Authentication Required response is returned to the client.
+ */
+ public function call()
+ {
+ $req = $this->app->request();
+ $res = $this->app->response();
+ $authUser = strip_tags($req->headers('PHP_AUTH_USER'));
+ $authPass = strip_tags($req->headers('PHP_AUTH_PW'));
+
+ if ($this->authenticate($authUser, $authPass)) {
+ $this->next->call();
+ } else {
+ $this->deny_access();
+ }
+ }
+}
diff --git a/non-puppet/qtmetrics2/src/HttpBasicAuthRoute.php b/non-puppet/qtmetrics2/src/HttpBasicAuthRoute.php
new file mode 100644
index 0000000..5974e53
--- /dev/null
+++ b/non-puppet/qtmetrics2/src/HttpBasicAuthRoute.php
@@ -0,0 +1,69 @@
+<?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$
+##
+#############################################################################
+
+/**
+ * Basic HTTP authentication for a route
+ * @since 06-08-2015
+ * @author Juha Sippola
+ */
+
+class HttpBasicAuthRoute extends HttpBasicAuth {
+
+ /**
+ * @var string
+ */
+ protected $route;
+
+ /**
+ * Constructor
+ * @param string $realm The HTTP Authentication realm
+ * @param string route The route for authentication
+ */
+ public function __construct($realm = 'Protected Area', $route = '') {
+ $this->route = $route;
+ parent::__construct($realm);
+ }
+
+ /**
+ * Call
+ * This method will check if the requested route needs authentication.
+ */
+ public function call() {
+ if(strpos($this->app->request()->getPathInfo(), $this->route) !== false) {
+ parent::call();
+ return;
+ }
+ $this->next->call();
+ }
+}
diff --git a/non-puppet/qtmetrics2/src/test/DatabaseAdminTest.php b/non-puppet/qtmetrics2/src/test/DatabaseAdminTest.php
new file mode 100644
index 0000000..a3a8c1e
--- /dev/null
+++ b/non-puppet/qtmetrics2/src/test/DatabaseAdminTest.php
@@ -0,0 +1,516 @@
+<?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');
+
+/**
+ * DatabaseAdmin 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 19-08-2015
+ * @author Juha Sippola
+ */
+
+class DatabaseAdminTest extends PHPUnit_Framework_TestCase
+{
+
+ /**
+ * Type for delete tests (only one can be tested at a time)
+ */
+ const DELETE_NONE = 0; // Do not delete (default to enable unit testing all classes)
+ const DELETE_FROM_RUN_TABLE = 1; // Delete by id from each xxx_run table *)
+ const DELETE_PROJECT_RUN_DATA = 2; // Delete by project_run id *)
+ const DELETE_BRANCH = 3; // Delete by branch *)
+ const DELETE_RUN_DATA = 4; // Delete by project_run state and date *)
+ // *) Note: The data must be re-inserted into the database after each test run
+ const DELETE_TEST_TYPE = self::DELETE_NONE;
+
+ /**
+ * Test getTablesStatistics
+ * @dataProvider testGetTablesStatisticsData
+ */
+ public function testGetTablesStatistics($exp_table)
+ {
+ $items = array();
+ $dbAdmin = Factory::dbAdmin();
+ $result = $dbAdmin->getTablesStatistics();
+ $this->assertNotEmpty($result);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('name', $row);
+ $this->assertArrayHasKey('rowCount', $row);
+ $items[] = $row['name'];
+ }
+ $this->assertContains($exp_table, $items);
+ }
+ public function testGetTablesStatisticsData()
+ {
+ return array(
+ array('branch'),
+ array('compiler'),
+ array('conf'),
+ array('conf_run'),
+ array('db_status'),
+ array('phase'),
+ array('phase_run'),
+ array('platform'),
+ array('project'),
+ array('project_run'),
+ array('state'),
+ array('testfunction'),
+ array('testfunction_run'),
+ array('testrow'),
+ array('testrow_run'),
+ array('testset'),
+ array('testset_run')
+ );
+ }
+
+ /**
+ * Test getBranchesStatistics
+ * @dataProvider testGetBranchesStatisticsData
+ */
+ public function testGetBranchesStatistics($exp_branch)
+ {
+ $items = array();
+ $dbAdmin = Factory::dbAdmin();
+ $result = $dbAdmin->getBranchesStatistics();
+ $this->assertNotEmpty($result);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('name', $row);
+ $this->assertArrayHasKey('runCount', $row);
+ $this->assertArrayHasKey('latestRun', $row);
+ $items[] = $row['name'];
+ }
+ $this->assertContains($exp_branch, $items);
+ }
+ public function testGetBranchesStatisticsData()
+ {
+ return array(
+ array('dev'),
+ array('stable')
+ );
+ }
+
+ /**
+ * Test getProjectRunsStatistics
+ */
+ public function testGetProjectRunsStatistics()
+ {
+ $items = array();
+ $dbAdmin = Factory::dbAdmin();
+ $result = $dbAdmin->getProjectRunsStatistics();
+ $this->assertNotEmpty($result);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('state', $row);
+ $this->assertArrayHasKey('year', $row);
+ $this->assertArrayHasKey('month', $row);
+ $this->assertArrayHasKey('day', $row);
+ $this->assertArrayHasKey('runCount', $row);
+ }
+ }
+
+ /**
+ * Test getProjectRunIdsBranch
+ * @dataProvider testGetProjectRunIdsBranchData
+ */
+ public function testGetProjectRunIdsBranch($branch, $has_data)
+ {
+ $dbAdmin = Factory::dbAdmin();
+ $result = $dbAdmin->getProjectRunIdsBranch($branch);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('id', $row);
+ }
+ if ($has_data) {
+ $this->assertNotEmpty($result);
+ } else {
+ $this->assertEmpty($result);
+ }
+ }
+ public function testGetProjectRunIdsBranchData()
+ {
+ return array(
+ array('dev', 1),
+ array('stable', 1),
+ array('invalid', 0)
+ );
+ }
+
+ /**
+ * Test getProjectRunIdsDate
+ * @dataProvider testGetProjectRunIdsDateData
+ */
+ public function testGetProjectRunIdsDate($state, $date, $has_data)
+ {
+ $dbAdmin = Factory::dbAdmin();
+ $result = $dbAdmin->getProjectRunIdsDate($state, $date);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('id', $row);
+ }
+ if ($has_data) {
+ $this->assertNotEmpty($result);
+ } else {
+ $this->assertEmpty($result);
+ }
+ }
+ public function testGetProjectRunIdsDateData()
+ {
+ return array(
+ array('state', '2013-05-20', 1),
+ array('state', '2013-04-02', 1),
+ array('state', '2013-03-20', 1),
+ array('state', '2013-02-01', 0),
+ array('state', '2012-12-01', 1),
+ array('state', 'invalid', 0),
+ array('invalid', '2013-05-20', 0),
+ );
+ }
+
+ /**
+ * Test getConfRunIds
+ * @dataProvider testGetConfRunIdsData
+ */
+ public function testGetConfRunIds($projectRunId, $has_data)
+ {
+ $dbAdmin = Factory::dbAdmin();
+ $result = $dbAdmin->getConfRunIds($projectRunId);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('id', $row);
+ }
+ if ($has_data) {
+ $this->assertNotEmpty($result);
+ } else {
+ $this->assertEmpty($result);
+ }
+ }
+ public function testGetConfRunIdsData()
+ {
+ return array(
+ array(140, 1),
+ array(0, 0)
+ );
+ }
+
+ /**
+ * Test getTestsetRunIds
+ * @dataProvider testGetTestsetRunIdsData
+ */
+ public function testGetTestsetRunIds($confRunId, $has_data)
+ {
+ $dbAdmin = Factory::dbAdmin();
+ $result = $dbAdmin->getTestsetRunIds($confRunId);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('id', $row);
+ }
+ if ($has_data) {
+ $this->assertNotEmpty($result);
+ } else {
+ $this->assertEmpty($result);
+ }
+ }
+ public function testGetTestsetRunIdsData()
+ {
+ return array(
+ array(260, 1),
+ array(0, 0)
+ );
+ }
+
+ /**
+ * Test getTestfunctionRunIds
+ * @dataProvider testGetTestfunctionRunIdsData
+ */
+ public function testGetTestfunctionRunIds($testsetRunId, $has_data)
+ {
+ $dbAdmin = Factory::dbAdmin();
+ $result = $dbAdmin->getTestfunctionRunIds($testsetRunId);
+ foreach($result as $row) {
+ $this->assertArrayHasKey('id', $row);
+ }
+ if ($has_data) {
+ $this->assertNotEmpty($result);
+ } else {
+ $this->assertEmpty($result);
+ }
+ }
+ public function testGetTestfunctionRunIdsData()
+ {
+ return array(
+ array(95, 1),
+ array(0, 0)
+ );
+ }
+
+ /**
+ * Test deleteProjectRun, deleteConfRuns, deletePhaseRuns, deleteTestsetRuns, deleteTestfunctionRuns, deleteTestrowRuns
+ * @dataProvider testDeleteRunData
+ */
+ public function testDeleteRun($projectRunId, $confRunId, $phaseRunId, $testsetRunId, $testfunctionRunId, $valid)
+ {
+ if (self::DELETE_TEST_TYPE === self::DELETE_FROM_RUN_TABLE) {
+ $dbAdmin = Factory::dbAdmin();
+ $before = $dbAdmin->getTablesStatistics();
+ $result = $dbAdmin->deleteProjectRun($projectRunId);
+ $this->assertTrue($result);
+ $result = $dbAdmin->deleteConfRuns($projectRunId);
+ $this->assertTrue($result);
+ $result = $dbAdmin->deletePhaseRuns($confRunId);
+ $this->assertTrue($result);
+ $result = $dbAdmin->deleteTestsetRuns($confRunId);
+ $this->assertTrue($result);
+ $result = $dbAdmin->deleteTestfunctionRuns($testsetRunId);
+ $this->assertTrue($result);
+ $result = $dbAdmin->deleteTestrowRuns($testfunctionRunId);
+ $this->assertTrue($result);
+ $after = $dbAdmin->getTablesStatistics();
+ foreach($after as $key => $row) {
+ if (strpos($row['name'],'_run') !== false) {
+ if ($valid) {
+ $this->assertLessThan($before[$key]['rowCount'], $row['rowCount']);
+ } else {
+ $this->assertEquals($before[$key]['rowCount'], $row['rowCount']);
+ }
+ }
+ }
+ }
+ }
+ public function testDeleteRunData()
+ {
+ return array(
+ array(140, 103, 280, 17, 23, 1), // Test data for testfunction "defaultFamily"
+ array(999, 999, 999, 999, 999, 0)
+ );
+ }
+
+ /**
+ * Test deleteProjectRunData
+ * @dataProvider testDeleteProjectRunDataData
+ */
+ public function testDeleteProjectRunData($projectRunId, $valid)
+ {
+ if (self::DELETE_TEST_TYPE === self::DELETE_PROJECT_RUN_DATA) {
+ $dbAdmin = Factory::dbAdmin();
+ $before = $dbAdmin->getTablesStatistics();
+ $result = $dbAdmin->deleteProjectRunData($projectRunId);
+ $this->assertTrue($result);
+ $after = $dbAdmin->getTablesStatistics();
+ foreach($after as $key => $row) {
+ if (strpos($row['name'],'_run') !== false) {
+ if ($valid) {
+ $this->assertLessThan($before[$key]['rowCount'], $row['rowCount']);
+ } else {
+ $this->assertEquals($before[$key]['rowCount'], $row['rowCount']);
+ }
+ }
+ }
+ }
+ }
+ public function testDeleteProjectRunDataData()
+ {
+ return array(
+ array(140, 1),
+ array(999, 0)
+ );
+ }
+
+ /**
+ * Test deleteBranch and deleteProjectRunData
+ * @dataProvider testDeleteBranchData
+ */
+ public function testDeleteBranch($runProject, $runState, $branch, $has_data, $step)
+ {
+ if (self::DELETE_TEST_TYPE === self::DELETE_BRANCH) {
+ $db = Factory::db();
+ $dbAdmin = Factory::dbAdmin();
+ // Check that xxx_run tables have data initially
+ if ($step == 'first') {
+ $result = $dbAdmin->getTablesStatistics();
+ foreach($result as $row) {
+ if (strpos($row['name'],'_run') !== false) {
+ $this->assertGreaterThan(0, $row['rowCount']);
+ }
+ }
+ }
+ if ($has_data) {
+ // Check that project_runs exist for the branch initially
+ $result = $db->getProjectBuildsByBranch($runProject, $runState);
+ $branches = array();
+ foreach($result as $row) {
+ $branches[] = $row['branch'];
+ }
+ $this->assertContains($branch, $branches);
+ // Check that conf_runs exist for the branch initially
+ $result = $db->getConfBuildsByBranch($runProject, $runState);
+ $branches = array();
+ foreach($result as $row) {
+ $branches[] = $row['branch'];
+ }
+ $this->assertContains($branch, $branches);
+ }
+ // Delete the branch data
+ $branches = array();
+ $result = $db->getBranches();
+ foreach($result as $row) {
+ $branches[] = $row['name'];
+ }
+ $this->assertContains($branch, $branches);
+ $success = $dbAdmin->deleteBranch($branch);
+ $this->assertTrue($success);
+ $branches = array();
+ $result = $db->getBranches();
+ foreach($result as $row) {
+ $branches[] = $row['name'];
+ }
+ $this->assertNotContains($branch, $branches);
+ // Check if project_runs deleted
+ $result = $db->getProjectBuildsByBranch($runProject, $runState);
+ $branches = array();
+ foreach($result as $row) {
+ $branches[] = $row['branch'];
+ }
+ $this->assertNotContains($branch, $branches);
+ // Check if conf_runs deleted
+ $result = $db->getConfBuildsByBranch($runProject, $runState);
+ $branches = array();
+ foreach($result as $row) {
+ $branches[] = $row['branch'];
+ }
+ $this->assertNotContains($branch, $branches);
+ // Check that xxx_run tables are empty
+ if ($step == 'last') {
+ $result = $dbAdmin->getTablesStatistics();
+ foreach($result as $row) {
+ if (strpos($row['name'],'_run') !== false) {
+ $this->assertEquals(0, $row['rowCount']);
+ }
+ }
+ }
+ }
+ }
+ public function testDeleteBranchData()
+ {
+ return array(
+ array('Qt5', 'state', 'dev', 1, 'first'),
+ array('Qt5', 'state', 'stable', 1, ''),
+ array('Qt5', 'state', 'release', 1, ''),
+ array('Qt5', 'state', 'master', 1, ''),
+ array('Qt5', 'state', '1.2.3', 0, 'last')
+ );
+ }
+
+ /**
+ * Test deleteRunsData and deleteProjectRunData
+ * @dataProvider testDeleteRunsDataData
+ */
+ public function testDeleteRunsData($runProject, $runState, $state, $date, $has_data, $step)
+ {
+ if (self::DELETE_TEST_TYPE === self::DELETE_RUN_DATA) {
+ $db = Factory::db();
+ $dbAdmin = Factory::dbAdmin();
+ if ($step == 'first') {
+ // Check that xxx_run tables have data
+ $result = $dbAdmin->getTablesStatistics();
+ foreach($result as $row) {
+ if (strpos($row['name'],'_run') !== false) {
+ $this->assertGreaterThan(0, $row['rowCount']);
+ }
+ }
+ }
+ if ($has_data) {
+ // Check that project_runs exist initially
+ $result = $db->getProjectBuildsByBranch($runProject, $runState);
+ $dates = array();
+ foreach($result as $row) {
+ $dates[] = substr($row['timestamp'], 0, strlen('2015-08-01'));
+ }
+ $this->assertContains($date, $dates);
+ }
+ // Delete the data
+ $dbAdmin->deleteRunsData($state, $date);
+ // Check if project_runs deleted
+ $result = $db->getProjectBuildsByBranch($runProject, $runState);
+ $dates = array();
+ foreach($result as $row) {
+ $dates[] = substr($row['timestamp'], 0, strlen('2015-08-01'));
+ }
+ $this->assertNotContains($date, $dates);
+ if ($step == 'last') {
+ // Check that xxx_run tables are empty
+ $result = $dbAdmin->getTablesStatistics();
+ foreach($result as $row) {
+ if (strpos($row['name'],'_run') !== false) {
+ $this->assertEquals(0, $row['rowCount']);
+ }
+ }
+ }
+ }
+ }
+ public function testDeleteRunsDataData()
+ {
+ return array(
+ array('Qt5', 'state', 'state', '2013-05-20', '1', 'first'),
+ array('Qt5', 'state', 'state', '2013-04-02', '1', ''),
+ array('Qt5', 'state', 'state', '2013-03-20', '1', ''),
+ array('Qt5', 'state', 'state', '2013-02-01', '0', ''),
+ array('Qt5', 'state', 'state', '2012-12-01', '1', '')
+ );
+ }
+
+ /**
+ * Print info on the delete test type
+ */
+ public function testPrintDeleteTestInfo()
+ {
+ switch (self::DELETE_TEST_TYPE) {
+ case self::DELETE_FROM_RUN_TABLE:
+ echo ' Note: DatabaseAdmin delete test type DELETE_FROM_RUN_TABLE selected.';
+ echo ' Please re-insert the data into the database before rerunning the tests!';
+ break;
+ case self::DELETE_PROJECT_RUN_DATA:
+ echo ' Note: DatabaseAdmin delete test type DELETE_PROJECT_RUN_DATA selected.';
+ echo ' Please re-insert the data into the database before rerunning the tests!';
+ break;
+ case self::DELETE_BRANCH:
+ echo ' Note: DatabaseAdmin delete test type DELETE_BRANCH selected.';
+ echo ' Please re-insert the data into the database before rerunning the tests!';
+ break;
+ case self::DELETE_RUN_DATA:
+ echo ' Note: DatabaseAdmin delete test type DELETE_RUN_DATA selected.';
+ echo ' Please re-insert the data into the database before rerunning the tests!';
+ break;
+ }
+ }
+
+}
+
+?>
diff --git a/non-puppet/qtmetrics2/src/test/FactoryTest.php b/non-puppet/qtmetrics2/src/test/FactoryTest.php
index 126702b..8fbb17e 100644
--- a/non-puppet/qtmetrics2/src/test/FactoryTest.php
+++ b/non-puppet/qtmetrics2/src/test/FactoryTest.php
@@ -37,8 +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
- * @version 0.7
- * @since 24-07-2015
+ * @since 17-08-2015
* @author Juha Sippola
*/
@@ -79,6 +78,15 @@ class FactoryTest extends PHPUnit_Framework_TestCase
}
/**
+ * Test dbAdmin
+ */
+ public function testDbAdmin()
+ {
+ $db = Factory::dbAdmin();
+ $this->assertTrue($db instanceof DatabaseAdmin);
+ }
+
+ /**
* Test getCiLogPath
* @dataProvider testGetCiLogPathData
*/
diff --git a/non-puppet/qtmetrics2/styles/qtmetrics.css b/non-puppet/qtmetrics2/styles/qtmetrics.css
index f61a25e..72846dc 100644
--- a/non-puppet/qtmetrics2/styles/qtmetrics.css
+++ b/non-puppet/qtmetrics2/styles/qtmetrics.css
@@ -35,7 +35,7 @@
/**
* Qt Metrics style sheet
- * @since 05-08-2015
+ * @since 06-08-2015
* @author Juha Sippola
*/
@@ -149,7 +149,10 @@
border-right: 2px solid lightgrey;
}
.center {
- text-align: center
+ text-align: center;
+}
+.right {
+ text-align: right;
}
thead {
white-space: nowrap;
@@ -176,6 +179,9 @@ thead {
.redBackground {
background-color: indianred;
}
+.blueBackground {
+ background-color: #337AB7;
+}
/*
* Placing and sizing
diff --git a/non-puppet/qtmetrics2/templates/about.html b/non-puppet/qtmetrics2/templates/about.html
index cb92979..e8b7dd2 100644
--- a/non-puppet/qtmetrics2/templates/about.html
+++ b/non-puppet/qtmetrics2/templates/about.html
@@ -34,7 +34,7 @@
/**
* About window content
- * @since 14-08-2015
+ * @since 19-08-2015
* @author Juha Sippola
*/
@@ -43,4 +43,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.19 (14-Aug-2015)</small></p>
+<p><small>Version 0.20 (19-Aug-2015)</small></p>
diff --git a/non-puppet/qtmetrics2/templates/admin.html b/non-puppet/qtmetrics2/templates/admin.html
new file mode 100644
index 0000000..c3ada7d
--- /dev/null
+++ b/non-puppet/qtmetrics2/templates/admin.html
@@ -0,0 +1,137 @@
+{#
+#############################################################################
+##
+## 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$
+##
+#############################################################################
+
+/**
+ * Admin page: Summary
+ * @since 17-08-2015
+ * @author Juha Sippola
+ */
+
+#}
+
+{# Row count bar area size in px #}
+{% set BAR_AREA = 200 %}
+
+{% include "header.html" %}
+
+<ol class="breadcrumb">
+{% for link in breadcrumb %}
+<li><a href="{{ link.link }}">{{ link.name }}</a></li>
+{% endfor %}
+<li class="active">admin</li>
+</ol>
+
+<div class="container-fluid">
+<div class="row">
+
+<div class="col-sm-12 col-md-12 main">
+
+{##### Title #####}
+
+<h1 class="page-header">Admin</h1>
+
+{##### Navigation #####}
+
+<ul class="nav nav-tabs">
+<li role="presentation" class="active"><a href="{{ adminRoute }}">Summary</a></li>
+<li role="presentation"><a href="{{ adminBranchesRoute }}">Branches</a></li>
+<li role="presentation"><a href="{{ adminDataRoute }}">Data maintenance</a></li>
+</ul>
+<br>
+
+{##### Tables #####}
+
+{# Calculate counts #}
+{% set maxCount = 1 %}
+{% set tableCount = 0 %}
+{% for table in tables %}
+{% if table.rowCount > maxCount %}
+{% set maxCount = table.rowCount %}
+{% endif %}
+{% set tableCount = tableCount + 1 %}
+{% endfor %}
+
+<div class="panel panel-primary">
+<div class="panel-heading">
+<h4 class="panel-title bold">Tables ({{ tableCount }})</h4>
+</div>
+<div class="panel-body">
+<div class="table-responsive">
+<table class="table table-striped">
+<thead>
+<tr>
+<th>table</th>
+<th class="leftBorder right">rows</th>
+<th class="showInLargeDisplay"></th>
+</tr>
+</thead>
+<tbody>
+{# Print tables #}
+{% for table in tables %}
+<tr>
+{# table name #}
+<td>{{ table.name }}</td>
+
+{# Show row count as numbers #}
+{% set rows = table.rowCount %}
+<td class="leftBorder right">{{ rows|number_format(0, '.', ' ') }}</td>
+
+{# Show row count as bars (scaled to BAR_AREA px) #}
+{% set rowsBar = ((BAR_AREA/maxCount) * rows)|round(0, 'floor') %}
+{% if (rows > 0) and (rowsBar == 0) %}
+{% set rowsBar = 1 %}
+{% endif %}
+<td class="center showInLargeDisplay">
+<div>
+<div class="floatLeft blueBackground" style="width: {{ rowsBar }}px">&nbsp;</div>
+</div>
+</td>
+</tr>
+{% endfor %}{# table #}
+</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 #}
+{# (none) #}
+
+{% include "close.html" %}
diff --git a/non-puppet/qtmetrics2/templates/admin_branches.html b/non-puppet/qtmetrics2/templates/admin_branches.html
new file mode 100644
index 0000000..1b8fc9a
--- /dev/null
+++ b/non-puppet/qtmetrics2/templates/admin_branches.html
@@ -0,0 +1,167 @@
+{#
+#############################################################################
+##
+## 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$
+##
+#############################################################################
+
+/**
+ * Admin page: Branches
+ * @since 19-08-2015
+ * @author Juha Sippola
+ */
+
+#}
+
+{# Row count bar area size in px #}
+{% set BAR_AREA = 120 %}
+
+{% include "header.html" %}
+
+<ol class="breadcrumb">
+{% for link in breadcrumb %}
+<li><a href="{{ link.link }}">{{ link.name }}</a></li>
+{% endfor %}
+<li class="active">branches</li>
+</ol>
+
+<div class="container-fluid">
+<div class="row">
+
+<div class="col-sm-12 col-md-12 main">
+
+{##### Title #####}
+
+<h1 class="page-header">Admin</h1>
+
+{##### Navigation #####}
+
+<ul class="nav nav-tabs">
+<li role="presentation"><a href="{{ adminRoute }}">Summary</a></li>
+<li role="presentation" class="active"><a href="{{ adminBranchesRoute }}">Branches</a></li>
+<li role="presentation"><a href="{{ adminDataRoute }}">Data maintenance</a></li>
+</ul>
+<br>
+
+{##### Branches #####}
+
+{# Calculate counts #}
+{% set maxCount = 1 %}
+{% set branchCount = 0 %}
+{% for branch in branches %}
+{% if branch.runCount > maxCount %}
+{% set maxCount = branch.runCount %}
+{% endif %}
+{% set branchCount = branchCount + 1 %}
+{% endfor %}
+
+<div class="panel panel-primary">
+<div class="panel-heading">
+<h4 class="panel-title bold">Branches ({{ branchCount }})</h4>
+</div>
+<div class="panel-body">
+<div class="table-responsive">
+<table class="table table-striped">
+<thead>
+<tr>
+<th>branch</th>
+<th class="rightBorder"></th>
+<th class="center">latest run</th>
+<th class="center">total runs</th>
+<th class="showInLargeDisplay"></th>
+</tr>
+</thead>
+<tbody>
+{# Print branches #}
+{% for branch in branches %}
+<tr>
+{# Branch name #}
+<td>{{ branch.name }}</td>
+{% set branchTag = branch.name|replace({'.': '-'}) %}{# '.' not allowed in id #}
+
+{# Button (opens a confirmation modal) #}
+<td id="{{ branchTag }}Button" class="rightBorder"><button type="button" class="btn btn-danger btn-xs" data-toggle="modal" data-target="#{{ branchTag }}Modal">Remove</button></td>
+
+{# Latest project_run #}
+<td class="center">{{ branch.latestRun }}</td>
+
+{# Show run count as numbers #}
+{% set runs = branch.runCount %}
+<td class="center">{{ runs }}</td>
+
+{# Show row count as bars (scaled to BAR_AREA px) #}
+{% set runsBar = ((BAR_AREA/maxCount) * runs)|round(0, 'floor') %}
+{% if (runs > 0) and (runsBar == 0) %}
+{% set runsBar = 1 %}
+{% endif %}
+<td class="center showInLargeDisplay">
+<div>
+<div class="floatLeft blueBackground" style="width: {{ runsBar }}px">&nbsp;</div>
+</div>
+</td>
+</tr>
+{% endfor %}{# branch #}
+</tbody>
+</table>
+</div> {# .table-responsive #}
+</div> {# .panel-body #}
+</div> {# .panel... #}
+
+{# Modals for remove confirmation #}
+{% set message = "Removing the branch will also delete all related items from the xxx_run tables. THIS OPERATION CANNOT BE UNDONE!" %}
+{% for branch in branches %}
+{% set branchTag = branch.name|replace({'.': '-'}) %}{# '.' not allowed in id #}
+<div class="modal fade" id="{{ branchTag }}Modal" tabindex="-1" role="dialog" aria-labelledby="{{ branchTag }}ModalLabel" aria-hidden="true">
+<div class="modal-dialog">
+<div class="modal-content">
+<div class="modal-header">
+<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+<h4 class="modal-title" id="{{ branchTag }}ModalLabel">Remove branch "{{ branch.name }}"</h4>
+</div>
+<div class="modal-body">{{ message }}</div>
+<div class="modal-footer">
+<button type="button" class="btn btn-default" data-dismiss="modal" id="cancel_branch_remove_{{ branch.name }}">Cancel</button>
+<button type="button" class="btn btn-danger remove_branch" data-dismiss="modal" id="confirm_branch_remove_{{ branchTag }}" name="{{ branch.name }}">Remove</button>
+</div>
+</div>
+</div>
+</div> {# .modal #}
+{% endfor %}{# branch #}
+
+</div> {# .col... #}
+</div> {# .row #}
+</div> {# .container-fluid #}
+
+{% include "footer.html" %}
+
+{# Local scripts for this page #}
+<script src="scripts/admin.js"></script>
+
+{% include "close.html" %}
diff --git a/non-puppet/qtmetrics2/templates/admin_data.html b/non-puppet/qtmetrics2/templates/admin_data.html
new file mode 100644
index 0000000..b7ce7fd
--- /dev/null
+++ b/non-puppet/qtmetrics2/templates/admin_data.html
@@ -0,0 +1,202 @@
+{#
+#############################################################################
+##
+## 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$
+##
+#############################################################################
+
+/**
+ * Admin page: Data maintenance
+ * @since 17-08-2015
+ * @author Juha Sippola
+ */
+
+#}
+
+{# Row count bar area size in px #}
+{% set BAR_AREA = 200 %}
+
+{% include "header.html" %}
+
+<ol class="breadcrumb">
+{% for link in breadcrumb %}
+<li><a href="{{ link.link }}">{{ link.name }}</a></li>
+{% endfor %}
+<li class="active">data</li>
+</ol>
+
+<div class="container-fluid">
+<div class="row">
+
+<div class="col-sm-12 col-md-12 main">
+
+{##### Title #####}
+
+<h1 class="page-header">Admin</h1>
+
+{##### Navigation #####}
+
+<ul class="nav nav-tabs">
+<li role="presentation"><a href="{{ adminRoute }}">Summary</a></li>
+<li role="presentation"><a href="{{ adminBranchesRoute }}">Branches</a></li>
+<li role="presentation" class="active"><a href="{{ adminDataRoute }}">Data maintenance</a></li>
+</ul>
+<br>
+
+{##### Project runs #####}
+
+{# Get states #}
+{% set states = [] %}
+{% for run in projectRuns %}
+{% if run.state not in states %}
+{% set states = states|merge([run.state]) %}
+{% endif %}
+{% endfor %}
+
+{# Calculate max count #}
+{% set maxCount = 1 %}
+{% for projectRun in projectRuns %}
+{% if projectRun.runCount > maxCount %}
+{% set maxCount = projectRun.runCount %}
+{% endif %}
+{% endfor %}
+
+{# Loop all the states #}
+{% for state in states %}
+
+{# Calculate run count #}
+{% set runCount = 0 %}
+{% for projectRun in projectRuns if projectRun.state == state %}
+{% set runCount = runCount + projectRun.runCount %}
+{% endfor %}
+
+<div class="panel panel-primary">
+<div class="panel-heading">
+<h4 class="panel-title bold">{{ state }} project runs ({{ runCount }})</h4>
+</div>
+<div class="panel-body">
+<div class="table-responsive">
+<table class="table table-striped">
+<thead>
+<tr>
+<th class="center">year</th>
+<th class="center">month</th>
+<th class="center">day</th>
+<th class="rightBorder"></th>
+<th class="center">runs</th>
+<th class="showInLargeDisplay"></th>
+</tr>
+</thead>
+<tbody>
+{# Print projectRuns #}
+{% for projectRun in projectRuns if projectRun.state == state %}
+<tr>
+{# year and month #}
+<td class="center">{{ projectRun.year }}</td>
+<td class="center">{{ projectRun.month }}</td>
+<td class="center">{{ projectRun.day }}</td>
+{% set monthFiller = '' %}
+{% set dayFiller = '' %}
+{% if projectRun.month < 10 %}
+{% set monthFiller = '0' %}
+{% endif %}
+{% if projectRun.day < 10 %}
+{% set dayFiller = '0' %}
+{% endif %}
+{% set tag = projectRun.state ~ "-" ~ projectRun.year ~ "-" ~ monthFiller ~ projectRun.month ~ "-" ~ dayFiller ~ projectRun.day %}
+
+{# Button (opens a confirmation modal) #}
+<td id="{{ tag }}Button" class="rightBorder"><button type="button" class="btn btn-danger btn-xs" data-toggle="modal" data-target="#{{ tag }}Modal">Remove</button></td>
+
+{# Show run count as numbers #}
+{% set runs = projectRun.runCount %}
+<td class="center">{{ runs }}</td>
+
+{# Show row count as bars (scaled to BAR_AREA px) #}
+{% set runsBar = ((BAR_AREA/maxCount) * runs)|round(0, 'floor') %}
+{% if (runs > 0) and (runsBar == 0) %}
+{% set runsBar = 1 %}
+{% endif %}
+<td class="center showInLargeDisplay">
+<div>
+<div class="floatLeft blueBackground" style="width: {{ runsBar }}px">&nbsp;</div>
+</div>
+</td>
+</tr>
+{% endfor %}{# projectRun #}
+</tbody>
+</table>
+</div> {# .table-responsive #}
+</div> {# .panel-body #}
+</div> {# .panel... #}
+{% endfor %}{# state #}
+
+{# Modals for remove confirmation #}
+{% set message = "Removing will delete all related items from the '_run' tables. This may take up to one or two minutes. THIS OPERATION CANNOT BE UNDONE!" %}
+{% for projectRun in projectRuns %}
+{% set monthFiller = '' %}
+{% set dayFiller = '' %}
+{% if projectRun.month < 10 %}
+{% set monthFiller = '0' %}
+{% endif %}
+{% if projectRun.day < 10 %}
+{% set dayFiller = '0' %}
+{% endif %}
+{% set tag = projectRun.state ~ "-" ~ projectRun.year ~ "-" ~ monthFiller ~ projectRun.month ~ "-" ~ dayFiller ~ projectRun.day %}
+{% set tagDate = projectRun.year ~ "-" ~ monthFiller ~ projectRun.month ~ "-" ~ dayFiller ~ projectRun.day %}
+<div class="modal fade" id="{{ tag }}Modal" tabindex="-1" role="dialog" aria-labelledby="{{ tag }}ModalLabel" aria-hidden="true">
+<div class="modal-dialog">
+<div class="modal-content">
+<div class="modal-header">
+<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+<h4 class="modal-title" id="{{ tag }}ModalLabel">Remove {{ tagDate }} data in "{{ projectRun.state }}"</h4>
+</div>
+<div class="modal-body">
+{{ message }}
+</div>
+<div class="modal-footer">
+<button type="button" class="btn btn-default" data-dismiss="modal" id="cancel_data_remove_{{ tag }}">Cancel</button>
+<button type="button" class="btn btn-danger remove_branch" data-dismiss="modal" id="confirm_data_remove_{{ tagDate }}" name="{{ projectRun.state }}">Remove</button>
+</div>
+</div>
+</div>
+</div> {# .modal #}
+{% endfor %}{# projectRun #}
+
+</div> {# .col... #}
+</div> {# .row #}
+</div> {# .container-fluid #}
+
+{% include "footer.html" %}
+
+{# Local scripts for this page #}
+<script src="scripts/admin.js"></script>
+
+{% include "close.html" %}