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