github.1git.de/docker/cli@v26.1.3+incompatible/cli/command/container/formatter_stats_test.go (about) 1 package container 2 3 import ( 4 "bytes" 5 "testing" 6 7 "github.com/docker/cli/cli/command/formatter" 8 "github.com/docker/docker/pkg/stringid" 9 "gotest.tools/v3/assert" 10 is "gotest.tools/v3/assert/cmp" 11 ) 12 13 func TestContainerStatsContext(t *testing.T) { 14 containerID := stringid.GenerateRandomID() 15 16 var ctx statsContext 17 tt := []struct { 18 stats StatsEntry 19 osType string 20 expValue string 21 expHeader string 22 call func() string 23 }{ 24 {StatsEntry{Container: containerID}, "", containerID, containerHeader, ctx.Container}, 25 {StatsEntry{CPUPercentage: 5.5}, "", "5.50%", cpuPercHeader, ctx.CPUPerc}, 26 {StatsEntry{CPUPercentage: 5.5, IsInvalid: true}, "", "--", cpuPercHeader, ctx.CPUPerc}, 27 {StatsEntry{NetworkRx: 0.31, NetworkTx: 12.3}, "", "0.31B / 12.3B", netIOHeader, ctx.NetIO}, 28 {StatsEntry{NetworkRx: 0.31, NetworkTx: 12.3, IsInvalid: true}, "", "--", netIOHeader, ctx.NetIO}, 29 {StatsEntry{BlockRead: 0.1, BlockWrite: 2.3}, "", "0.1B / 2.3B", blockIOHeader, ctx.BlockIO}, 30 {StatsEntry{BlockRead: 0.1, BlockWrite: 2.3, IsInvalid: true}, "", "--", blockIOHeader, ctx.BlockIO}, 31 {StatsEntry{MemoryPercentage: 10.2}, "", "10.20%", memPercHeader, ctx.MemPerc}, 32 {StatsEntry{MemoryPercentage: 10.2, IsInvalid: true}, "", "--", memPercHeader, ctx.MemPerc}, 33 {StatsEntry{MemoryPercentage: 10.2}, "windows", "--", memPercHeader, ctx.MemPerc}, 34 {StatsEntry{Memory: 24, MemoryLimit: 30}, "", "24B / 30B", memUseHeader, ctx.MemUsage}, 35 {StatsEntry{Memory: 24, MemoryLimit: 30, IsInvalid: true}, "", "-- / --", memUseHeader, ctx.MemUsage}, 36 {StatsEntry{Memory: 24, MemoryLimit: 30}, "windows", "24B", winMemUseHeader, ctx.MemUsage}, 37 {StatsEntry{PidsCurrent: 10}, "", "10", pidsHeader, ctx.PIDs}, 38 {StatsEntry{PidsCurrent: 10, IsInvalid: true}, "", "--", pidsHeader, ctx.PIDs}, 39 {StatsEntry{PidsCurrent: 10}, "windows", "--", pidsHeader, ctx.PIDs}, 40 } 41 42 for _, te := range tt { 43 ctx = statsContext{s: te.stats, os: te.osType} 44 if v := te.call(); v != te.expValue { 45 t.Fatalf("Expected %q, got %q", te.expValue, v) 46 } 47 } 48 } 49 50 func TestContainerStatsContextWrite(t *testing.T) { 51 tt := []struct { 52 context formatter.Context 53 expected string 54 }{ 55 { 56 formatter.Context{Format: "{{InvalidFunction}}"}, 57 `template parsing error: template: :1: function "InvalidFunction" not defined`, 58 }, 59 { 60 formatter.Context{Format: "{{nil}}"}, 61 `template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`, 62 }, 63 { 64 formatter.Context{Format: "table {{.MemUsage}}"}, 65 `MEM USAGE / LIMIT 66 20B / 20B 67 -- / -- 68 `, 69 }, 70 { 71 formatter.Context{Format: "{{.Container}} {{.ID}} {{.Name}}"}, 72 `container1 abcdef foo 73 container2 -- 74 `, 75 }, 76 { 77 formatter.Context{Format: "{{.Container}} {{.CPUPerc}}"}, 78 `container1 20.00% 79 container2 -- 80 `, 81 }, 82 } 83 84 for _, te := range tt { 85 stats := []StatsEntry{ 86 { 87 Container: "container1", 88 ID: "abcdef", 89 Name: "/foo", 90 CPUPercentage: 20, 91 Memory: 20, 92 MemoryLimit: 20, 93 MemoryPercentage: 20, 94 NetworkRx: 20, 95 NetworkTx: 20, 96 BlockRead: 20, 97 BlockWrite: 20, 98 PidsCurrent: 2, 99 IsInvalid: false, 100 }, 101 { 102 Container: "container2", 103 CPUPercentage: 30, 104 Memory: 30, 105 MemoryLimit: 30, 106 MemoryPercentage: 30, 107 NetworkRx: 30, 108 NetworkTx: 30, 109 BlockRead: 30, 110 BlockWrite: 30, 111 PidsCurrent: 3, 112 IsInvalid: true, 113 }, 114 } 115 var out bytes.Buffer 116 te.context.Output = &out 117 err := statsFormatWrite(te.context, stats, "linux", false) 118 if err != nil { 119 assert.Error(t, err, te.expected) 120 } else { 121 assert.Check(t, is.Equal(te.expected, out.String())) 122 } 123 } 124 } 125 126 func TestContainerStatsContextWriteWindows(t *testing.T) { 127 cases := []struct { 128 context formatter.Context 129 expected string 130 }{ 131 { 132 formatter.Context{Format: "table {{.MemUsage}}"}, 133 `PRIV WORKING SET 134 20B 135 -- / -- 136 `, 137 }, 138 { 139 formatter.Context{Format: "{{.Container}} {{.CPUPerc}}"}, 140 `container1 20.00% 141 container2 -- 142 `, 143 }, 144 { 145 formatter.Context{Format: "{{.Container}} {{.MemPerc}} {{.PIDs}}"}, 146 `container1 -- -- 147 container2 -- -- 148 `, 149 }, 150 } 151 stats := []StatsEntry{ 152 { 153 Container: "container1", 154 CPUPercentage: 20, 155 Memory: 20, 156 MemoryLimit: 20, 157 MemoryPercentage: 20, 158 NetworkRx: 20, 159 NetworkTx: 20, 160 BlockRead: 20, 161 BlockWrite: 20, 162 PidsCurrent: 2, 163 IsInvalid: false, 164 }, 165 { 166 Container: "container2", 167 CPUPercentage: 30, 168 Memory: 30, 169 MemoryLimit: 30, 170 MemoryPercentage: 30, 171 NetworkRx: 30, 172 NetworkTx: 30, 173 BlockRead: 30, 174 BlockWrite: 30, 175 PidsCurrent: 3, 176 IsInvalid: true, 177 }, 178 } 179 180 for _, tc := range cases { 181 tc := tc 182 t.Run(string(tc.context.Format), func(t *testing.T) { 183 var out bytes.Buffer 184 tc.context.Output = &out 185 err := statsFormatWrite(tc.context, stats, "windows", false) 186 if err != nil { 187 assert.Error(t, err, tc.expected) 188 } else { 189 assert.Equal(t, out.String(), tc.expected) 190 } 191 }) 192 } 193 } 194 195 func TestContainerStatsContextWriteWithNoStats(t *testing.T) { 196 var out bytes.Buffer 197 198 cases := []struct { 199 context formatter.Context 200 expected string 201 }{ 202 { 203 formatter.Context{ 204 Format: "{{.Container}}", 205 Output: &out, 206 }, 207 "", 208 }, 209 { 210 formatter.Context{ 211 Format: "table {{.Container}}", 212 Output: &out, 213 }, 214 "CONTAINER\n", 215 }, 216 { 217 formatter.Context{ 218 Format: "table {{.Container}}\t{{.CPUPerc}}", 219 Output: &out, 220 }, 221 "CONTAINER CPU %\n", 222 }, 223 } 224 225 for _, tc := range cases { 226 tc := tc 227 t.Run(string(tc.context.Format), func(t *testing.T) { 228 err := statsFormatWrite(tc.context, []StatsEntry{}, "linux", false) 229 assert.NilError(t, err) 230 assert.Equal(t, out.String(), tc.expected) 231 // Clean buffer 232 out.Reset() 233 }) 234 } 235 } 236 237 func TestContainerStatsContextWriteWithNoStatsWindows(t *testing.T) { 238 var out bytes.Buffer 239 240 cases := []struct { 241 context formatter.Context 242 expected string 243 }{ 244 { 245 formatter.Context{ 246 Format: "{{.Container}}", 247 Output: &out, 248 }, 249 "", 250 }, 251 { 252 formatter.Context{ 253 Format: "table {{.Container}}\t{{.MemUsage}}", 254 Output: &out, 255 }, 256 "CONTAINER PRIV WORKING SET\n", 257 }, 258 { 259 formatter.Context{ 260 Format: "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}", 261 Output: &out, 262 }, 263 "CONTAINER CPU % PRIV WORKING SET\n", 264 }, 265 } 266 267 for _, tc := range cases { 268 tc := tc 269 t.Run(string(tc.context.Format), func(t *testing.T) { 270 err := statsFormatWrite(tc.context, []StatsEntry{}, "windows", false) 271 assert.NilError(t, err) 272 assert.Equal(t, out.String(), tc.expected) 273 out.Reset() 274 }) 275 } 276 } 277 278 func TestContainerStatsContextWriteTrunc(t *testing.T) { 279 var out bytes.Buffer 280 281 contexts := []struct { 282 context formatter.Context 283 trunc bool 284 expected string 285 }{ 286 { 287 formatter.Context{ 288 Format: "{{.ID}}", 289 Output: &out, 290 }, 291 false, 292 "b95a83497c9161c9b444e3d70e1a9dfba0c1840d41720e146a95a08ebf938afc\n", 293 }, 294 { 295 formatter.Context{ 296 Format: "{{.ID}}", 297 Output: &out, 298 }, 299 true, 300 "b95a83497c91\n", 301 }, 302 } 303 304 for _, context := range contexts { 305 statsFormatWrite(context.context, []StatsEntry{{ID: "b95a83497c9161c9b444e3d70e1a9dfba0c1840d41720e146a95a08ebf938afc"}}, "linux", context.trunc) 306 assert.Check(t, is.Equal(context.expected, out.String())) 307 // Clean buffer 308 out.Reset() 309 } 310 } 311 312 func BenchmarkStatsFormat(b *testing.B) { 313 b.ReportAllocs() 314 stats := genStats() 315 316 for i := 0; i < b.N; i++ { 317 for _, s := range stats { 318 _ = s.CPUPerc() 319 _ = s.MemUsage() 320 _ = s.MemPerc() 321 _ = s.NetIO() 322 _ = s.BlockIO() 323 _ = s.PIDs() 324 } 325 } 326 } 327 328 func genStats() []statsContext { 329 entry := statsContext{s: StatsEntry{ 330 CPUPercentage: 12.3456789, 331 Memory: 123.456789, 332 MemoryLimit: 987.654321, 333 MemoryPercentage: 12.3456789, 334 BlockRead: 123.456789, 335 BlockWrite: 987.654321, 336 NetworkRx: 123.456789, 337 NetworkTx: 987.654321, 338 PidsCurrent: 123456789, 339 }} 340 stats := make([]statsContext, 100) 341 for i := 0; i < 100; i++ { 342 stats = append(stats, entry) 343 } 344 return stats 345 }