github.com/mattn/anko@v0.1.10/anko_test.go (about)

     1  // +build !appengine
     2  
     3  package main
     4  
     5  import (
     6  	"bufio"
     7  	"io"
     8  	"log"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  )
    17  
    18  var logger *log.Logger
    19  
    20  func TestMain(m *testing.M) {
    21  	parseFlags()
    22  	code := m.Run()
    23  	os.Exit(code)
    24  }
    25  
    26  func TestRunNonInteractiveFile(t *testing.T) {
    27  	_, filename, _, _ := runtime.Caller(0)
    28  	testDir := filepath.Join(filepath.Dir(filename), "core", "testdata")
    29  	setupEnv()
    30  
    31  	file = filepath.Join(testDir, "not-found.ank")
    32  	exitCode := runNonInteractive()
    33  	if exitCode != 2 {
    34  		t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 2)
    35  	}
    36  
    37  	file = filepath.Join(testDir, "broken.ank")
    38  	exitCode = runNonInteractive()
    39  	os.Args = []string{os.Args[0]}
    40  	if exitCode != 4 {
    41  		t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 4)
    42  	}
    43  
    44  	file = filepath.Join(testDir, "test.ank")
    45  	exitCode = runNonInteractive()
    46  	os.Args = []string{os.Args[0]}
    47  	if exitCode != 0 {
    48  		t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 0)
    49  	}
    50  
    51  	file = ""
    52  }
    53  
    54  func TestRunNonInteractiveExecute(t *testing.T) {
    55  	setupEnv()
    56  
    57  	flagExecute = "1 + 1"
    58  	exitCode := runNonInteractive()
    59  	if exitCode != 0 {
    60  		t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 0)
    61  	}
    62  
    63  	flagExecute = "1++"
    64  	exitCode = runNonInteractive()
    65  	if exitCode != 4 {
    66  		t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 4)
    67  	}
    68  
    69  	flagExecute = ""
    70  }
    71  
    72  type testInteractive struct {
    73  	runLines   []string
    74  	runOutputs []string
    75  	runError   string
    76  }
    77  
    78  func TestRunInteractive(t *testing.T) {
    79  	// empty strings in runOutputs will ignore read timeouts
    80  	tests := []testInteractive{
    81  		{runLines: []string{".."}, runError: "1:1 syntax error on '.' at 1:1"},
    82  		{runLines: []string{"1++"}, runError: "1:1 invalid operation"},
    83  		{runLines: []string{"var , b = 1, 2"}, runError: "1:7 syntax error: unexpected ','"},
    84  
    85  		{runLines: []string{"", "1"}, runOutputs: []string{"", "1"}},
    86  		{runLines: []string{"1 + 1"}, runOutputs: []string{"2"}},
    87  		{runLines: []string{"a = 1", "b = 2", "a + b"}, runOutputs: []string{"1", "2", "3"}},
    88  		{runLines: []string{"a = 1", "if a == 1 {", "b = 1", "b = 2", "}", "a"}, runOutputs: []string{"1", "", "", "", "2", "1"}},
    89  		{runLines: []string{"a = 1", "for i = 0; i < 2; i++ {", "a++", "}", "a"}, runOutputs: []string{"1", "", "", "<nil>", "3"}},
    90  		{runLines: []string{"1 + 1", "// comment 1", "2 + 2 // comment 2", "// 3 + 3"}, runOutputs: []string{"2", "<nil>", "4", "<nil>"}},
    91  	}
    92  	runInteractiveTests(t, tests)
    93  }
    94  
    95  func runInteractiveTests(t *testing.T, tests []testInteractive) {
    96  	// create logger
    97  	// Note: logger is used for debugging since real stdout cannot be used
    98  	logger = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.LUTC|log.Llongfile)
    99  	gopath := os.Getenv("GOPATH")
   100  	if gopath == "" {
   101  		b, err := exec.Command("go", "env", "GOPATH").CombinedOutput()
   102  		if err != nil {
   103  			t.Fatal(err)
   104  		}
   105  		gopath = strings.TrimSpace(string(b))
   106  	}
   107  	filehandle, err := os.OpenFile(filepath.Join(gopath, "bin", "anko_test.log"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
   108  	if err != nil {
   109  		t.Fatal("OpenFile error:", err)
   110  	}
   111  	defer filehandle.Close()
   112  	logger.SetOutput(filehandle)
   113  	logger.Print("logger file created")
   114  
   115  	// defer sending std back to real
   116  	realStdin := os.Stdin
   117  	realStderr := os.Stderr
   118  	realStdout := os.Stdout
   119  	defer setStd(realStdin, realStderr, realStdout)
   120  
   121  	// create pipes
   122  	readFromIn, writeToIn, err := os.Pipe()
   123  	if err != nil {
   124  		t.Fatal("Pipe error:", err)
   125  	}
   126  	os.Stdin = readFromIn
   127  	logger.Print("pipe in created")
   128  	readFromErr, writeToErr, err := os.Pipe()
   129  	if err != nil {
   130  		t.Fatal("Pipe error:", err)
   131  	}
   132  	os.Stderr = writeToErr
   133  	logger.Print("pipe err created")
   134  	readFromOut, writeToOut, err := os.Pipe()
   135  	if err != nil {
   136  		t.Fatal("Pipe error:", err)
   137  	}
   138  	os.Stdout = writeToOut
   139  	logger.Print("pipe out created")
   140  
   141  	// setup reader
   142  	readerErr := bufio.NewReaderSize(readFromErr, 256)
   143  	readerOut := bufio.NewReaderSize(readFromOut, 256)
   144  	chanQuit := make(chan struct{}, 1)
   145  	chanErr := make(chan string, 3)
   146  	chanOut := make(chan string, 3)
   147  	readTimeout := 10 * time.Millisecond
   148  	var dataErr string
   149  	var dataOut string
   150  
   151  	go readerToChan(t, chanQuit, readerErr, chanErr)
   152  	go readerToChan(t, chanQuit, readerOut, chanOut)
   153  
   154  	go runInteractive()
   155  
   156  	time.Sleep(readTimeout)
   157  
   158  	// basic read and write to make sure things are working
   159  	_, err = writeToIn.WriteString("1\n")
   160  	if err != nil {
   161  		t.Fatal("Stdin WriteString error:", err)
   162  	}
   163  	select {
   164  	case dataOut = <-chanOut:
   165  		if len(dataOut) > 0 && dataOut[0] == '>' {
   166  			dataOut = dataOut[1:]
   167  			dataOut = strings.TrimSpace(dataOut)
   168  		}
   169  		if dataOut != "1" {
   170  			t.Fatalf("Stdout - received: %v - expected: %v - basic test", dataOut, "1")
   171  		}
   172  	case dataErr = <-chanErr:
   173  		dataErr = strings.TrimSpace(dataErr)
   174  		if dataErr != "" {
   175  			t.Fatalf("Stderr - received: %v - expected: %v - basic test", dataErr, "")
   176  		}
   177  	case <-time.After(readTimeout):
   178  		t.Fatal("read timeout for basic test")
   179  	}
   180  
   181  	// run tests
   182  	logger.Print("running tests start")
   183  	for _, test := range tests {
   184  
   185  		for i, runLine := range test.runLines {
   186  
   187  			_, err = writeToIn.WriteString(runLine + "\n")
   188  			if err != nil {
   189  				t.Fatal("Stdin WriteString error:", err)
   190  			}
   191  
   192  			select {
   193  			case <-time.After(readTimeout):
   194  				if test.runOutputs[i] != "" {
   195  					t.Fatalf("read timeout for i: %v - runLines: %v", i, test.runLines)
   196  				}
   197  			case dataOut = <-chanOut:
   198  				for len(dataOut) > 0 && dataOut[0] == '>' {
   199  					dataOut = dataOut[1:]
   200  					dataOut = strings.TrimSpace(dataOut)
   201  				}
   202  				if dataOut != test.runOutputs[i] {
   203  					t.Fatalf("Stdout - received: %v - expected: %v - i: %v - runLines: %v", dataOut, test.runOutputs[i], i, test.runLines)
   204  				}
   205  			case dataErr = <-chanErr:
   206  				dataErr = strings.TrimSpace(dataErr)
   207  				if dataErr != test.runError {
   208  					t.Fatalf("Stderr - received: %v - expected: %v - i: %v - runLines: %v", dataErr, test.runError, i, test.runLines)
   209  				}
   210  
   211  				// to clean output from error
   212  				_, err = writeToIn.WriteString("1\n")
   213  				if err != nil {
   214  					t.Fatal("Stdin WriteString error:", err)
   215  				}
   216  
   217  				select {
   218  				case dataOut = <-chanOut:
   219  					for len(dataOut) > 0 && dataOut[0] == '>' {
   220  						dataOut = dataOut[1:]
   221  						dataOut = strings.TrimSpace(dataOut)
   222  					}
   223  					if dataOut != "1" {
   224  						t.Fatalf("Stdout - received: %v - expected: %v - i: %v - runLines: %v", dataOut, test.runOutputs[i], i, test.runLines)
   225  					}
   226  				case <-time.After(readTimeout):
   227  					t.Fatalf("read timeout for i: %v - runLines: %v", i, test.runLines)
   228  				}
   229  
   230  			}
   231  
   232  		}
   233  
   234  	}
   235  	logger.Print("running tests end")
   236  
   237  	// quit
   238  	_, err = writeToIn.WriteString("quit()\n")
   239  	if err != nil {
   240  		t.Fatal("Stdin WriteString error:", err)
   241  	}
   242  	logger.Print("quit() sent")
   243  
   244  	close(chanQuit)
   245  	logger.Print("chanQuit closed")
   246  
   247  	writeToErr.Close()
   248  	writeToOut.Close()
   249  	logger.Print("pipes closed")
   250  }
   251  
   252  func readerToChan(t *testing.T, chanQuit chan struct{}, reader *bufio.Reader, chanOut chan string) {
   253  	logger.Print("readerToChan start")
   254  	for {
   255  		data, err := reader.ReadString('\n')
   256  		if err != nil && err != io.EOF {
   257  			t.Fatal("readerToChan ReadString error:", err)
   258  		}
   259  		select {
   260  		case <-chanQuit:
   261  			logger.Print("readerToChan end")
   262  			return
   263  		default:
   264  		}
   265  		chanOut <- data
   266  	}
   267  }
   268  
   269  func setStd(stdin *os.File, stderr *os.File, stdout *os.File) {
   270  	os.Stdin = stdin
   271  	os.Stderr = stderr
   272  	os.Stdout = stdout
   273  }