Test caching with same function appearing multiple times (recursion).
(self)
| 3102 | "Test only runs on Linux with process_vm_readv support", |
| 3103 | ) |
| 3104 | def test_recursive_frames(self): |
| 3105 | """Test caching with same function appearing multiple times (recursion).""" |
| 3106 | script_body = """\ |
| 3107 | def recurse(n): |
| 3108 | if n <= 0: |
| 3109 | sock.sendall(b"sync1") |
| 3110 | sock.recv(16) |
| 3111 | sock.sendall(b"sync2") |
| 3112 | sock.recv(16) |
| 3113 | else: |
| 3114 | recurse(n - 1) |
| 3115 | |
| 3116 | recurse(5) |
| 3117 | """ |
| 3118 | |
| 3119 | with self._target_process(script_body) as ( |
| 3120 | p, |
| 3121 | client_socket, |
| 3122 | make_unwinder, |
| 3123 | ): |
| 3124 | unwinder = make_unwinder(cache_frames=True) |
| 3125 | |
| 3126 | frames1 = self._sample_frames( |
| 3127 | client_socket, unwinder, b"sync1", b"ack", {"recurse"} |
| 3128 | ) |
| 3129 | frames2 = self._sample_frames( |
| 3130 | client_socket, unwinder, b"sync2", b"done", {"recurse"} |
| 3131 | ) |
| 3132 | |
| 3133 | self.assertIsNotNone(frames1) |
| 3134 | self.assertIsNotNone(frames2) |
| 3135 | |
| 3136 | # Should have multiple "recurse" frames (6 total: recurse(5) down to recurse(0)) |
| 3137 | recurse_count = sum(1 for f in frames1 if f.funcname == "recurse") |
| 3138 | self.assertEqual(recurse_count, 6, "Should have 6 recursive frames") |
| 3139 | |
| 3140 | self.assertEqual(len(frames1), len(frames2)) |
| 3141 | |
| 3142 | # Current frame (index 0) is re-read, check value equality |
| 3143 | self.assertEqual(frames1[0].funcname, frames2[0].funcname) |
| 3144 | |
| 3145 | # Parent frames (index 1+) should be identical objects (cache reuse) |
| 3146 | for i in range(1, len(frames1)): |
| 3147 | self.assertIs( |
| 3148 | frames1[i], |
| 3149 | frames2[i], |
| 3150 | f"Frame {i}: recursive frames must be same object", |
| 3151 | ) |
| 3152 | |
| 3153 | @skip_if_not_supported |
| 3154 | @unittest.skipIf( |
nothing calls this directly
no test coverage detected