123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- package container // import "github.com/docker/docker/container"
- import (
- "context"
- "testing"
- "time"
- "github.com/docker/docker/api/types"
- )
- func TestIsValidHealthString(t *testing.T) {
- contexts := []struct {
- Health string
- Expected bool
- }{
- {types.Healthy, true},
- {types.Unhealthy, true},
- {types.Starting, true},
- {types.NoHealthcheck, true},
- {"fail", false},
- }
- for _, c := range contexts {
- v := IsValidHealthString(c.Health)
- if v != c.Expected {
- t.Fatalf("Expected %t, but got %t", c.Expected, v)
- }
- }
- }
- func TestStateRunStop(t *testing.T) {
- s := NewState()
- // Begin another wait with WaitConditionRemoved. It should complete
- // within 200 milliseconds.
- ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
- defer cancel()
- removalWait := s.Wait(ctx, WaitConditionRemoved)
- // Full lifecycle two times.
- for i := 1; i <= 2; i++ {
- // A wait with WaitConditionNotRunning should return
- // immediately since the state is now either "created" (on the
- // first iteration) or "exited" (on the second iteration). It
- // shouldn't take more than 50 milliseconds.
- ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
- defer cancel()
- // Expectx exit code to be i-1 since it should be the exit
- // code from the previous loop or 0 for the created state.
- if status := <-s.Wait(ctx, WaitConditionNotRunning); status.ExitCode() != i-1 {
- t.Fatalf("ExitCode %v, expected %v, err %q", status.ExitCode(), i-1, status.Err())
- }
- // A wait with WaitConditionNextExit should block until the
- // container has started and exited. It shouldn't take more
- // than 100 milliseconds.
- ctx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond)
- defer cancel()
- initialWait := s.Wait(ctx, WaitConditionNextExit)
- // Set the state to "Running".
- s.Lock()
- s.SetRunning(i, true)
- s.Unlock()
- // Assert desired state.
- if !s.IsRunning() {
- t.Fatal("State not running")
- }
- if s.Pid != i {
- t.Fatalf("Pid %v, expected %v", s.Pid, i)
- }
- if s.ExitCode() != 0 {
- t.Fatalf("ExitCode %v, expected 0", s.ExitCode())
- }
- // Now that it's running, a wait with WaitConditionNotRunning
- // should block until we stop the container. It shouldn't take
- // more than 100 milliseconds.
- ctx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond)
- defer cancel()
- exitWait := s.Wait(ctx, WaitConditionNotRunning)
- // Set the state to "Exited".
- s.Lock()
- s.SetStopped(&ExitStatus{ExitCode: i})
- s.Unlock()
- // Assert desired state.
- if s.IsRunning() {
- t.Fatal("State is running")
- }
- if s.ExitCode() != i {
- t.Fatalf("ExitCode %v, expected %v", s.ExitCode(), i)
- }
- if s.Pid != 0 {
- t.Fatalf("Pid %v, expected 0", s.Pid)
- }
- // Receive the initialWait result.
- if status := <-initialWait; status.ExitCode() != i {
- t.Fatalf("ExitCode %v, expected %v, err %q", status.ExitCode(), i, status.Err())
- }
- // Receive the exitWait result.
- if status := <-exitWait; status.ExitCode() != i {
- t.Fatalf("ExitCode %v, expected %v, err %q", status.ExitCode(), i, status.Err())
- }
- }
- // Set the state to dead and removed.
- s.Lock()
- s.Dead = true
- s.Unlock()
- s.SetRemoved()
- // Wait for removed status or timeout.
- if status := <-removalWait; status.ExitCode() != 2 {
- // Should have the final exit code from the loop.
- t.Fatalf("Removal wait exitCode %v, expected %v, err %q", status.ExitCode(), 2, status.Err())
- }
- }
- func TestStateTimeoutWait(t *testing.T) {
- s := NewState()
- s.Lock()
- s.SetRunning(0, true)
- s.Unlock()
- // Start a wait with a timeout.
- ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
- defer cancel()
- waitC := s.Wait(ctx, WaitConditionNotRunning)
- // It should timeout *before* this 200ms timer does.
- select {
- case <-time.After(200 * time.Millisecond):
- t.Fatal("Stop callback doesn't fire in 200 milliseconds")
- case status := <-waitC:
- t.Log("Stop callback fired")
- // Should be a timeout error.
- if status.Err() == nil {
- t.Fatal("expected timeout error, got nil")
- }
- if status.ExitCode() != -1 {
- t.Fatalf("expected exit code %v, got %v", -1, status.ExitCode())
- }
- }
- s.Lock()
- s.SetStopped(&ExitStatus{ExitCode: 0})
- s.Unlock()
- // Start another wait with a timeout. This one should return
- // immediately.
- ctx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond)
- defer cancel()
- waitC = s.Wait(ctx, WaitConditionNotRunning)
- select {
- case <-time.After(200 * time.Millisecond):
- t.Fatal("Stop callback doesn't fire in 200 milliseconds")
- case status := <-waitC:
- t.Log("Stop callback fired")
- if status.ExitCode() != 0 {
- t.Fatalf("expected exit code %v, got %v, err %q", 0, status.ExitCode(), status.Err())
- }
- }
- }
- func TestIsValidStateString(t *testing.T) {
- states := []struct {
- state string
- expected bool
- }{
- {"paused", true},
- {"restarting", true},
- {"running", true},
- {"dead", true},
- {"start", false},
- {"created", true},
- {"exited", true},
- {"removing", true},
- {"stop", false},
- }
- for _, s := range states {
- v := IsValidStateString(s.state)
- if v != s.expected {
- t.Fatalf("Expected %t, but got %t", s.expected, v)
- }
- }
- }
|