Add new graph time for variance of individual tests
This commit adds a new graph type which is used to show run time variance for individual tests. It will get all the run times for each test and then generate a box and whiskers (gag) plot for this data. Change-Id: Ia44ea36486296fb0db705b1c55cd2338c204dc91
This commit is contained in:
parent
c7c7f83f45
commit
7f87b412bc
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A new subunit2sql-graph type, `test_run_time`, is added. This will graph the
|
||||||
|
run time variance for the specified tests as box and wiskers plots.
|
||||||
|
- |
|
||||||
|
A new DB API function, get_run_times_all_test_runs(), was added. This will
|
||||||
|
return a all the individual durations for sucessful executions for tests.
|
@ -24,6 +24,7 @@ import subunit2sql.analysis.failures
|
|||||||
import subunit2sql.analysis.run_failure_rate
|
import subunit2sql.analysis.run_failure_rate
|
||||||
import subunit2sql.analysis.run_time
|
import subunit2sql.analysis.run_time
|
||||||
import subunit2sql.analysis.run_time_meta
|
import subunit2sql.analysis.run_time_meta
|
||||||
|
import subunit2sql.analysis.test_run_time
|
||||||
from subunit2sql import shell
|
from subunit2sql import shell
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -57,7 +58,7 @@ def add_command_parsers(subparsers):
|
|||||||
graph_commands = {}
|
graph_commands = {}
|
||||||
# Put commands from in-tree commands on init list
|
# Put commands from in-tree commands on init list
|
||||||
for command in ['failures', 'run_time', 'agg_count', 'dailycount',
|
for command in ['failures', 'run_time', 'agg_count', 'dailycount',
|
||||||
'run_failure_rate', 'run_time_meta']:
|
'run_failure_rate', 'run_time_meta', 'test_run_time']:
|
||||||
graph_commands[command] = getattr(subunit2sql.analysis, command)
|
graph_commands[command] = getattr(subunit2sql.analysis, command)
|
||||||
|
|
||||||
# Load any installed out of tree commands on the init list
|
# Load any installed out of tree commands on the init list
|
||||||
|
66
subunit2sql/analysis/test_run_time.py
Normal file
66
subunit2sql/analysis/test_run_time.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from oslo_config import cfg
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from subunit2sql.db import api
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
matplotlib.style.use('ggplot')
|
||||||
|
|
||||||
|
|
||||||
|
def set_cli_opts(parser):
|
||||||
|
parser.add_argument('test_ids', nargs='*',
|
||||||
|
help='Test ids to graph the run time for, if none '
|
||||||
|
'are specified all tests will be used')
|
||||||
|
|
||||||
|
|
||||||
|
def generate_series():
|
||||||
|
session = api.get_session()
|
||||||
|
|
||||||
|
test_ids = None
|
||||||
|
if CONF.command.test_ids:
|
||||||
|
test_ids = CONF.command.test_ids
|
||||||
|
if CONF.start_date:
|
||||||
|
start_date = datetime.datetime.strptime(CONF.start_date, '%Y-%m-%d')
|
||||||
|
else:
|
||||||
|
start_date = None
|
||||||
|
if CONF.stop_date:
|
||||||
|
stop_date = datetime.datetime.strptime(CONF.stop_date, '%Y-%m-%d')
|
||||||
|
else:
|
||||||
|
stop_date = None
|
||||||
|
run_times = api.get_run_times_all_test_runs(tests=test_ids,
|
||||||
|
start_date=start_date,
|
||||||
|
stop_date=stop_date,
|
||||||
|
session=session)
|
||||||
|
df = pd.DataFrame(dict(
|
||||||
|
[(k, pd.Series(v)) for k, v in run_times.iteritems()]))
|
||||||
|
if not CONF.title:
|
||||||
|
title = "Run aggregate run time grouped by metadata"
|
||||||
|
else:
|
||||||
|
title = CONF.title
|
||||||
|
# NOTE(mtreinish): Decrease label font size for the worst case where we
|
||||||
|
# have tons of groups
|
||||||
|
matplotlib.rcParams['xtick.labelsize'] = '3'
|
||||||
|
plt.figure()
|
||||||
|
plt.title(title)
|
||||||
|
df.plot(kind='box', rot=90)
|
||||||
|
plt.ylabel('Time (sec.)')
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(CONF.output, dpi=900)
|
@ -1220,6 +1220,60 @@ def get_ids_for_all_tests(session=None):
|
|||||||
return db_utils.model_query(models.Test, session).values(models.Test.id)
|
return db_utils.model_query(models.Test, session).values(models.Test.id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_run_times_all_test_runs(tests=None, start_date=None, stop_date=None,
|
||||||
|
session=None):
|
||||||
|
"""Return the all the individual duration times for each test_run
|
||||||
|
|
||||||
|
This function will return a dictionary where each key is a test_id and
|
||||||
|
the value is a list of all the durations for each run of that test
|
||||||
|
|
||||||
|
:param list tests: the list of test_ids to get results for, if none is
|
||||||
|
specified all tests
|
||||||
|
:param str start_date: The date to use as the start date for results
|
||||||
|
:param str stop_date: The date to use as the cutoff date for results
|
||||||
|
:param session: optional session object if one isn't provided a new session
|
||||||
|
will be acquired for the duration of this operation
|
||||||
|
|
||||||
|
:return run_times: all the durations for test_runs grouped by test_id
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
session = session or get_session()
|
||||||
|
run_times_query = db_utils.model_query(models.TestRun, session).filter(
|
||||||
|
models.TestRun.status == 'success').join(
|
||||||
|
models.Test, models.TestRun.test_id == models.Test.id)
|
||||||
|
if tests:
|
||||||
|
run_times_query = run_times_query.filter(
|
||||||
|
models.Test.test_id.in_(tests))
|
||||||
|
if start_date:
|
||||||
|
run_times_query = run_times_query.filter(
|
||||||
|
models.TestRun.start_time >= start_date)
|
||||||
|
if stop_date:
|
||||||
|
run_times_query = run_times_query.filter(
|
||||||
|
models.TestRun.start_time <= stop_date)
|
||||||
|
run_times = run_times_query.values(models.Test.test_id,
|
||||||
|
models.TestRun.start_time,
|
||||||
|
models.TestRun.start_time_microsecond,
|
||||||
|
models.TestRun.stop_time,
|
||||||
|
models.TestRun.stop_time_microsecond)
|
||||||
|
run_times_dict = {}
|
||||||
|
for run_time in run_times:
|
||||||
|
test_id = run_time[0]
|
||||||
|
if run_time[1]:
|
||||||
|
start_time = run_time[1]
|
||||||
|
start_time = start_time.replace(microsecond=run_time[2])
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
if run_time[3]:
|
||||||
|
stop_time = run_time[3]
|
||||||
|
stop_time = stop_time.replace(microsecond=run_time[4])
|
||||||
|
duration = read_subunit.get_duration(start_time, stop_time)
|
||||||
|
if test_id in run_times_dict:
|
||||||
|
run_times_dict[test_id].append(duration)
|
||||||
|
else:
|
||||||
|
run_times_dict[test_id] = [duration]
|
||||||
|
return run_times_dict
|
||||||
|
|
||||||
|
|
||||||
def get_run_times_grouped_by_run_metadata_key(key, start_date=None,
|
def get_run_times_grouped_by_run_metadata_key(key, start_date=None,
|
||||||
stop_date=None, session=None,
|
stop_date=None, session=None,
|
||||||
match_key=None,
|
match_key=None,
|
||||||
|
@ -248,6 +248,45 @@ class TestDatabaseAPI(base.TestCase):
|
|||||||
id_value = api.get_id_from_test_id('fake_test')
|
id_value = api.get_id_from_test_id('fake_test')
|
||||||
self.assertEqual(test_a.id, id_value)
|
self.assertEqual(test_a.id, id_value)
|
||||||
|
|
||||||
|
def test_get_run_times_all_test_runs(self):
|
||||||
|
timestamp_a = datetime.datetime.utcnow()
|
||||||
|
timestamp_b = timestamp_a + datetime.timedelta(seconds=3)
|
||||||
|
run_a = api.create_run()
|
||||||
|
run_b = api.create_run()
|
||||||
|
test_a = api.create_test('test_a')
|
||||||
|
test_b = api.create_test('test_b')
|
||||||
|
api.create_test_run(test_a.id, run_a.id, 'success', timestamp_a,
|
||||||
|
timestamp_b)
|
||||||
|
api.create_test_run(test_a.id, run_b.id, 'success', timestamp_a,
|
||||||
|
timestamp_b)
|
||||||
|
api.create_test_run(test_b.id, run_b.id, 'success', timestamp_a,
|
||||||
|
timestamp_b)
|
||||||
|
res = api.get_run_times_all_test_runs()
|
||||||
|
expected_dict = {
|
||||||
|
'test_a': [3, 3],
|
||||||
|
'test_b': [3]
|
||||||
|
}
|
||||||
|
self.assertEqual(expected_dict, res)
|
||||||
|
|
||||||
|
def test_get_run_times_all_test_runs_with_tests_filter(self):
|
||||||
|
timestamp_a = datetime.datetime.utcnow()
|
||||||
|
timestamp_b = timestamp_a + datetime.timedelta(seconds=3)
|
||||||
|
run_a = api.create_run()
|
||||||
|
run_b = api.create_run()
|
||||||
|
test_a = api.create_test('test_a')
|
||||||
|
test_b = api.create_test('test_b')
|
||||||
|
api.create_test_run(test_a.id, run_a.id, 'success', timestamp_a,
|
||||||
|
timestamp_b)
|
||||||
|
api.create_test_run(test_a.id, run_b.id, 'success', timestamp_a,
|
||||||
|
timestamp_b)
|
||||||
|
api.create_test_run(test_b.id, run_b.id, 'success', timestamp_a,
|
||||||
|
timestamp_b)
|
||||||
|
res = api.get_run_times_all_test_runs(tests=['test_a'])
|
||||||
|
expected_dict = {
|
||||||
|
'test_a': [3, 3],
|
||||||
|
}
|
||||||
|
self.assertEqual(expected_dict, res)
|
||||||
|
|
||||||
def test_get_test_runs_by_run_id(self):
|
def test_get_test_runs_by_run_id(self):
|
||||||
run_b = api.create_run()
|
run_b = api.create_run()
|
||||||
run_a = api.create_run()
|
run_a = api.create_run()
|
||||||
|
Loading…
Reference in New Issue
Block a user