jsonmessage_test.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. package jsonmessage
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "testing"
  8. "time"
  9. "github.com/docker/docker/pkg/jsonlog"
  10. "github.com/docker/docker/pkg/term"
  11. )
  12. func TestError(t *testing.T) {
  13. je := JSONError{404, "Not found"}
  14. if je.Error() != "Not found" {
  15. t.Fatalf("Expected 'Not found' got '%s'", je.Error())
  16. }
  17. }
  18. func TestProgress(t *testing.T) {
  19. termsz, err := term.GetWinsize(0)
  20. if err != nil {
  21. // we can safely ignore the err here
  22. termsz = nil
  23. }
  24. jp := JSONProgress{}
  25. if jp.String() != "" {
  26. t.Fatalf("Expected empty string, got '%s'", jp.String())
  27. }
  28. expected := " 1B"
  29. jp2 := JSONProgress{Current: 1}
  30. if jp2.String() != expected {
  31. t.Fatalf("Expected %q, got %q", expected, jp2.String())
  32. }
  33. expectedStart := "[==========> ] 20B/100B"
  34. if termsz != nil && termsz.Width <= 110 {
  35. expectedStart = " 20B/100B"
  36. }
  37. jp3 := JSONProgress{Current: 20, Total: 100, Start: time.Now().Unix()}
  38. // Just look at the start of the string
  39. // (the remaining time is really hard to test -_-)
  40. if jp3.String()[:len(expectedStart)] != expectedStart {
  41. t.Fatalf("Expected to start with %q, got %q", expectedStart, jp3.String())
  42. }
  43. expected = "[=========================> ] 50B/100B"
  44. if termsz != nil && termsz.Width <= 110 {
  45. expected = " 50B/100B"
  46. }
  47. jp4 := JSONProgress{Current: 50, Total: 100}
  48. if jp4.String() != expected {
  49. t.Fatalf("Expected %q, got %q", expected, jp4.String())
  50. }
  51. // this number can't be negative gh#7136
  52. expected = "[==================================================>] 50B"
  53. if termsz != nil && termsz.Width <= 110 {
  54. expected = " 50B"
  55. }
  56. jp5 := JSONProgress{Current: 50, Total: 40}
  57. if jp5.String() != expected {
  58. t.Fatalf("Expected %q, got %q", expected, jp5.String())
  59. }
  60. expected = "[=========================> ] 50/100 units"
  61. if termsz != nil && termsz.Width <= 110 {
  62. expected = " 50/100 units"
  63. }
  64. jp6 := JSONProgress{Current: 50, Total: 100, Units: "units"}
  65. if jp6.String() != expected {
  66. t.Fatalf("Expected %q, got %q", expected, jp6.String())
  67. }
  68. // this number can't be negative
  69. expected = "[==================================================>] 50 units"
  70. if termsz != nil && termsz.Width <= 110 {
  71. expected = " 50 units"
  72. }
  73. jp7 := JSONProgress{Current: 50, Total: 40, Units: "units"}
  74. if jp7.String() != expected {
  75. t.Fatalf("Expected %q, got %q", expected, jp7.String())
  76. }
  77. expected = "[=========================> ] "
  78. if termsz != nil && termsz.Width <= 110 {
  79. expected = ""
  80. }
  81. jp8 := JSONProgress{Current: 50, Total: 100, HideCounts: true}
  82. if jp8.String() != expected {
  83. t.Fatalf("Expected %q, got %q", expected, jp8.String())
  84. }
  85. }
  86. func TestJSONMessageDisplay(t *testing.T) {
  87. now := time.Now()
  88. messages := map[JSONMessage][]string{
  89. // Empty
  90. {}: {"\n", "\n"},
  91. // Status
  92. {
  93. Status: "status",
  94. }: {
  95. "status\n",
  96. "status\n",
  97. },
  98. // General
  99. {
  100. Time: now.Unix(),
  101. ID: "ID",
  102. From: "From",
  103. Status: "status",
  104. }: {
  105. fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
  106. fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
  107. },
  108. // General, with nano precision time
  109. {
  110. TimeNano: now.UnixNano(),
  111. ID: "ID",
  112. From: "From",
  113. Status: "status",
  114. }: {
  115. fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
  116. fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
  117. },
  118. // General, with both times Nano is preferred
  119. {
  120. Time: now.Unix(),
  121. TimeNano: now.UnixNano(),
  122. ID: "ID",
  123. From: "From",
  124. Status: "status",
  125. }: {
  126. fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
  127. fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
  128. },
  129. // Stream over status
  130. {
  131. Status: "status",
  132. Stream: "stream",
  133. }: {
  134. "stream",
  135. "stream",
  136. },
  137. // With progress message
  138. {
  139. Status: "status",
  140. ProgressMessage: "progressMessage",
  141. }: {
  142. "status progressMessage",
  143. "status progressMessage",
  144. },
  145. // With progress, stream empty
  146. {
  147. Status: "status",
  148. Stream: "",
  149. Progress: &JSONProgress{Current: 1},
  150. }: {
  151. "",
  152. fmt.Sprintf("%c[1K%c[K\rstatus 1B\r", 27, 27),
  153. },
  154. }
  155. // The tests :)
  156. for jsonMessage, expectedMessages := range messages {
  157. // Without terminal
  158. data := bytes.NewBuffer([]byte{})
  159. if err := jsonMessage.Display(data, nil); err != nil {
  160. t.Fatal(err)
  161. }
  162. if data.String() != expectedMessages[0] {
  163. t.Fatalf("Expected %q,got %q", expectedMessages[0], data.String())
  164. }
  165. // With terminal
  166. data = bytes.NewBuffer([]byte{})
  167. if err := jsonMessage.Display(data, &noTermInfo{}); err != nil {
  168. t.Fatal(err)
  169. }
  170. if data.String() != expectedMessages[1] {
  171. t.Fatalf("\nExpected %q\n got %q", expectedMessages[1], data.String())
  172. }
  173. }
  174. }
  175. // Test JSONMessage with an Error. It will return an error with the text as error, not the meaning of the HTTP code.
  176. func TestJSONMessageDisplayWithJSONError(t *testing.T) {
  177. data := bytes.NewBuffer([]byte{})
  178. jsonMessage := JSONMessage{Error: &JSONError{404, "Can't find it"}}
  179. err := jsonMessage.Display(data, &noTermInfo{})
  180. if err == nil || err.Error() != "Can't find it" {
  181. t.Fatalf("Expected a JSONError 404, got %q", err)
  182. }
  183. jsonMessage = JSONMessage{Error: &JSONError{401, "Anything"}}
  184. err = jsonMessage.Display(data, &noTermInfo{})
  185. if err == nil || err.Error() != "Authentication is required." {
  186. t.Fatalf("Expected an error \"Authentication is required.\", got %q", err)
  187. }
  188. }
  189. func TestDisplayJSONMessagesStreamInvalidJSON(t *testing.T) {
  190. var (
  191. inFd uintptr
  192. )
  193. data := bytes.NewBuffer([]byte{})
  194. reader := strings.NewReader("This is not a 'valid' JSON []")
  195. inFd, _ = term.GetFdInfo(reader)
  196. if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err == nil && err.Error()[:17] != "invalid character" {
  197. t.Fatalf("Should have thrown an error (invalid character in ..), got %q", err)
  198. }
  199. }
  200. func TestDisplayJSONMessagesStream(t *testing.T) {
  201. var (
  202. inFd uintptr
  203. )
  204. messages := map[string][]string{
  205. // empty string
  206. "": {
  207. "",
  208. ""},
  209. // Without progress & ID
  210. "{ \"status\": \"status\" }": {
  211. "status\n",
  212. "status\n",
  213. },
  214. // Without progress, with ID
  215. "{ \"id\": \"ID\",\"status\": \"status\" }": {
  216. "ID: status\n",
  217. fmt.Sprintf("ID: status\n"),
  218. },
  219. // With progress
  220. "{ \"id\": \"ID\", \"status\": \"status\", \"progress\": \"ProgressMessage\" }": {
  221. "ID: status ProgressMessage",
  222. fmt.Sprintf("\n%c[%dAID: status ProgressMessage%c[%dB", 27, 1, 27, 1),
  223. },
  224. // With progressDetail
  225. "{ \"id\": \"ID\", \"status\": \"status\", \"progressDetail\": { \"Current\": 1} }": {
  226. "", // progressbar is disabled in non-terminal
  227. fmt.Sprintf("\n%c[%dA%c[1K%c[K\rID: status 1B\r%c[%dB", 27, 1, 27, 27, 27, 1),
  228. },
  229. }
  230. // Use $TERM which is unlikely to exist, forcing DisplayJSONMessageStream to
  231. // (hopefully) use &noTermInfo.
  232. origTerm := os.Getenv("TERM")
  233. os.Setenv("TERM", "xyzzy-non-existent-terminfo")
  234. for jsonMessage, expectedMessages := range messages {
  235. data := bytes.NewBuffer([]byte{})
  236. reader := strings.NewReader(jsonMessage)
  237. inFd, _ = term.GetFdInfo(reader)
  238. // Without terminal
  239. if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err != nil {
  240. t.Fatal(err)
  241. }
  242. if data.String() != expectedMessages[0] {
  243. t.Fatalf("Expected an %q, got %q", expectedMessages[0], data.String())
  244. }
  245. // With terminal
  246. data = bytes.NewBuffer([]byte{})
  247. reader = strings.NewReader(jsonMessage)
  248. if err := DisplayJSONMessagesStream(reader, data, inFd, true, nil); err != nil {
  249. t.Fatal(err)
  250. }
  251. if data.String() != expectedMessages[1] {
  252. t.Fatalf("\nExpected %q\n got %q", expectedMessages[1], data.String())
  253. }
  254. }
  255. os.Setenv("TERM", origTerm)
  256. }