Skip to content

Commit a55f435

Browse files
committed
Update resultssummary to use new resultsstore
1 parent 5591c4f commit a55f435

File tree

15 files changed

+1612
-1492
lines changed

15 files changed

+1612
-1492
lines changed

pyproject.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@ include = '''
55
(
66
^\/python\/moosecontrol\/.*.py$ |
77
^\/python\/TestHarness\/resultsstore\/.*.py$ |
8-
^\/python\/TestHarness/tests/resultsstore\/.*.py$
8+
^\/python\/TestHarness\/resultssummary\/.*.py$ |
9+
^\/python\/TestHarness/tests/resultsstore\/.*.py$ |
10+
^\/python\/TestHarness/tests/resultssummary\/.*.py$
911
)
1012
'''
1113

1214
[tool.ruff]
1315
include = [
1416
"python/moosecontrol/**/*.py",
1517
"python/TestHarness/resultsstore/**/*.py",
16-
"python/TestHarness/tests/resultsstore/**/*.py"
18+
"python/TestHarness/resultssummary/**/*.py",
19+
"python/TestHarness/tests/resultsstore/**/*.py",
20+
"python/TestHarness/tests/resultssummary/**/*.py"
1721
]
1822
exclude = ["python/moosecontrol/requests_unixsocket/*.py"]
1923

python/TestHarness/.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ concurrency = thread
33
branch = True
44
source =
55
resultsstore/*
6+
resultssummary/*
67
omit =
78
resultsstore/*/tests
89
parallel = False

python/TestHarness/pytest.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
addopts =
33
-vvv
44
--cov=TestHarness.resultsstore
5+
--cov=TestHarness.resultssummary
56
--cov-config=.coveragerc
67
--durations=20
78
testpaths =
89
tests/resultsstore
10+
tests/resultssummary
911
markers =
1012
moose: marks tests as requiring moose (use --no-moose to skip)
1113
live_db: marks tests as using a live database (use --no-live-db to skip)

python/TestHarness/resultsstore/reader.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
Authentication,
2121
load_authentication,
2222
)
23-
from TestHarness.resultsstore.resultcollection import ResultCollection
23+
from TestHarness.resultsstore.resultcollection import (
24+
ResultCollection,
25+
ResultsCollection,
26+
)
2427
from TestHarness.resultsstore.storedresult import StoredResult
2528

2629
NoneType = type(None)
@@ -337,7 +340,7 @@ def get_pipeline(batch_size):
337340
self._found_final_push_event = True
338341
return
339342

340-
def get_latest_push_results(self, num: int) -> ResultCollection:
343+
def get_latest_push_results(self, num: int) -> ResultsCollection:
341344
"""Get the latest results from push events, newest to oldest."""
342345
assert isinstance(num, int)
343346
assert num > 0
@@ -348,7 +351,7 @@ def get_latest_push_results(self, num: int) -> ResultCollection:
348351
if len(results) == num:
349352
break
350353

351-
return ResultCollection(results, self.get_database)
354+
return ResultsCollection(results, self.get_database)
352355

353356
def get_cached_result(self, index: str, value) -> Optional[ResultCollection]:
354357
"""Get a result given a filter and store it in the cache."""
@@ -357,7 +360,7 @@ def get_cached_result(self, index: str, value) -> Optional[ResultCollection]:
357360
# Value exists in the cache
358361
cached_value = cache.get(value)
359362
if cached_value is not None:
360-
return ResultCollection([cached_value], self.get_database)
363+
return ResultCollection(cached_value, self.get_database)
361364

362365
# Search for the value
363366
docs = self._find_results({index: {"$eq": value}}, limit=1)
@@ -377,7 +380,7 @@ def get_cached_result(self, index: str, value) -> Optional[ResultCollection]:
377380
# Build it and return
378381
result = self._build_result(data)
379382
cache[value] = result
380-
return ResultCollection([result], self.get_database)
383+
return ResultCollection(result, self.get_database)
381384

