top_test.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /*
  2. Copyright 2024 Docker Compose CLI authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package compose
  14. import (
  15. "bytes"
  16. "strings"
  17. "testing"
  18. "gotest.tools/v3/assert"
  19. "github.com/docker/compose/v5/pkg/api"
  20. )
  21. var topTestCases = []struct {
  22. name string
  23. titles []string
  24. procs [][]string
  25. header topHeader
  26. entries []topEntries
  27. output string
  28. }{
  29. {
  30. name: "noprocs",
  31. titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
  32. procs: [][]string{},
  33. header: topHeader{"SERVICE": 0, "#": 1},
  34. entries: []topEntries{},
  35. output: "",
  36. },
  37. {
  38. name: "simple",
  39. titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
  40. procs: [][]string{{"root", "1", "1", "0", "12:00", "?", "00:00:01", "/entrypoint"}},
  41. header: topHeader{
  42. "SERVICE": 0,
  43. "#": 1,
  44. "UID": 2,
  45. "PID": 3,
  46. "PPID": 4,
  47. "C": 5,
  48. "STIME": 6,
  49. "TTY": 7,
  50. "TIME": 8,
  51. "CMD": 9,
  52. },
  53. entries: []topEntries{
  54. {
  55. "SERVICE": "simple",
  56. "#": "1",
  57. "UID": "root",
  58. "PID": "1",
  59. "PPID": "1",
  60. "C": "0",
  61. "STIME": "12:00",
  62. "TTY": "?",
  63. "TIME": "00:00:01",
  64. "CMD": "/entrypoint",
  65. },
  66. },
  67. output: trim(`
  68. SERVICE # UID PID PPID C STIME TTY TIME CMD
  69. simple 1 root 1 1 0 12:00 ? 00:00:01 /entrypoint
  70. `),
  71. },
  72. {
  73. name: "noppid",
  74. titles: []string{"UID", "PID", "C", "STIME", "TTY", "TIME", "CMD"},
  75. procs: [][]string{{"root", "1", "0", "12:00", "?", "00:00:02", "/entrypoint"}},
  76. header: topHeader{
  77. "SERVICE": 0,
  78. "#": 1,
  79. "UID": 2,
  80. "PID": 3,
  81. "C": 4,
  82. "STIME": 5,
  83. "TTY": 6,
  84. "TIME": 7,
  85. "CMD": 8,
  86. },
  87. entries: []topEntries{
  88. {
  89. "SERVICE": "noppid",
  90. "#": "1",
  91. "UID": "root",
  92. "PID": "1",
  93. "C": "0",
  94. "STIME": "12:00",
  95. "TTY": "?",
  96. "TIME": "00:00:02",
  97. "CMD": "/entrypoint",
  98. },
  99. },
  100. output: trim(`
  101. SERVICE # UID PID C STIME TTY TIME CMD
  102. noppid 1 root 1 0 12:00 ? 00:00:02 /entrypoint
  103. `),
  104. },
  105. {
  106. name: "extra-hdr",
  107. titles: []string{"UID", "GID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
  108. procs: [][]string{{"root", "1", "1", "1", "0", "12:00", "?", "00:00:03", "/entrypoint"}},
  109. header: topHeader{
  110. "SERVICE": 0,
  111. "#": 1,
  112. "UID": 2,
  113. "GID": 3,
  114. "PID": 4,
  115. "PPID": 5,
  116. "C": 6,
  117. "STIME": 7,
  118. "TTY": 8,
  119. "TIME": 9,
  120. "CMD": 10,
  121. },
  122. entries: []topEntries{
  123. {
  124. "SERVICE": "extra-hdr",
  125. "#": "1",
  126. "UID": "root",
  127. "GID": "1",
  128. "PID": "1",
  129. "PPID": "1",
  130. "C": "0",
  131. "STIME": "12:00",
  132. "TTY": "?",
  133. "TIME": "00:00:03",
  134. "CMD": "/entrypoint",
  135. },
  136. },
  137. output: trim(`
  138. SERVICE # UID GID PID PPID C STIME TTY TIME CMD
  139. extra-hdr 1 root 1 1 1 0 12:00 ? 00:00:03 /entrypoint
  140. `),
  141. },
  142. {
  143. name: "multiple",
  144. titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
  145. procs: [][]string{
  146. {"root", "1", "1", "0", "12:00", "?", "00:00:04", "/entrypoint"},
  147. {"root", "123", "1", "0", "12:00", "?", "00:00:42", "sleep infinity"},
  148. },
  149. header: topHeader{
  150. "SERVICE": 0,
  151. "#": 1,
  152. "UID": 2,
  153. "PID": 3,
  154. "PPID": 4,
  155. "C": 5,
  156. "STIME": 6,
  157. "TTY": 7,
  158. "TIME": 8,
  159. "CMD": 9,
  160. },
  161. entries: []topEntries{
  162. {
  163. "SERVICE": "multiple",
  164. "#": "1",
  165. "UID": "root",
  166. "PID": "1",
  167. "PPID": "1",
  168. "C": "0",
  169. "STIME": "12:00",
  170. "TTY": "?",
  171. "TIME": "00:00:04",
  172. "CMD": "/entrypoint",
  173. },
  174. {
  175. "SERVICE": "multiple",
  176. "#": "1",
  177. "UID": "root",
  178. "PID": "123",
  179. "PPID": "1",
  180. "C": "0",
  181. "STIME": "12:00",
  182. "TTY": "?",
  183. "TIME": "00:00:42",
  184. "CMD": "sleep infinity",
  185. },
  186. },
  187. output: trim(`
  188. SERVICE # UID PID PPID C STIME TTY TIME CMD
  189. multiple 1 root 1 1 0 12:00 ? 00:00:04 /entrypoint
  190. multiple 1 root 123 1 0 12:00 ? 00:00:42 sleep infinity
  191. `),
  192. },
  193. }
  194. // TestRunTopCore only tests the core functionality of runTop: formatting
  195. // and printing of the output of (api.Compose).Top().
  196. func TestRunTopCore(t *testing.T) {
  197. t.Parallel()
  198. all := []api.ContainerProcSummary{}
  199. for _, tc := range topTestCases {
  200. summary := api.ContainerProcSummary{
  201. Name: "not used",
  202. Titles: tc.titles,
  203. Processes: tc.procs,
  204. Service: tc.name,
  205. Replica: "1",
  206. }
  207. all = append(all, summary)
  208. t.Run(tc.name, func(t *testing.T) {
  209. header, entries := collectTop([]api.ContainerProcSummary{summary})
  210. assert.DeepEqual(t, tc.header, header)
  211. assert.DeepEqual(t, tc.entries, entries)
  212. var buf bytes.Buffer
  213. err := topPrint(&buf, header, entries)
  214. assert.NilError(t, err)
  215. assert.Equal(t, tc.output, buf.String())
  216. })
  217. }
  218. t.Run("all", func(t *testing.T) {
  219. header, entries := collectTop(all)
  220. assert.DeepEqual(t, topHeader{
  221. "SERVICE": 0,
  222. "#": 1,
  223. "UID": 2,
  224. "PID": 3,
  225. "PPID": 4,
  226. "C": 5,
  227. "STIME": 6,
  228. "TTY": 7,
  229. "TIME": 8,
  230. "GID": 9,
  231. "CMD": 10,
  232. }, header)
  233. assert.DeepEqual(t, []topEntries{
  234. {
  235. "SERVICE": "simple",
  236. "#": "1",
  237. "UID": "root",
  238. "PID": "1",
  239. "PPID": "1",
  240. "C": "0",
  241. "STIME": "12:00",
  242. "TTY": "?",
  243. "TIME": "00:00:01",
  244. "CMD": "/entrypoint",
  245. }, {
  246. "SERVICE": "noppid",
  247. "#": "1",
  248. "UID": "root",
  249. "PID": "1",
  250. "C": "0",
  251. "STIME": "12:00",
  252. "TTY": "?",
  253. "TIME": "00:00:02",
  254. "CMD": "/entrypoint",
  255. }, {
  256. "SERVICE": "extra-hdr",
  257. "#": "1",
  258. "UID": "root",
  259. "GID": "1",
  260. "PID": "1",
  261. "PPID": "1",
  262. "C": "0",
  263. "STIME": "12:00",
  264. "TTY": "?",
  265. "TIME": "00:00:03",
  266. "CMD": "/entrypoint",
  267. }, {
  268. "SERVICE": "multiple",
  269. "#": "1",
  270. "UID": "root",
  271. "PID": "1",
  272. "PPID": "1",
  273. "C": "0",
  274. "STIME": "12:00",
  275. "TTY": "?",
  276. "TIME": "00:00:04",
  277. "CMD": "/entrypoint",
  278. }, {
  279. "SERVICE": "multiple",
  280. "#": "1",
  281. "UID": "root",
  282. "PID": "123",
  283. "PPID": "1",
  284. "C": "0",
  285. "STIME": "12:00",
  286. "TTY": "?",
  287. "TIME": "00:00:42",
  288. "CMD": "sleep infinity",
  289. },
  290. }, entries)
  291. var buf bytes.Buffer
  292. err := topPrint(&buf, header, entries)
  293. assert.NilError(t, err)
  294. assert.Equal(t, trim(`
  295. SERVICE # UID PID PPID C STIME TTY TIME GID CMD
  296. simple 1 root 1 1 0 12:00 ? 00:00:01 - /entrypoint
  297. noppid 1 root 1 - 0 12:00 ? 00:00:02 - /entrypoint
  298. extra-hdr 1 root 1 1 0 12:00 ? 00:00:03 1 /entrypoint
  299. multiple 1 root 1 1 0 12:00 ? 00:00:04 - /entrypoint
  300. multiple 1 root 123 1 0 12:00 ? 00:00:42 - sleep infinity
  301. `), buf.String())
  302. })
  303. }
  304. func trim(s string) string {
  305. var out bytes.Buffer
  306. for line := range strings.SplitSeq(strings.TrimSpace(s), "\n") {
  307. out.WriteString(strings.TrimSpace(line))
  308. out.WriteRune('\n')
  309. }
  310. return out.String()
  311. }