package container

import (
	"sync/atomic"
	"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()
	for i := 1; i < 3; i++ { // full lifecycle two times
		s.Lock()
		s.SetRunning(i+100, false)
		s.Unlock()

		if !s.IsRunning() {
			t.Fatal("State not running")
		}
		if s.Pid != i+100 {
			t.Fatalf("Pid %v, expected %v", s.Pid, i+100)
		}
		if s.ExitCode() != 0 {
			t.Fatalf("ExitCode %v, expected 0", s.ExitCode())
		}

		stopped := make(chan struct{})
		var exit int64
		go func() {
			exitCode, _ := s.WaitStop(-1 * time.Second)
			atomic.StoreInt64(&exit, int64(exitCode))
			close(stopped)
		}()
		s.Lock()
		s.SetStopped(&ExitStatus{ExitCode: i})
		s.Unlock()
		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)
		}
		select {
		case <-time.After(100 * time.Millisecond):
			t.Fatal("Stop callback doesn't fire in 100 milliseconds")
		case <-stopped:
			t.Log("Stop callback fired")
		}
		exitCode := int(atomic.LoadInt64(&exit))
		if exitCode != i {
			t.Fatalf("ExitCode %v, expected %v", exitCode, i)
		}
		if exitCode, err := s.WaitStop(-1 * time.Second); err != nil || exitCode != i {
			t.Fatalf("WaitStop returned exitCode: %v, err: %v, expected exitCode: %v, err: %v", exitCode, err, i, nil)
		}
	}
}

func TestStateTimeoutWait(t *testing.T) {
	s := NewState()
	stopped := make(chan struct{})
	go func() {
		s.WaitStop(100 * time.Millisecond)
		close(stopped)
	}()
	select {
	case <-time.After(200 * time.Millisecond):
		t.Fatal("Stop callback doesn't fire in 200 milliseconds")
	case <-stopped:
		t.Log("Stop callback fired")
	}

	s.Lock()
	s.SetStopped(&ExitStatus{ExitCode: 1})
	s.Unlock()

	stopped = make(chan struct{})
	go func() {
		s.WaitStop(100 * time.Millisecond)
		close(stopped)
	}()
	select {
	case <-time.After(200 * time.Millisecond):
		t.Fatal("Stop callback doesn't fire in 100 milliseconds")
	case <-stopped:
		t.Log("Stop callback fired")
	}

}
