package container

import (
	"sync/atomic"
	"testing"
	"time"

	"github.com/docker/docker/daemon/execdriver"
)

func TestStateRunStop(t *testing.T) {
	s := NewState()
	for i := 1; i < 3; i++ { // full lifecycle two times
		started := make(chan struct{})
		var pid int64
		go func() {
			runPid, _ := s.waitRunning(-1 * time.Second)
			atomic.StoreInt64(&pid, int64(runPid))
			close(started)
		}()
		s.Lock()
		s.SetRunning(i + 100)
		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)
		}
		select {
		case <-time.After(100 * time.Millisecond):
			t.Fatal("Start callback doesn't fire in 100 milliseconds")
		case <-started:
			t.Log("Start callback fired")
		}
		runPid := int(atomic.LoadInt64(&pid))
		if runPid != i+100 {
			t.Fatalf("Pid %v, expected %v", runPid, i+100)
		}
		if pid, err := s.waitRunning(-1 * time.Second); err != nil || pid != i+100 {
			t.Fatalf("waitRunning returned pid: %v, err: %v, expected pid: %v, err: %v", pid, err, i+100, nil)
		}

		stopped := make(chan struct{})
		var exit int64
		go func() {
			exitCode, _ := s.WaitStop(-1 * time.Second)
			atomic.StoreInt64(&exit, int64(exitCode))
			close(stopped)
		}()
		s.SetStoppedLocking(&execdriver.ExitStatus{ExitCode: i})
		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()
	started := make(chan struct{})
	go func() {
		s.waitRunning(100 * time.Millisecond)
		close(started)
	}()
	select {
	case <-time.After(200 * time.Millisecond):
		t.Fatal("Start callback doesn't fire in 100 milliseconds")
	case <-started:
		t.Log("Start callback fired")
	}

	s.Lock()
	s.SetRunning(49)
	s.Unlock()

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

}