@@ -250,6 +250,36 @@ def cpp_exception_state_present() -> bool:
250250 return True
251251
252252
253+ def gtest_libraries_present () -> bool :
254+ """
255+ Is this program linked against gtest libraries?
256+ """
257+ obj_paths = [Path (o .filename ) for o in gdb .objfiles () if o .filename is not None ]
258+ return any (o .name .startswith ("libgtest" ) for o in obj_paths )
259+
260+
261+ class GTestNotAvailable (Exception ):
262+ """
263+ Raised by GTest-specific tools if the Google Test libraries are not present.
264+ """
265+
266+ def __init__ (self ):
267+ super ().__init__ ("Tool unavailable: This program was not run with gtest." )
268+
269+
270+ class GTestAnnotationsNotAvailable (Exception ):
271+ """
272+ Raised by GTest-specific tools if our Google Test annotations are not found.
273+ """
274+
275+ def __init__ (self ):
276+ super ().__init__ (
277+ "Tool unavailable: Did not find gtest annotations, which are required to navigate "
278+ "Google Test recordings. Maybe the program was built without the "
279+ "Undo's `undo_gtest_annotation.h` addon?"
280+ )
281+
282+
253283class UdbMcpGateway :
254284 """
255285 Plumbing class to expose selected UDB functionality as a set of tools to an MCP server.
@@ -646,6 +676,55 @@ def tool_ugo_bookmark(self, name) -> None:
646676 """
647677 self .udb .bookmarks .goto (name )
648678
679+ @report
680+ @chain_of_thought
681+ def tool_gtest_get_tests (self ) -> list [tuple [str , str ]]:
682+ """
683+ Retrieve a list of GTest tests captured in this recording, along with their results.
684+
685+ Full test names are returned in the form:
686+
687+ <test suite name>.<test name>/run<run number>
688+
689+ Each instance of a test is assigned a unique run number. Run numbers don't have meaning
690+ beyond being a unique suffix.
691+
692+ Returns a list of (<full test name>, <result>) tuples.
693+ """
694+ if not gtest_libraries_present ():
695+ raise GTestNotAvailable ()
696+
697+ results = list (self .udb .annotations .get ("" , "u-test-result" ))
698+ if not results :
699+ raise GTestAnnotationsNotAvailable ()
700+
701+ return [(r .name , r .get_content_as_printable_text ()) for r in results ]
702+
703+ @report
704+ @source_context
705+ @chain_of_thought
706+ def tool_gtest_goto_test (self , name ) -> str :
707+ """
708+ Move to the end of the specified Gtest test case.
709+ """
710+ if not gtest_libraries_present ():
711+ raise GTestNotAvailable ()
712+
713+ if not list (self .udb .annotations .get ("" , "u-test-result" )):
714+ raise GTestAnnotationsNotAvailable ()
715+
716+ results = list (self .udb .annotations .get (name , "u-test-result" ))
717+ if len (results ) != 1 :
718+ raise Exception ("Must specify a unique, existing test identifier." )
719+
720+ annotation = results [0 ]
721+ self .udb .time .goto (annotation .bbcount )
722+
723+ test_name , _ = annotation .name .split ("/run" )
724+ target_fn = test_name .replace ("." , "_" ) + "_Test::TestBody"
725+
726+ return self ._reverse_into_target_function (target_fn )
727+
649728
650729command .register_prefix (
651730 "uexperimental mcp" ,
0 commit comments