2025-12-03 19:29:37 +00:00
|
|
|
package testutils
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"sync"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
parallelLock sync.Mutex
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// SetupParallelTest configure un test pour exécution parallèle (T0048)
|
|
|
|
|
func SetupParallelTest(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
// Acquérir un lock si ressources partagées
|
|
|
|
|
// parallelLock.Lock()
|
|
|
|
|
// t.Cleanup(func() { parallelLock.Unlock() })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RunParallelTests exécute plusieurs tests en parallèle (T0048)
|
2025-12-16 16:23:49 +00:00
|
|
|
// Note: The sub-tests created by t.Run() already call t.Parallel(), so testFuncs
|
|
|
|
|
// should NOT call SetupParallelTest() or t.Parallel() themselves to avoid "t.Parallel called multiple times" panic
|
|
|
|
|
// The parent test must wait for all sub-tests to complete
|
2025-12-03 19:29:37 +00:00
|
|
|
func RunParallelTests(t *testing.T, testFuncs map[string]func(*testing.T)) {
|
2025-12-16 16:23:49 +00:00
|
|
|
// Use t.Run() which automatically waits for all sub-tests to complete
|
|
|
|
|
// Each sub-test calls t.Parallel() to run in parallel
|
2025-12-03 19:29:37 +00:00
|
|
|
for name, fn := range testFuncs {
|
2025-12-16 16:23:49 +00:00
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
fn(t)
|
|
|
|
|
})
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
2025-12-16 16:23:49 +00:00
|
|
|
// t.Run() blocks until all sub-tests complete, so we don't need WaitGroup
|
2025-12-03 19:29:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WithLock exécute une fonction avec un lock partagé (T0048)
|
|
|
|
|
func WithLock(fn func()) {
|
|
|
|
|
parallelLock.Lock()
|
|
|
|
|
defer parallelLock.Unlock()
|
|
|
|
|
fn()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestLockManager gère les locks pour les tests parallèles (T0048)
|
|
|
|
|
type TestLockManager struct {
|
|
|
|
|
locks map[string]*sync.Mutex
|
|
|
|
|
mu sync.RWMutex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewTestLockManager crée un nouveau gestionnaire de locks (T0048)
|
|
|
|
|
func NewTestLockManager() *TestLockManager {
|
|
|
|
|
return &TestLockManager{
|
|
|
|
|
locks: make(map[string]*sync.Mutex),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Lock acquiert un lock nommé (T0048)
|
|
|
|
|
func (tm *TestLockManager) Lock(name string) func() {
|
|
|
|
|
tm.mu.Lock()
|
|
|
|
|
lock, exists := tm.locks[name]
|
|
|
|
|
if !exists {
|
|
|
|
|
lock = &sync.Mutex{}
|
|
|
|
|
tm.locks[name] = lock
|
|
|
|
|
}
|
|
|
|
|
tm.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
lock.Lock()
|
|
|
|
|
return func() {
|
|
|
|
|
lock.Unlock()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Example usage:
|
|
|
|
|
/*
|
|
|
|
|
func TestParallel(t *testing.T) {
|
|
|
|
|
testFuncs := map[string]func(*testing.T){
|
|
|
|
|
"test1": func(t *testing.T) {
|
|
|
|
|
SetupParallelTest(t)
|
|
|
|
|
// Test code
|
|
|
|
|
},
|
|
|
|
|
"test2": func(t *testing.T) {
|
|
|
|
|
SetupParallelTest(t)
|
|
|
|
|
// Test code
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RunParallelTests(t, testFuncs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestWithSharedResource(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
WithLock(func() {
|
|
|
|
|
// Code qui nécessite un lock
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestWithNamedLock(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
lockManager := NewTestLockManager()
|
|
|
|
|
unlock := lockManager.Lock("resource1")
|
|
|
|
|
defer unlock()
|
|
|
|
|
|
|
|
|
|
// Code qui nécessite un lock nommé
|
|
|
|
|
}
|
|
|
|
|
*/
|