anko_test.go 7.1 KB

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