mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-12 13:31:52 +00:00
mount-utils: fix flaky test 'TestFormat'
Signed-off-by: TommyStarK <thomasmilox@gmail.com>
This commit is contained in:
parent
b1f901acf4
commit
1c45bacfb0
@ -649,41 +649,28 @@ func TestCheckUmountError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFormat(t *testing.T) {
|
// TODO https://github.com/kubernetes/kubernetes/pull/117539#discussion_r1181873355
|
||||||
|
func TestFormatConcurrency(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
formatCount = 5
|
formatCount = 5
|
||||||
fstype = "ext4"
|
fstype = "ext4"
|
||||||
output = "complete"
|
output = "complete"
|
||||||
cmdDuration = 1 * time.Millisecond
|
|
||||||
defaultTimeout = 1 * time.Minute
|
defaultTimeout = 1 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
max int
|
max int
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
wantConcurrent int
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
max: 0,
|
max: 2,
|
||||||
wantConcurrent: formatCount,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
max: -1,
|
max: 3,
|
||||||
wantConcurrent: formatCount,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
max: 1,
|
max: 4,
|
||||||
wantConcurrent: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
max: 3,
|
|
||||||
wantConcurrent: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
max: 3,
|
|
||||||
timeout: 1 * time.Nanosecond,
|
|
||||||
wantConcurrent: formatCount,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,20 +680,18 @@ func TestFormat(t *testing.T) {
|
|||||||
tc.timeout = defaultTimeout
|
tc.timeout = defaultTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
var concurrent, maxConcurrent int
|
var concurrent int
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
|
witness := make(chan struct{})
|
||||||
|
|
||||||
exec := &testexec.FakeExec{}
|
exec := &testexec.FakeExec{}
|
||||||
for i := 0; i < formatCount; i++ {
|
for i := 0; i < formatCount; i++ {
|
||||||
exec.CommandScript = append(exec.CommandScript, makeFakeCommandAction(output, nil, func() {
|
exec.CommandScript = append(exec.CommandScript, makeFakeCommandAction(output, nil, func() {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
concurrent++
|
concurrent++
|
||||||
if concurrent > maxConcurrent {
|
|
||||||
maxConcurrent = concurrent
|
|
||||||
}
|
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
|
||||||
time.Sleep(cmdDuration)
|
<-witness
|
||||||
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
concurrent--
|
concurrent--
|
||||||
@ -715,23 +700,111 @@ func TestFormat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
mounter := NewSafeFormatAndMount(nil, exec, WithMaxConcurrentFormat(tc.max, tc.timeout))
|
mounter := NewSafeFormatAndMount(nil, exec, WithMaxConcurrentFormat(tc.max, tc.timeout))
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
// we run max+1 goroutines and block the command execution
|
||||||
for i := 0; i < formatCount; i++ {
|
// only max goroutine should be running and the additional one should wait
|
||||||
wg.Add(1)
|
// for one to be released
|
||||||
|
for i := 0; i < tc.max+1; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
|
||||||
mounter.format(fstype, nil)
|
mounter.format(fstype, nil)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
if maxConcurrent != tc.wantConcurrent {
|
// wait for all goorutines to be scheduled
|
||||||
t.Errorf("SafeFormatAndMount.format() got concurrency: %d, want: %d", maxConcurrent, tc.wantConcurrent)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
if concurrent != tc.max {
|
||||||
|
t.Errorf("SafeFormatAndMount.format() got concurrency: %d, want: %d", concurrent, tc.max)
|
||||||
}
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
// signal the commands to finish the goroutines, this will allow the command
|
||||||
|
// that is pending to be executed
|
||||||
|
for i := 0; i < tc.max; i++ {
|
||||||
|
witness <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for all goroutines to acquire the lock and decrement the counter
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
if concurrent != 1 {
|
||||||
|
t.Errorf("SafeFormatAndMount.format() got concurrency: %d, want: 1", concurrent)
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
// signal the pending command to finish, no more command should be running
|
||||||
|
close(witness)
|
||||||
|
|
||||||
|
// wait a few for the last goroutine to acquire the lock and decrements the counter down to zero
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
if concurrent != 0 {
|
||||||
|
t.Errorf("SafeFormatAndMount.format() got concurrency: %d, want: 0", concurrent)
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO https://github.com/kubernetes/kubernetes/pull/117539#discussion_r1181873355
|
||||||
|
func TestFormatTimeout(t *testing.T) {
|
||||||
|
const (
|
||||||
|
formatCount = 5
|
||||||
|
fstype = "ext4"
|
||||||
|
output = "complete"
|
||||||
|
maxConcurrency = 4
|
||||||
|
timeout = 200 * time.Millisecond
|
||||||
|
)
|
||||||
|
|
||||||
|
var concurrent int
|
||||||
|
var mu sync.Mutex
|
||||||
|
witness := make(chan struct{})
|
||||||
|
|
||||||
|
exec := &testexec.FakeExec{}
|
||||||
|
for i := 0; i < formatCount; i++ {
|
||||||
|
exec.CommandScript = append(exec.CommandScript, makeFakeCommandAction(output, nil, func() {
|
||||||
|
mu.Lock()
|
||||||
|
concurrent++
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
<-witness
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
concurrent--
|
||||||
|
mu.Unlock()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
mounter := NewSafeFormatAndMount(nil, exec, WithMaxConcurrentFormat(maxConcurrency, timeout))
|
||||||
|
|
||||||
|
for i := 0; i < maxConcurrency+1; i++ {
|
||||||
|
go func() {
|
||||||
|
mounter.format(fstype, nil)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait a bit more than the configured timeout
|
||||||
|
time.Sleep(timeout + 100*time.Millisecond)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
if concurrent != maxConcurrency+1 {
|
||||||
|
t.Errorf("SafeFormatAndMount.format() got concurrency: %d, want: %d", concurrent, maxConcurrency+1)
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
// signal the pending commands to finish
|
||||||
|
close(witness)
|
||||||
|
// wait for all goroutines to acquire the lock and decrement the counter
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
if concurrent != 0 {
|
||||||
|
t.Errorf("SafeFormatAndMount.format() got concurrency: %d, want: 0", concurrent)
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func makeFakeCommandAction(stdout string, err error, cmdFn func()) testexec.FakeCommandAction {
|
func makeFakeCommandAction(stdout string, err error, cmdFn func()) testexec.FakeCommandAction {
|
||||||
c := testexec.FakeCmd{
|
c := testexec.FakeCmd{
|
||||||
CombinedOutputScript: []testexec.FakeAction{
|
CombinedOutputScript: []testexec.FakeAction{
|
||||||
|
Loading…
Reference in New Issue
Block a user