Skip to content

Commit

Permalink
Merge pull request #7650 from tdesveaux/typing/db/test_results
Browse files Browse the repository at this point in the history
 typing: Add TestResultModel dataclass
  • Loading branch information
p12tic committed May 21, 2024
2 parents eb83ecd + a48ad8a commit 4bb57f3
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 201 deletions.
33 changes: 18 additions & 15 deletions master/buildbot/data/test_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,31 @@
# Copyright Buildbot Team Members


from __future__ import annotations

from typing import TYPE_CHECKING

from twisted.internet import defer

from buildbot.data import base
from buildbot.data import types

if TYPE_CHECKING:
from buildbot.db.test_results import TestResultModel


class Db2DataMixin:
def db2data(self, dbdict):
data = {
'test_resultid': dbdict['id'],
'builderid': dbdict['builderid'],
'test_result_setid': dbdict['test_result_setid'],
'test_name': dbdict['test_name'],
'test_code_path': dbdict['test_code_path'],
'line': dbdict['line'],
'duration_ns': dbdict['duration_ns'],
'value': dbdict['value'],
def db2data(self, model: TestResultModel):
return {
'test_resultid': model.id,
'builderid': model.builderid,
'test_result_setid': model.test_result_setid,
'test_name': model.test_name,
'test_code_path': model.test_code_path,
'line': model.line,
'duration_ns': model.duration_ns,
'value': model.value,
}
return defer.succeed(data)


class TestResultsEndpoint(Db2DataMixin, base.Endpoint):
Expand All @@ -54,10 +60,7 @@ def get(self, resultSpec, kwargs):
set_dbdict['builderid'], kwargs['test_result_setid'], result_spec=resultSpec
)

results = []
for dbdict in result_dbdicts:
results.append((yield self.db2data(dbdict)))
return results
return [self.db2data(result) for result in result_dbdicts]


class TestResult(base.ResourceType):
Expand Down
103 changes: 67 additions & 36 deletions master/buildbot/db/test_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,60 @@
#
# Copyright Buildbot Team Members

from __future__ import annotations

from dataclasses import dataclass

import sqlalchemy as sa
from twisted.internet import defer
from twisted.python import deprecate
from twisted.python import versions

from buildbot.db import base
from buildbot.warnings import warn_deprecated


@dataclass
class TestResultModel:
id: int
builderid: int
test_result_setid: int
test_name: str | None
test_code_path: str | None
line: int | None
duration_ns: int | None
value: str | None

# For backward compatibility
def __getitem__(self, key: str):
warn_deprecated(
'4.1.0',
(
'TestResultsConnectorComponent '
'getTestResult, and getTestResults '
'no longer return TestResult as dictionnaries. '
'Usage of [] accessor is deprecated: please access the member directly'
),
)

if hasattr(self, key):
return getattr(self, key)

raise KeyError(key)


class TestResultDict(dict):
@deprecate.deprecated(versions.Version("buildbot", 4, 1, 0), TestResultModel)
class TestResultDict(TestResultModel):
pass


class TestResultsConnectorComponent(base.DBConnectorComponent):
@defer.inlineCallbacks
def _add_code_paths(self, builderid, paths):
def _add_code_paths(self, builderid: int, paths: set[str]) -> defer.Deferred[dict[str, int]]:
# returns a dictionary of path to id in the test_code_paths table.
# For paths that already exist, the id of the row in the test_code_paths is retrieved.
assert isinstance(paths, set)

def thd(conn):
def thd(conn) -> dict[str, int]:
paths_to_ids = {}
paths_table = self.db.model.test_code_paths

Expand Down Expand Up @@ -77,12 +113,12 @@ def thd(conn):

return paths_to_ids

paths_to_id = yield self.db.pool.do(thd)
return paths_to_id
return self.db.pool.do(thd)

@defer.inlineCallbacks
def getTestCodePaths(self, builderid, path_prefix=None, result_spec=None):
def thd(conn):
def getTestCodePaths(
self, builderid, path_prefix: str | None = None, result_spec=None
) -> defer.Deferred[list[str]]:
def thd(conn) -> list[str]:
paths_table = self.db.model.test_code_paths
q = paths_table.select()
if path_prefix is not None:
Expand All @@ -92,16 +128,14 @@ def thd(conn):
res = conn.execute(q)
return [row['path'] for row in res.fetchall()]

res = yield self.db.pool.do(thd)
return res
return self.db.pool.do(thd)

@defer.inlineCallbacks
def _add_names(self, builderid, names):
def _add_names(self, builderid: int, names: set[str]) -> defer.Deferred[dict[str, int]]:
# returns a dictionary of name to id in the test_names table.
# For names that already exist, the id of the row in the test_names is retrieved.
assert isinstance(names, set)

def thd(conn):
def thd(conn) -> dict[str, int]:
names_to_ids = {}
names_table = self.db.model.test_names

