| 90 | } |
| 91 | |
| 92 | func Test_App_Test_Goroutine_Leak_Compare(t *testing.T) { |
| 93 | // Not parallel: this test relies on runtime.NumGoroutine() deltas, |
| 94 | // which are unreliable when hundreds of other tests spawn goroutines |
| 95 | // concurrently. |
| 96 | |
| 97 | testCases := []struct { |
| 98 | handler Handler |
| 99 | name string |
| 100 | timeout time.Duration |
| 101 | sleepTime time.Duration |
| 102 | expectLeak bool |
| 103 | }{ |
| 104 | { |
| 105 | name: "With timeout (potential leak)", |
| 106 | handler: func(c Ctx) error { |
| 107 | time.Sleep(300 * time.Millisecond) // Simulate time-consuming operation |
| 108 | return c.SendString("ok") |
| 109 | }, |
| 110 | timeout: 50 * time.Millisecond, // // Short timeout to ensure triggering |
| 111 | sleepTime: 500 * time.Millisecond, // Wait time longer than handler execution time |
| 112 | expectLeak: true, |
| 113 | }, |
| 114 | { |
| 115 | name: "Without timeout (no leak)", |
| 116 | handler: func(c Ctx) error { |
| 117 | return c.SendString("ok") // Return immediately |
| 118 | }, |
| 119 | timeout: 0, // Disable timeout |
| 120 | sleepTime: 100 * time.Millisecond, |
| 121 | expectLeak: false, |
| 122 | }, |
| 123 | } |
| 124 | |
| 125 | for _, tc := range testCases { |
| 126 | t.Run(tc.name, func(t *testing.T) { |
| 127 | app := New() |
| 128 | |
| 129 | // Record initial goroutine count |
| 130 | initialGoroutines := runtime.NumGoroutine() |
| 131 | t.Logf("[%s] Initial goroutines: %d", tc.name, initialGoroutines) |
| 132 | |
| 133 | app.Get("/", tc.handler) |
| 134 | |
| 135 | // Send 10 requests |
| 136 | numRequests := 10 |
| 137 | for range numRequests { |
| 138 | req := httptest.NewRequest(MethodGet, "/", http.NoBody) |
| 139 | |
| 140 | if tc.timeout > 0 { |
| 141 | _, err := app.Test(req, TestConfig{ |
| 142 | Timeout: tc.timeout, |
| 143 | FailOnTimeout: true, |
| 144 | }) |
| 145 | require.Error(t, err) |
| 146 | require.ErrorIs(t, err, os.ErrDeadlineExceeded) |
| 147 | } else if resp, err := app.Test(req); err != nil { |
| 148 | t.Errorf("unexpected error: %v", err) |
| 149 | } else { |