package cmdext

import (
	"fmt"
	"testing"
	"time"
)

func TestStdout(t *testing.T) {

	res1, err := Runner("printf").Arg("hello").Run()
	if err != nil {
		t.Errorf("%v", err)
	}
	if res1.StdErr != "" {
		t.Errorf("res1.StdErr == '%v'", res1.StdErr)
	}
	if res1.StdOut != "hello" {
		t.Errorf("res1.StdOut == '%v'", res1.StdOut)
	}
	if res1.StdCombined != "hello\n" {
		t.Errorf("res1.StdCombined == '%v'", res1.StdCombined)
	}

}

func TestStderr(t *testing.T) {

	res1, err := Runner("python").Arg("-c").Arg("import sys; print(\"error\", file=sys.stderr, end='')").Run()
	if err != nil {
		t.Errorf("%v", err)
	}
	if res1.StdErr != "error" {
		t.Errorf("res1.StdErr == '%v'", res1.StdErr)
	}
	if res1.StdOut != "" {
		t.Errorf("res1.StdOut == '%v'", res1.StdOut)
	}
	if res1.StdCombined != "error\n" {
		t.Errorf("res1.StdCombined == '%v'", res1.StdCombined)
	}

}

func TestStdcombined(t *testing.T) {
	res1, err := Runner("python").
		Arg("-c").
		Arg("import sys; import time; print(\"1\", file=sys.stderr, flush=True); time.sleep(0.1); print(\"2\", file=sys.stdout, flush=True); time.sleep(0.1); print(\"3\", file=sys.stderr, flush=True)").
		Run()
	if err != nil {
		t.Errorf("%v", err)
	}
	if res1.StdErr != "1\n3\n" {
		t.Errorf("res1.StdErr == '%v'", res1.StdErr)
	}
	if res1.StdOut != "2\n" {
		t.Errorf("res1.StdOut == '%v'", res1.StdOut)
	}
	if res1.StdCombined != "1\n2\n3\n" {
		t.Errorf("res1.StdCombined == '%v'", res1.StdCombined)
	}

}

func TestPartialRead(t *testing.T) {
	res1, err := Runner("python").
		Arg("-c").
		Arg("import sys; import time; print(\"first message\", flush=True); time.sleep(5); print(\"cant see me\", flush=True);").
		Timeout(100 * time.Millisecond).
		Run()
	if err != nil {
		t.Errorf("%v", err)
	}
	if !res1.CommandTimedOut {
		t.Errorf("!CommandTimedOut")
	}
	if res1.StdErr != "" {
		t.Errorf("res1.StdErr == '%v'", res1.StdErr)
	}
	if res1.StdOut != "first message\n" {
		t.Errorf("res1.StdOut == '%v'", res1.StdOut)
	}
	if res1.StdCombined != "first message\n" {
		t.Errorf("res1.StdCombined == '%v'", res1.StdCombined)
	}

}

func TestPartialReadStderr(t *testing.T) {
	res1, err := Runner("python").
		Arg("-c").
		Arg("import sys; import time; print(\"first message\", file=sys.stderr, flush=True); time.sleep(5); print(\"cant see me\", file=sys.stderr, flush=True);").
		Timeout(100 * time.Millisecond).
		Run()
	if err != nil {
		t.Errorf("%v", err)
	}
	if !res1.CommandTimedOut {
		t.Errorf("!CommandTimedOut")
	}
	if res1.StdErr != "first message\n" {
		t.Errorf("res1.StdErr == '%v'", res1.StdErr)
	}
	if res1.StdOut != "" {
		t.Errorf("res1.StdOut == '%v'", res1.StdOut)
	}
	if res1.StdCombined != "first message\n" {
		t.Errorf("res1.StdCombined == '%v'", res1.StdCombined)
	}

}

