|
1 | 1 | package interp_test
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "bytes" |
4 | 5 | "context"
|
5 | 6 | "fmt"
|
| 7 | + "io" |
6 | 8 | "log"
|
7 | 9 | "net/http"
|
| 10 | + "os" |
8 | 11 | "reflect"
|
| 12 | + "strconv" |
9 | 13 | "strings"
|
| 14 | + "sync" |
10 | 15 | "testing"
|
11 | 16 | "time"
|
12 | 17 |
|
@@ -611,3 +616,117 @@ func assertEval(t *testing.T, i *interp.Interpreter, src, expectedError, expecte
|
611 | 616 | t.Fatalf("got %v, want %s", res, expectedRes)
|
612 | 617 | }
|
613 | 618 | }
|
| 619 | + |
| 620 | +func TestEvalEOF(t *testing.T) { |
| 621 | + tests := []struct { |
| 622 | + desc string |
| 623 | + src []string |
| 624 | + errorLine int |
| 625 | + }{ |
| 626 | + { |
| 627 | + desc: "no error", |
| 628 | + src: []string{ |
| 629 | + `func main() {`, |
| 630 | + `println("foo")`, |
| 631 | + `}`, |
| 632 | + }, |
| 633 | + errorLine: -1, |
| 634 | + }, |
| 635 | + { |
| 636 | + desc: "no parsing error, but block error", |
| 637 | + src: []string{ |
| 638 | + `func main() {`, |
| 639 | + `println(foo)`, |
| 640 | + `}`, |
| 641 | + }, |
| 642 | + errorLine: 2, |
| 643 | + }, |
| 644 | + { |
| 645 | + desc: "parsing error", |
| 646 | + src: []string{ |
| 647 | + `func main() {`, |
| 648 | + `println(/foo)`, |
| 649 | + `}`, |
| 650 | + }, |
| 651 | + errorLine: 1, |
| 652 | + }, |
| 653 | + } |
| 654 | + |
| 655 | + for it, test := range tests { |
| 656 | + i := interp.New(interp.Options{}) |
| 657 | + var stderr bytes.Buffer |
| 658 | + safeStderr := &safeBuffer{buf: &stderr} |
| 659 | + pin, pout := io.Pipe() |
| 660 | + defer func() { |
| 661 | + // Closing the pipe also takes care of making i.REPL terminate, |
| 662 | + // hence freeing its goroutine. |
| 663 | + _ = pin.Close() |
| 664 | + _ = pout.Close() |
| 665 | + }() |
| 666 | + |
| 667 | + go func() { |
| 668 | + i.REPL(pin, safeStderr) |
| 669 | + }() |
| 670 | + for k, v := range test.src { |
| 671 | + if _, err := pout.Write([]byte(v + "\n")); err != nil { |
| 672 | + t.Error(err) |
| 673 | + } |
| 674 | + Sleep(100 * time.Millisecond) |
| 675 | + |
| 676 | + errMsg := safeStderr.String() |
| 677 | + if k == test.errorLine { |
| 678 | + if errMsg == "" { |
| 679 | + t.Fatalf("%d: statement %q should have produced an error", it, v) |
| 680 | + } |
| 681 | + break |
| 682 | + } |
| 683 | + if errMsg != "" { |
| 684 | + t.Fatalf("%d: unexpected error: %v", it, errMsg) |
| 685 | + } |
| 686 | + } |
| 687 | + } |
| 688 | +} |
| 689 | + |
| 690 | +type safeBuffer struct { |
| 691 | + mu sync.RWMutex |
| 692 | + buf *bytes.Buffer |
| 693 | +} |
| 694 | + |
| 695 | +func (sb *safeBuffer) Read(p []byte) (int, error) { |
| 696 | + return sb.buf.Read(p) |
| 697 | +} |
| 698 | + |
| 699 | +func (sb *safeBuffer) String() string { |
| 700 | + sb.mu.RLock() |
| 701 | + defer sb.mu.RUnlock() |
| 702 | + return sb.buf.String() |
| 703 | +} |
| 704 | + |
| 705 | +func (sb *safeBuffer) Write(p []byte) (int, error) { |
| 706 | + sb.mu.Lock() |
| 707 | + defer sb.mu.Unlock() |
| 708 | + return sb.buf.Write(p) |
| 709 | +} |
| 710 | + |
| 711 | +const ( |
| 712 | + // CITimeoutMultiplier is the multiplier for all timeouts in the CI. |
| 713 | + CITimeoutMultiplier = 3 |
| 714 | +) |
| 715 | + |
| 716 | +// Sleep pauses the current goroutine for at least the duration d. |
| 717 | +func Sleep(d time.Duration) { |
| 718 | + d = applyCIMultiplier(d) |
| 719 | + time.Sleep(d) |
| 720 | +} |
| 721 | + |
| 722 | +func applyCIMultiplier(timeout time.Duration) time.Duration { |
| 723 | + ci := os.Getenv("CI") |
| 724 | + if ci == "" { |
| 725 | + return timeout |
| 726 | + } |
| 727 | + b, err := strconv.ParseBool(ci) |
| 728 | + if err != nil || !b { |
| 729 | + return timeout |
| 730 | + } |
| 731 | + return time.Duration(float64(timeout) * CITimeoutMultiplier) |
| 732 | +} |
0 commit comments