github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/container/state_test.go (about)

     1  package container // import "github.com/docker/docker/container"
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/docker/docker/api/types"
     9  )
    10  
    11  func TestIsValidHealthString(t *testing.T) {
    12  	contexts := []struct {
    13  		Health   string
    14  		Expected bool
    15  	}{
    16  		{types.Healthy, true},
    17  		{types.Unhealthy, true},
    18  		{types.Starting, true},
    19  		{types.NoHealthcheck, true},
    20  		{"fail", false},
    21  	}
    22  
    23  	for _, c := range contexts {
    24  		v := IsValidHealthString(c.Health)
    25  		if v != c.Expected {
    26  			t.Fatalf("Expected %t, but got %t", c.Expected, v)
    27  		}
    28  	}
    29  }
    30  
    31  func TestStateRunStop(t *testing.T) {
    32  	s := NewState()
    33  
    34  	// Begin another wait with WaitConditionRemoved. It should complete
    35  	// within 200 milliseconds.
    36  	ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
    37  	defer cancel()
    38  	removalWait := s.Wait(ctx, WaitConditionRemoved)
    39  
    40  	// Full lifecycle two times.
    41  	for i := 1; i <= 2; i++ {
    42  		// A wait with WaitConditionNotRunning should return
    43  		// immediately since the state is now either "created" (on the
    44  		// first iteration) or "exited" (on the second iteration). It
    45  		// shouldn't take more than 50 milliseconds.
    46  		ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
    47  		defer cancel()
    48  		// Expectx exit code to be i-1 since it should be the exit
    49  		// code from the previous loop or 0 for the created state.
    50  		if status := <-s.Wait(ctx, WaitConditionNotRunning); status.ExitCode() != i-1 {
    51  			t.Fatalf("ExitCode %v, expected %v, err %q", status.ExitCode(), i-1, status.Err())
    52  		}
    53  
    54  		// A wait with WaitConditionNextExit should block until the
    55  		// container has started and exited. It shouldn't take more
    56  		// than 100 milliseconds.
    57  		ctx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond)
    58  		defer cancel()
    59  		initialWait := s.Wait(ctx, WaitConditionNextExit)
    60  
    61  		// Set the state to "Running".
    62  		s.Lock()
    63  		s.SetRunning(i, true)
    64  		s.Unlock()
    65  
    66  		// Assert desired state.
    67  		if !s.IsRunning() {
    68  			t.Fatal("State not running")
    69  		}
    70  		if s.Pid != i {
    71  			t.Fatalf("Pid %v, expected %v", s.Pid, i)
    72  		}
    73  		if s.ExitCode() != 0 {
    74  			t.Fatalf("ExitCode %v, expected 0", s.ExitCode())
    75  		}
    76  
    77  		// Now that it's running, a wait with WaitConditionNotRunning
    78  		// should block until we stop the container. It shouldn't take
    79  		// more than 100 milliseconds.
    80  		ctx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond)
    81  		defer cancel()
    82  		exitWait := s.Wait(ctx, WaitConditionNotRunning)
    83  
    84  		// Set the state to "Exited".
    85  		s.Lock()
    86  		s.SetStopped(&ExitStatus{ExitCode: i})
    87  		s.Unlock()
    88  
    89  		// Assert desired state.
    90  		if s.IsRunning() {
    91  			t.Fatal("State is running")
    92  		}
    93  		if s.ExitCode() != i {
    94  			t.Fatalf("ExitCode %v, expected %v", s.ExitCode(), i)
    95  		}
    96  		if s.Pid != 0 {
    97  			t.Fatalf("Pid %v, expected 0", s.Pid)
    98  		}
    99  
   100  		// Receive the initialWait result.
   101  		if status := <-initialWait; status.ExitCode() != i {
   102  			t.Fatalf("ExitCode %v, expected %v, err %q", status.ExitCode(), i, status.Err())
   103  		}
   104  
   105  		// Receive the exitWait result.
   106  		if status := <-exitWait; status.ExitCode() != i {
   107  			t.Fatalf("ExitCode %v, expected %v, err %q", status.ExitCode(), i, status.Err())
   108  		}
   109  	}
   110  
   111  	// Set the state to dead and removed.
   112  	s.Lock()
   113  	s.Dead = true
   114  	s.Unlock()
   115  	s.SetRemoved()
   116  
   117  	// Wait for removed status or timeout.
   118  	if status := <-removalWait; status.ExitCode() != 2 {
   119  		// Should have the final exit code from the loop.
   120  		t.Fatalf("Removal wait exitCode %v, expected %v, err %q", status.ExitCode(), 2, status.Err())
   121  	}
   122  }
   123  
   124  func TestStateTimeoutWait(t *testing.T) {
   125  	s := NewState()
   126  
   127  	s.Lock()
   128  	s.SetRunning(0, true)
   129  	s.Unlock()
   130  
   131  	// Start a wait with a timeout.
   132  	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
   133  	defer cancel()
   134  	waitC := s.Wait(ctx, WaitConditionNotRunning)
   135  
   136  	// It should timeout *before* this 200ms timer does.
   137  	select {
   138  	case <-time.After(200 * time.Millisecond):
   139  		t.Fatal("Stop callback doesn't fire in 200 milliseconds")
   140  	case status := <-waitC:
   141  		t.Log("Stop callback fired")
   142  		// Should be a timeout error.
   143  		if status.Err() == nil {
   144  			t.Fatal("expected timeout error, got nil")
   145  		}
   146  		if status.ExitCode() != -1 {
   147  			t.Fatalf("expected exit code %v, got %v", -1, status.ExitCode())
   148  		}
   149  	}
   150  
   151  	s.Lock()
   152  	s.SetStopped(&ExitStatus{ExitCode: 0})
   153  	s.Unlock()
   154  
   155  	// Start another wait with a timeout. This one should return
   156  	// immediately.
   157  	ctx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond)
   158  	defer cancel()
   159  	waitC = s.Wait(ctx, WaitConditionNotRunning)
   160  
   161  	select {
   162  	case <-time.After(200 * time.Millisecond):
   163  		t.Fatal("Stop callback doesn't fire in 200 milliseconds")
   164  	case status := <-waitC:
   165  		t.Log("Stop callback fired")
   166  		if status.ExitCode() != 0 {
   167  			t.Fatalf("expected exit code %v, got %v, err %q", 0, status.ExitCode(), status.Err())
   168  		}
   169  	}
   170  }
   171  
   172  func TestIsValidStateString(t *testing.T) {
   173  	states := []struct {
   174  		state    string
   175  		expected bool
   176  	}{
   177  		{"paused", true},
   178  		{"restarting", true},
   179  		{"running", true},
   180  		{"dead", true},
   181  		{"start", false},
   182  		{"created", true},
   183  		{"exited", true},
   184  		{"removing", true},
   185  		{"stop", false},
   186  	}
   187  
   188  	for _, s := range states {
   189  		v := IsValidStateString(s.state)
   190  		if v != s.expected {
   191  			t.Fatalf("Expected %t, but got %t", s.expected, v)
   192  		}
   193  	}
   194  }