382385
def get_event_result(self, event_id: int) -> Optional[ResultCollection]:
383386
"""Get the latest result for the given event, if any."""

python/TestHarness/resultsstore/resultcollection.py

Lines changed: 144 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"""Implements the ResultCollection, storage for multiple test results."""
1111

1212
from collections import defaultdict
13-
from typing import Callable, Iterable, Tuple
13+
from typing import Callable, Iterable, Optional, Tuple
1414

1515
from bson.objectid import ObjectId
1616
from pymongo import DESCENDING
@@ -22,11 +22,13 @@
2222
from TestHarness.resultsstore.utils import TestName, results_test_iterator
2323

2424

25-
class ResultCollection:
25+
class ResultsCollectionBase:
2626
"""
27-
Stores multiple test results and exposes methods to loading tests.
27+
Base for a collection of results.
2828
29-
These objects wrap the results returned by the ResultsReader.
29+
Enables having a collection that contains multiple
30+
results and a collection that has a single result,
31+
with the same underlying getters.
3032
"""
3133

3234
def __init__(
@@ -56,28 +58,22 @@ def __init__(
5658
# Method to call to get the database
5759
self._database_getter = database_getter
5860

59-
@property
60-
def results(self) -> list[StoredResult]:
61-
"""Get the underlying results."""
62-
return self._results
63-
6461
@property
6562
def result_ids(self) -> list[ObjectId]:
6663
"""Get the IDs of the results in the collection."""
67-
return [r.id for r in self.results]
64+
return [r.id for r in self._results]
6865

6966
def get_database(self) -> Database:
7067
"""Get the database."""
7168
return self._database_getter()
7269

73-
def get_all_tests(
70+
def _get_all_tests(
7471
self, filters: Iterable[TestDataFilter]
7572
) -> dict[TestName, list[StoredTestResult]]:
7673
"""
7774
Get all test results.
7875
79-
This can be particularly expensive! Best to do it on a
80-
per-test basis with get_tests() if possible.
76+
Used in derived classes.
8177
8278
Arguments:
8379
---------
@@ -98,7 +94,7 @@ def get_all_tests(
9894

9995
# Load the full documents; this is generally quicker
10096
# than trying to load just the data we need
101-
result_it = iter(self.results)
97+
result_it = iter(self._results)
10298
with self.get_database().results.find(
10399
{"_id": {"$in": self.result_ids}},
104100
{"tests": 1},
@@ -159,12 +155,14 @@ def get_all_tests(
159155

160156
return tests
161157

162-
def get_tests(
158+
def _get_tests(
163159
self, name: TestName, filters: Iterable[TestDataFilter]
164160
) -> list[StoredTestResult]:
165161
"""
166162
Get the results for the test with the given name.
167163
164+
Used in derived classes.
165+
168166
Arguments:
169167
---------
170168
name : TestName
@@ -224,7 +222,7 @@ def get_tests(
224222

225223
# Build test results from documents
226224
tests = []
227-
result_it = iter(self.results)
225+
result_it = iter(self._results)
228226
with self.get_database().results.aggregate(pipeline) as cursor:
229227
for doc in cursor:
230228
id = doc["_id"]
@@ -296,3 +294,133 @@ def get_test_names(self) -> set[TestName]:
296294
)
297295

298296
return names
297+
298+
299+
class ResultCollection(ResultsCollectionBase):
300+
"""Store a single test result and exposes methods to loading tests."""
301+
302+
def __init__(
303+
self,
304+
result: StoredResult,
305+
database_getter: Callable[[], Database],
306+
):
307+
"""
308+
Initialize state.
309+
310+
Arguments:
311+
---------
312+
result : StoredResult
313+
The result in the collection.
314+
database_getter : Callable[[], Database]
315+
Function to be called to get the database.
316+
317+
"""
318+
super().__init__([result], database_getter)
319+
320+
@property
321+
def result(self) -> StoredResult:
322+
"""Get the underlying result."""
323+
assert len(self._results) == 1
324+
return self._results[0]
325+
326+
def get_all_tests(
327+
self, filters: Iterable[TestDataFilter]
328+
) -> dict[TestName, StoredTestResult]:
329+
"""
330+
Get all test results for the result in the collection.
331+
332+
This can be particularly expensive! Best to do it on a
333+
per-test basis with get_test() if possible.
334+
335+
Arguments:
336+
---------
337+
filters : Iterable[TestDataFilter]
338+
The TestDataFilter objects that represent which data
339+
to obtain for the tests.
340+
341+
"""
342+
# Convert a value of list[StoredTestResult] to a single result
343+
return {k: v[0] for k, v in self._get_all_tests(filters).items()}
344+
345+
def get_test(
346+
self, name: TestName, filters: Iterable[TestDataFilter]
347+
) -> Optional[StoredTestResult]:
348+
"""
349+
Get the test result for the given test name, if any.
350+
351+
Arguments:
352+
---------
353+
name : TestName
354+
The name of the test.
355+
filters : Iterable[TestDataFilter]
356+
The TestDataFilter objects that represent which data
357+
to obtain for the tests.
358+
359+
"""
360+
tests = self._get_tests(name, filters)
361+
if tests:
362+
assert len(tests) == 1
363+
return tests[0]
364+
return None
365+
366+
367+
class ResultsCollection(ResultsCollectionBase):
368+
"""Store multiple test results and exposes methods to loading tests."""
369+
370+
def __init__(
371+
self,
372+
results: list[StoredResult],
373+
database_getter: Callable[[], Database],
374+
):
375+
"""
376+
Initialize state.
377+
378+
Arguments:
379+
---------
380+
results : list[StoredResult]
381+
The results in the collection.
382+
database_getter : Callable[[], Database]
383+
Function to be called to get the database.
384+
385+
"""
386+
super().__init__(results, database_getter)
387+
388+
@property
389+
def results(self) -> list[StoredResult]:
390+
"""Get the underlying results."""
391+
return self._results
392+
393+
def get_all_tests(
394+
self, filters: Iterable[TestDataFilter]
395+
) -> dict[TestName, list[StoredTestResult]]:
396+
"""
397+
Get all test results across all results in the collection.
398+
399+
This can be particularly expensive! Best to do it on a
400+
per-test basis with get_tests() if possible.
401+
402+
Arguments:
403+
---------
404+
filters : Iterable[TestDataFilter]
405+
The TestDataFilter objects that represent which data
406+
to obtain for the tests.
407+
408+
"""
409+
return self._get_all_tests(filters)
410+
411+
def get_tests(
412+
self, name: TestName, filters: Iterable[TestDataFilter]
413+
) -> list[StoredTestResult]:
414+
"""
415+
Get the test results for a test across all results in the collection.
416+
417+
Arguments:
418+
---------
419+
name : TestName
420+
The name of the test.
421+
filters : Iterable[TestDataFilter]
422+
The TestDataFilter objects that represent which data
423+
to obtain for the tests.
424+
425+
"""
426+
return self._get_tests(name, filters)
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
#* This file is part of the MOOSE framework
2-
#* https://mooseframework.inl.gov
3-
#*
4-
#* All rights reserved, see COPYRIGHT for full restrictions
5-
#* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6-
#*
7-
#* Licensed under LGPL 2.1, please see LICENSE for details
8-
#* https://www.gnu.org/licenses/lgpl-2.1.html
1+
# This file is part of the MOOSE framework
2+
# https://mooseframework.inl.gov
3+
#
4+
# All rights reserved, see COPYRIGHT for full restrictions
5+
# https://github.com/idaholab/moose/blob/master/COPYRIGHT
6+
#
7+
# Licensed under LGPL 2.1, please see LICENSE for details
8+
# https://www.gnu.org/licenses/lgpl-2.1.html
9+
10+
"""Implement tools for building summaries from result."""

0 commit comments

Comments
 (0)