TestConcurrentRouter_ParametersNoRace verifies that accessing RouteInfo.Parameters while routes are being added concurrently doesn't cause data races. This test validates that Routes() deep-copies RouteInfo, not just the Routes slice.
(t *testing.T)
| 293 | // while routes are being added concurrently doesn't cause data races. |
| 294 | // This test validates that Routes() deep-copies RouteInfo, not just the Routes slice. |
| 295 | func TestConcurrentRouter_ParametersNoRace(t *testing.T) { |
| 296 | router := NewConcurrentRouter(NewRouter(RouterConfig{})) |
| 297 | |
| 298 | // Add routes with parameters |
| 299 | _, err := router.Add(Route{ |
| 300 | Method: http.MethodGet, |
| 301 | Path: "/users/:id/:name", |
| 302 | Handler: handlerFunc, |
| 303 | }) |
| 304 | assert.NoError(t, err) |
| 305 | |
| 306 | _, err = router.Add(Route{ |
| 307 | Method: http.MethodPost, |
| 308 | Path: "/posts/:postId/comments/:commentId", |
| 309 | Handler: handlerFunc, |
| 310 | }) |
| 311 | assert.NoError(t, err) |
| 312 | |
| 313 | var wg sync.WaitGroup |
| 314 | var paramsAccessCount atomic.Int64 |
| 315 | var addCount atomic.Int64 |
| 316 | |
| 317 | // Launch 3 goroutines that read Parameters repeatedly |
| 318 | for i := range 3 { |
| 319 | wg.Add(1) |
| 320 | go func(goroutineID int) { |
| 321 | defer wg.Done() |
| 322 | for range 100 { |
| 323 | routes := router.Routes() |
| 324 | // Actually access the Parameters slice data |
| 325 | // This would cause a data race if Parameters weren't deep-copied |
| 326 | for _, r := range routes { |
| 327 | for _, p := range r.Parameters { |
| 328 | _ = len(p) // Read parameter name length |
| 329 | if len(p) > 0 { // Read first character |
| 330 | _ = p[0] |
| 331 | } |
| 332 | } |
| 333 | paramsAccessCount.Add(int64(len(r.Parameters))) |
| 334 | } |
| 335 | } |
| 336 | }(i) |
| 337 | } |
| 338 | |
| 339 | // Launch 2 goroutines that add routes with parameters concurrently |
| 340 | for i := range 2 { |
| 341 | wg.Add(1) |
| 342 | go func(goroutineID int) { |
| 343 | defer wg.Done() |
| 344 | for j := range 20 { |
| 345 | path := fmt.Sprintf("/api/:v%d/resource/:id", goroutineID*100+j) |
| 346 | _, err := router.Add(Route{ |
| 347 | Method: http.MethodPost, |
| 348 | Path: path, |
| 349 | Handler: handlerFunc, |
| 350 | }) |
| 351 | if err == nil { |
| 352 | addCount.Add(1) |
nothing calls this directly
no test coverage detected
searching dependent graphs…