github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/container/state_test.go (about) 1 package container // import "github.com/demonoid81/moby/container" 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/demonoid81/moby/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 }