Merge "Add API methods for getting tests by prefix"
This commit is contained in:
commit
5201d60a09
@ -464,6 +464,148 @@ def get_all_tests(session=None):
|
|||||||
return query.all()
|
return query.all()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_test_prefixes_mysql(session):
|
||||||
|
query = session.query(
|
||||||
|
sqlalchemy.func.substring_index(models.Test.test_id, '.', 1))
|
||||||
|
|
||||||
|
prefixes = set()
|
||||||
|
for prefix in query.distinct().all():
|
||||||
|
prefix = prefix[0]
|
||||||
|
|
||||||
|
# strip out any wrapped function names, e.g. 'setUpClass (
|
||||||
|
if '(' in prefix:
|
||||||
|
prefix = prefix.split('(', 1)[1]
|
||||||
|
|
||||||
|
prefixes.add(prefix)
|
||||||
|
|
||||||
|
return list(prefixes)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_test_prefixes_other(session):
|
||||||
|
query = session.query(models.Test.test_id)
|
||||||
|
|
||||||
|
unique = set()
|
||||||
|
for test_id in query:
|
||||||
|
# get the first '.'-separated token (possibly including 'setUpClass (')
|
||||||
|
prefix = test_id[0].split('.', 1)[0]
|
||||||
|
if '(' in prefix:
|
||||||
|
# strip out the function name and paren, e.g. 'setUpClass(a' -> 'a'
|
||||||
|
prefix = prefix.split('(', 1)[1]
|
||||||
|
|
||||||
|
unique.add(prefix)
|
||||||
|
|
||||||
|
return list(unique)
|
||||||
|
|
||||||
|
|
||||||
|
def get_test_prefixes(session=None):
|
||||||
|
"""Returns all test prefixes from the DB.
|
||||||
|
|
||||||
|
This returns a list of unique test_id prefixes from the database, defined
|
||||||
|
as the first dot-separated token in the test id. Prefixes wrapped in
|
||||||
|
function syntax, such as 'setUpClass (a', will have this extra syntax
|
||||||
|
stripped out of the returned value, up to and including the '(' character.
|
||||||
|
|
||||||
|
As an example, given an input test with an ID 'prefix.test.Clazz.a_method',
|
||||||
|
the derived prefix would be 'prefix'. Given a second test with an ID
|
||||||
|
'setUpClass (prefix.test.Clazz)', the derived prefix would also be
|
||||||
|
'prefix'. If this function were called on a database containing only these
|
||||||
|
tests, a list with only one entry, 'prefix', would be returned.
|
||||||
|
|
||||||
|
Note that this implementation assumes that tests ids are semantically
|
||||||
|
separated by a period. If this is not the case (and no period characters
|
||||||
|
occur at any position within test ids), the full test id will be considered
|
||||||
|
the prefix, and the result of this function will be all unique test ids in
|
||||||
|
the database.
|
||||||
|
|
||||||
|
:param session: optional session object if one isn't provided a new session
|
||||||
|
will be acquired for the duration of this operation
|
||||||
|
:return list: a list of all unique prefix strings, with any extraneous
|
||||||
|
details removed, e.g. 'setUpClass ('.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
session = session or get_session()
|
||||||
|
|
||||||
|
backend = session.bind.dialect.name
|
||||||
|
if backend == 'mysql':
|
||||||
|
return _get_test_prefixes_mysql(session)
|
||||||
|
else:
|
||||||
|
return _get_test_prefixes_other(session)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tests_by_prefix_mysql(prefix, session, limit, offset):
|
||||||
|
# use mysql's substring_index to pull the prefix out of the full test_id
|
||||||
|
func_filter = sqlalchemy.func.substring_index(models.Test.test_id, '.', 1)
|
||||||
|
|
||||||
|
# query for tests against the prefix token, but use an ends-with compare
|
||||||
|
# this way, if a test_id has a function call, e.g. 'setUpClass (a.b..c)' we
|
||||||
|
# can still match it here
|
||||||
|
# (we use an ugly 'like' query here, but this won't be operating on an
|
||||||
|
# index regardless)
|
||||||
|
query = db_utils.model_query(models.Test, session).filter(
|
||||||
|
func_filter.like('%' + prefix)).order_by(models.Test.test_id.asc())
|
||||||
|
|
||||||
|
return query.limit(limit).offset(offset).all()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tests_by_prefix_other(prefix, session, limit, offset):
|
||||||
|
query = db_utils.model_query(models.Test, session).order_by(
|
||||||
|
models.Test.test_id.asc())
|
||||||
|
|
||||||
|
# counter to track progress toward offset
|
||||||
|
skipped = 0
|
||||||
|
|
||||||
|
ret = []
|
||||||
|
for test in query:
|
||||||
|
test_prefix = test.test_id.split('.', 1)[0]
|
||||||
|
# compare via endswith to match wrapped test_ids: given
|
||||||
|
# 'setUpClass (a.b.c)', the first token will be 'setUpClass (a',
|
||||||
|
# which endswith() will catch
|
||||||
|
if test_prefix.endswith(prefix):
|
||||||
|
# manually track offset progress since we aren't checking for
|
||||||
|
# matches on the database-side
|
||||||
|
if offset > 0 and skipped < offset:
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
ret.append(test)
|
||||||
|
|
||||||
|
if len(ret) >= limit:
|
||||||
|
break
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def get_tests_by_prefix(prefix, session=None, limit=100, offset=0):
|
||||||
|
"""Returns all tests with the given prefix in the DB.
|
||||||
|
|
||||||
|
A test prefix is the first segment of a test_id when split using a period
|
||||||
|
('.'). This function will return a list of tests whose first
|
||||||
|
period-separated token ends with the specified prefix. As a side-effect,
|
||||||
|
given an input 'a', this will return tests with prefixes 'a', but also
|
||||||
|
prefixes wrapped in function syntax, such as 'setUpClass (a'.
|
||||||
|
|
||||||
|
Note that this implementation assumes that tests ids are semantically
|
||||||
|
separated by a period. If no period character exists in a test id, its
|
||||||
|
prefix will be considered the full test id, and this method may return
|
||||||
|
unexpected results.
|
||||||
|
|
||||||
|
:param str prefix: the test prefix to search for
|
||||||
|
:param session: optional session object: if one isn't provided, a new
|
||||||
|
session will be acquired for the duration of this operation
|
||||||
|
:param int limit: the maximum number of results to return
|
||||||
|
:param int offset: the starting index, for pagination purposes
|
||||||
|
:return list: the list of matching test objects, ordered by their test id
|
||||||
|
:rtype: subunit2sql.models.Test
|
||||||
|
"""
|
||||||
|
session = session or get_session()
|
||||||
|
|
||||||
|
backend = session.bind.dialect.name
|
||||||
|
if backend == 'mysql':
|
||||||
|
return _get_tests_by_prefix_mysql(prefix, session, limit, offset)
|
||||||
|
else:
|
||||||
|
return _get_tests_by_prefix_other(prefix, session, limit, offset)
|
||||||
|
|
||||||
|
|
||||||
def get_all_runs_by_date(start_date=None, stop_date=None, session=None):
|
def get_all_runs_by_date(start_date=None, stop_date=None, session=None):
|
||||||
"""Return all runs from the DB.
|
"""Return all runs from the DB.
|
||||||
|
|
||||||
|
@ -669,3 +669,40 @@ class TestDatabaseAPI(base.TestCase):
|
|||||||
fail_rate = api.get_run_failure_rate_by_key_value_metadata(
|
fail_rate = api.get_run_failure_rate_by_key_value_metadata(
|
||||||
'a_key', 'a_value')
|
'a_key', 'a_value')
|
||||||
self.assertEqual(50, fail_rate)
|
self.assertEqual(50, fail_rate)
|
||||||
|
|
||||||
|
def test_get_test_prefixes(self):
|
||||||
|
api.create_test('prefix.token.token')
|
||||||
|
api.create_test('setUpClass (prefix.token.token)')
|
||||||
|
api.create_test('other.token.token')
|
||||||
|
api.create_test('justonetoken')
|
||||||
|
|
||||||
|
prefixes = api.get_test_prefixes()
|
||||||
|
self.assertEqual(len(prefixes), 3)
|
||||||
|
self.assertIn('prefix', prefixes)
|
||||||
|
self.assertIn('other', prefixes)
|
||||||
|
self.assertIn('justonetoken', prefixes)
|
||||||
|
|
||||||
|
def test_get_tests_by_prefix(self):
|
||||||
|
api.create_test('prefix.token.token')
|
||||||
|
api.create_test('setUpClass (prefix.token.token)')
|
||||||
|
api.create_test('other.token.token')
|
||||||
|
api.create_test('justonetoken')
|
||||||
|
|
||||||
|
tests = api.get_tests_by_prefix('prefix')
|
||||||
|
self.assertEqual(len(tests), 2)
|
||||||
|
self.assertIn('prefix.token.token', [test.test_id for test in tests])
|
||||||
|
self.assertIn('setUpClass (prefix.token.token)',
|
||||||
|
[test.test_id for test in tests])
|
||||||
|
|
||||||
|
tests = api.get_tests_by_prefix('other')
|
||||||
|
self.assertEqual(len(tests), 1)
|
||||||
|
self.assertIn('other.token.token', [test.test_id for test in tests])
|
||||||
|
|
||||||
|
tests = api.get_tests_by_prefix('prefix', limit=1, offset=1)
|
||||||
|
self.assertEqual(len(tests), 1)
|
||||||
|
self.assertIn('setUpClass (prefix.token.token)',
|
||||||
|
[test.test_id for test in tests])
|
||||||
|
|
||||||
|
tests = api.get_tests_by_prefix('justonetoken')
|
||||||
|
self.assertEqual(len(tests), 1)
|
||||||
|
self.assertIn('justonetoken', [test.test_id for test in tests])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user