1010"""Implements the ResultCollection, storage for multiple test results."""
1111
1212from collections import defaultdict
13- from typing import Callable , Iterable , Tuple
13+ from typing import Callable , Iterable , Optional , Tuple
1414
1515from bson .objectid import ObjectId
1616from pymongo import DESCENDING
2222from 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 )
0 commit comments