Add migration to populate run_time where needed

The existing migration to add an avg run_time column to the tests table
neglected to calculate the run_time for existing rows and instead just
added a NULL value. Since this migration is part of a released version
of subunit2sql it's too late to add support for adding a run_time to
the columns where it makes sense. This commit adds a new migration
that will add the avg run_time for each test in the tests table where
there are successful test runs and the run_time is still set to NULL.

Additionally there were a couple of fixes around adding migrations
that needed to be added here, mostly just changing the base
alembic.ini to reflect the new relative path of the migration dir and
ignoring an additional pep8 rule which conflicted with the sqlalchemy
syntax.

Change-Id: I0b9c673106c41e9e66a2be5da6ebe9ca58d0ad36
This commit is contained in:
Matthew Treinish 2014-10-04 00:07:02 -04:00
parent 0a2fb7b7ac
commit 7948c0e026
4 changed files with 109 additions and 2 deletions

View File

@ -2,7 +2,7 @@
[alembic]
# path to migration scripts
script_location = subunit2sql/migrations
script_location = .
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s

View File

@ -0,0 +1,57 @@
# Copyright 2014 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.
"""Populate run_time for existing tests
Revision ID: 5332fe255095
Revises: 28ac1ba9c3db
Create Date: 2014-10-03 10:23:25.469128
"""
# revision identifiers, used by Alembic.
revision = '5332fe255095'
down_revision = '28ac1ba9c3db'
from oslo.db.sqlalchemy import utils as db_utils
from subunit2sql.db import api as db_api
from subunit2sql.db import models
def upgrade():
query = db_utils.model_query(models.Test,
db_api.get_session()).filter(
models.Test.success > 0,
models.Test.run_time == None)
tests = query.all()
for test in tests:
test_runs = db_api.get_test_runs_by_test_id(test.id)
duration = 0.0
for test_run in test_runs:
if test_run.status == 'success':
test_run_duration = db_api.get_test_run_duration(test_run.id)
duration = duration + test_run_duration
avg = duration / test.success
db_api.update_test({'run_time': avg}, test.id)
def downgrade():
# NOTE(mtreinish) there is no possible downgrade for this migration, since
# we won't be able to tell which rows had run_time NULL before this.
# Ideally this would have been baked into 163fd5aa1380 and the downgrade
# there of deleting the column would have covered this. But, because that
# wasn't included as a part of the released migration we can't change it.
pass

View File

@ -443,3 +443,52 @@ class TestWalkMigrations(base.TestCase):
for run in data:
self.assertIn((run['id'], None), run_at)
self.assertIn((time_data['id'], now), run_at)
def _pre_upgrade_5332fe255095(self, engine):
tests = get_table(engine, 'tests')
test_runs = get_table(engine, 'test_runs')
# Create 2 sample rows one for a passing test the other failing
fake_tests = {'pass': {'id': 'fake_null_test_id',
'test_id': 'I_am_a_little_test_that_works',
'success': 2,
'failure': 0},
'fail': {'id': 'fake_null_test_id_fails',
'test_id': 'Im_a_little_test_that_doesnt_work',
'success': 0,
'failure': 1}}
now = datetime.datetime.now()
future_now = now + datetime.timedelta(0, 4)
# Create sample rows for the test_runs corresponding to the test rows
fake_test_runs = {'pass': [
{'id': 'fake_test_run_pass_1', 'test_id': 'fake_null_test_id',
'run_id': 'fake_run.id1', 'start_time': now, 'status': 'success',
'stop_time': future_now},
{'id': 'fake_test_run_pass_2', 'test_id': 'fake_null_test_id',
'run_id': 'fake_run.id2', 'start_time': now, 'status': 'success',
'stop_time': future_now}]}
fake_test_runs['fail'] = {'id': 'fake_test_run_fail',
'test_id': 'fake_null_test_id_fails',
'run_id': 'fake_run.id1',
'start_time': now,
'status': 'fail',
'stop_time': future_now}
for test in fake_tests:
tests.insert().values(fake_tests[test]).execute()
for test_run in fake_test_runs['pass']:
test_runs.insert().values(test_run).execute()
test_runs.insert().values(fake_test_runs['fail']).execute()
return {'tests': fake_tests, 'test_runs': fake_test_runs}
def _check_5332fe255095(self, engine, data):
tests = get_table(engine, 'tests')
# Get the test uuids from the same data set
test_ids = [data['tests'][x]['id'] for x in data['tests']]
# Query the DB for the tests from the sample dataset above
where = ' OR '.join(["tests.id='%s'" % x for x in test_ids])
result = tests.select(where).execute()
run_time_pairs = map(lambda x: (x['id'], x['run_time']), result)
# Ensure the test with one failure is None
self.assertIn(('fake_null_test_id_fails', None), run_time_pairs)
# Ensure the test with 2 success each taking 4 sec lists the proper
# run_time
self.assertIn(('fake_null_test_id', 4.0), run_time_pairs)

View File

@ -35,5 +35,6 @@ commands = python setup.py build_sphinx
# E123 skipped because it is ignored by default in the default pep8
# E129 skipped because it is too limiting when combined with other rules
# H305 skipped because it is inconsistent between python versions
ignore = E125,H402,E123,E129,H305
# E711 skipped because sqlalchemy filter() requires using == instead of is
ignore = E125,H402,E123,E129,H305,E711
exclude = .venv,.git,.tox,dist,doc,*egg,build