Expand Down Expand Up @@ -147,12 +181,12 @@ def thd(conn):

return names_to_ids

names_to_id = yield self.db.pool.do(thd)
return names_to_id
return self.db.pool.do(thd)

@defer.inlineCallbacks
def getTestNames(self, builderid, name_prefix=None, result_spec=None):
def thd(conn):
def getTestNames(
self, builderid, name_prefix=None, result_spec=None
) -> defer.Deferred[list[str]]:
def thd(conn) -> list[str]:
names_table = self.db.model.test_names
q = names_table.select().where(names_table.c.builderid == builderid)
if name_prefix is not None:
Expand All @@ -162,8 +196,7 @@ def thd(conn):
res = conn.execute(q)
return [row['name'] for row in res.fetchall()]

res = yield self.db.pool.do(thd)
return res
return self.db.pool.do(thd)

@defer.inlineCallbacks
def addTestResults(self, builderid, test_result_setid, result_values):
Expand Down Expand Up @@ -224,9 +257,8 @@ def thd(conn):

yield self.db.pool.do(thd)

@defer.inlineCallbacks
def getTestResult(self, test_resultid):
def thd(conn):
def getTestResult(self, test_resultid: int) -> defer.Deferred[TestResultModel | None]:
def thd(conn) -> TestResultModel | None:
results_table = self.db.model.test_results
code_paths_table = self.db.model.test_code_paths
names_table = self.db.model.test_names
Expand All @@ -240,14 +272,14 @@ def thd(conn):
row = res.fetchone()
if not row:
return None
return self._thd_row2dict(conn, row)
return self._mode_from_row(row)

res = yield self.db.pool.do(thd)
return res
return self.db.pool.do(thd)

@defer.inlineCallbacks
def getTestResults(self, builderid, test_result_setid, result_spec=None):
def thd(conn):
def getTestResults(
self, builderid: int, test_result_setid: int, result_spec=None
) -> defer.Deferred[list[TestResultModel]]:
def thd(conn) -> list[TestResultModel]:
results_table = self.db.model.test_results
code_paths_table = self.db.model.test_code_paths
names_table = self.db.model.test_names
Expand All @@ -274,15 +306,14 @@ def thd(conn):
)

if result_spec is not None:
return result_spec.thd_execute(conn, q, lambda x: self._thd_row2dict(conn, x))
return result_spec.thd_execute(conn, q, self._mode_from_row)
res = conn.execute(q)
return [self._thd_row2dict(conn, row) for row in res.fetchall()]
return [self._mode_from_row(row) for row in res.fetchall()]

res = yield self.db.pool.do(thd)
return res
return self.db.pool.do(thd)

def _thd_row2dict(self, conn, row):
return TestResultDict(
def _mode_from_row(self, row):
return TestResultModel(
id=row.id,
builderid=row.builderid,
test_result_setid=row.test_result_setid,
Expand Down
20 changes: 16 additions & 4 deletions master/buildbot/test/fakedb/test_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
#
# Copyright Buildbot Team Members

from __future__ import annotations

from twisted.internet import defer

from buildbot.db.test_results import TestResultModel
from buildbot.test.fakedb.base import FakeDBComponent
from buildbot.test.fakedb.row import Row

Expand Down Expand Up @@ -211,7 +214,7 @@ def getTestCodePaths(self, builderid, path_prefix=None, result_spec=None):

return defer.succeed(ret)

def _fill_extra_data(self, id, row):
def _model_from_id(self, id: int, row: dict) -> TestResultModel:
row = row.copy()
row['id'] = id

Expand All @@ -227,13 +230,22 @@ def _fill_extra_data(self, id, row):
row['test_code_path'] = None
del row['test_code_pathid']

return row
return TestResultModel(
id=row['id'],
builderid=row['builderid'],
test_result_setid=row['test_result_setid'],
line=row['line'],
duration_ns=row['duration_ns'],
value=row['value'],
test_name=row['test_name'],
test_code_path=row['test_code_path'],
)

# returns a Deferred
def getTestResult(self, test_resultid):
if test_resultid not in self.results:
return defer.succeed(None)
return defer.succeed(self._fill_extra_data(test_resultid, self.results[test_resultid]))
return defer.succeed(self._model_from_id(test_resultid, self.results[test_resultid]))

# returns a Deferred
def getTestResults(self, builderid, test_result_setid, result_spec=None):
Expand All @@ -243,7 +255,7 @@ def getTestResults(self, builderid, test_result_setid, result_spec=None):
continue
if row['test_result_setid'] != test_result_setid:
continue
ret.append(self._fill_extra_data(id, row))
ret.append(self._model_from_id(id, row))

if result_spec is not None:
ret = self.applyResultSpec(ret, result_spec)
Expand Down

0 comments on commit 4bb57f3

Please sign in to comment.