func TestReadUnflushedStdout(t *testing.T) {

	res1, err := Runner("python").Arg("-c").Arg("import sys; print(\"message101\", file=sys.stdout, end='')").Run()
	if err != nil {
		t.Errorf("%v", err)
	}
	if res1.StdErr != "" {
		t.Errorf("res1.StdErr == '%v'", res1.StdErr)
	}
	if res1.StdOut != "message101" {
		t.Errorf("res1.StdOut == '%v'", res1.StdOut)
	}
	if res1.StdCombined != "message101\n" {
		t.Errorf("res1.StdCombined == '%v'", res1.StdCombined)
	}

}

func TestReadUnflushedStderr(t *testing.T) {

	res1, err := Runner("python").Arg("-c").Arg("import sys; print(\"message101\", file=sys.stderr, end='')").Run()
	if err != nil {
		t.Errorf("%v", err)
	}
	if res1.StdErr != "message101" {
		t.Errorf("res1.StdErr == '%v'", res1.StdErr)
	}
	if res1.StdOut != "" {
		t.Errorf("res1.StdOut == '%v'", res1.StdOut)
	}
	if res1.StdCombined != "message101\n" {
		t.Errorf("res1.StdCombined == '%v'", res1.StdCombined)
	}

}

func TestPartialReadUnflushed(t *testing.T) {
	t.SkipNow()

	res1, err := Runner("python").
		Arg("-c").
		Arg("import sys; import time; print(\"first message\", end=''); time.sleep(5); print(\"cant see me\", end='');").
		Timeout(100 * time.Millisecond).
		Run()
	if err != nil {
		t.Errorf("%v", err)
	}
	if !res1.CommandTimedOut {
		t.Errorf("!CommandTimedOut")
	}
	if res1.StdErr != "" {
		t.Errorf("res1.StdErr == '%v'", res1.StdErr)
	}
	if res1.StdOut != "first message" {
		t.Errorf("res1.StdOut == '%v'", res1.StdOut)
	}
	if res1.StdCombined != "first message" {
		t.Errorf("res1.StdCombined == '%v'", res1.StdCombined)
	}

}

func TestPartialReadUnflushedStderr(t *testing.T) {
	t.SkipNow()

	res1, err := Runner("python").
		Arg("-c").
		Arg("import sys; import time; print(\"first message\", file=sys.stderr, end=''); time.sleep(5); print(\"cant see me\", file=sys.stderr, end='');").
		Timeout(100 * time.Millisecond).
		Run()
	if err != nil {
		t.Errorf("%v", err)
	}
	if !res1.CommandTimedOut {
		t.Errorf("!CommandTimedOut")
	}
	if res1.StdErr != "first message" {
		t.Errorf("res1.StdErr == '%v'", res1.StdErr)
	}
	if res1.StdOut != "" {
		t.Errorf("res1.StdOut == '%v'", res1.StdOut)
	}
	if res1.StdCombined != "first message" {
		t.Errorf("res1.StdCombined == '%v'", res1.StdCombined)
	}

}

func TestListener(t *testing.T) {

	_, err := Runner("python").
		Arg("-c").
		Arg("import sys;" +
			"import time;" +
			"print(\"message 1\", flush=True);" +
			"time.sleep(1);" +
			"print(\"message 2\", flush=True);" +
			"time.sleep(1);" +
			"print(\"message 3\", flush=True);" +
			"time.sleep(1);" +
			"print(\"message 4\", file=sys.stderr, flush=True);" +
			"time.sleep(1);" +
			"print(\"message 5\", flush=True);" +
			"time.sleep(1);" +
			"print(\"final\");").
		ListenStdout(func(s string) { fmt.Printf("@@STDOUT <<- %v (%v)\n", s, time.Now().Format(time.RFC3339Nano)) }).
		ListenStderr(func(s string) { fmt.Printf("@@STDERR <<- %v (%v)\n", s, time.Now().Format(time.RFC3339Nano)) }).
		Timeout(10 * time.Second).
		Run()

	if err != nil {
		panic(err)
	}
}