1
2
3
4
5 package cgroup_test
6
7 import (
8 "fmt"
9 "internal/runtime/cgroup"
10 "io"
11 "strings"
12 "testing"
13 )
14
15 func TestParseV1Number(t *testing.T) {
16 tests := []struct {
17 name string
18 contents string
19 want int64
20 wantErr bool
21 }{
22 {
23 name: "disabled",
24 contents: "-1\n",
25 want: -1,
26 },
27 {
28 name: "500000",
29 contents: "500000\n",
30 want: 500000,
31 },
32 {
33 name: "MaxInt64",
34 contents: "9223372036854775807\n",
35 want: 9223372036854775807,
36 },
37 {
38 name: "missing-newline",
39 contents: "500000",
40 wantErr: true,
41 },
42 {
43 name: "not-a-number",
44 contents: "123max\n",
45 wantErr: true,
46 },
47 {
48 name: "v2",
49 contents: "1000 5000\n",
50 wantErr: true,
51 },
52 }
53
54 for _, tc := range tests {
55 t.Run(tc.name, func(t *testing.T) {
56 got, err := cgroup.ParseV1Number([]byte(tc.contents))
57 if tc.wantErr {
58 if err == nil {
59 t.Fatalf("parseV1Number got err nil want non-nil")
60 }
61 return
62 }
63 if err != nil {
64 t.Fatalf("parseV1Number got err %v want nil", err)
65 }
66
67 if got != tc.want {
68 t.Errorf("parseV1Number got %d want %d", got, tc.want)
69 }
70 })
71 }
72 }
73
74 func TestParseV2Limit(t *testing.T) {
75 tests := []struct {
76 name string
77 contents string
78 want float64
79 wantOK bool
80 wantErr bool
81 }{
82 {
83 name: "disabled",
84 contents: "max 100000\n",
85 wantOK: false,
86 },
87 {
88 name: "5",
89 contents: "500000 100000\n",
90 want: 5,
91 wantOK: true,
92 },
93 {
94 name: "0.5",
95 contents: "50000 100000\n",
96 want: 0.5,
97 wantOK: true,
98 },
99 {
100 name: "2.5",
101 contents: "250000 100000\n",
102 want: 2.5,
103 wantOK: true,
104 },
105 {
106 name: "MaxInt64",
107 contents: "9223372036854775807 9223372036854775807\n",
108 want: 1,
109 wantOK: true,
110 },
111 {
112 name: "missing-newline",
113 contents: "500000 100000",
114 wantErr: true,
115 },
116 {
117 name: "v1",
118 contents: "500000\n",
119 wantErr: true,
120 },
121 {
122 name: "quota-not-a-number",
123 contents: "500000us 100000\n",
124 wantErr: true,
125 },
126 {
127 name: "period-not-a-number",
128 contents: "500000 100000us\n",
129 wantErr: true,
130 },
131 }
132
133 for _, tc := range tests {
134 t.Run(tc.name, func(t *testing.T) {
135 got, gotOK, err := cgroup.ParseV2Limit([]byte(tc.contents))
136 if tc.wantErr {
137 if err == nil {
138 t.Fatalf("parseV1Limit got err nil want non-nil")
139 }
140 return
141 }
142 if err != nil {
143 t.Fatalf("parseV2Limit got err %v want nil", err)
144 }
145
146 if gotOK != tc.wantOK {
147 t.Errorf("parseV2Limit got ok %v want %v", gotOK, tc.wantOK)
148 }
149
150 if tc.wantOK && got != tc.want {
151 t.Errorf("parseV2Limit got %f want %f", got, tc.want)
152 }
153 })
154 }
155 }
156
157 func readString(contents string) func(fd int, b []byte) (int, uintptr) {
158 r := strings.NewReader(contents)
159 return func(fd int, b []byte) (int, uintptr) {
160 n, err := r.Read(b)
161 if err != nil && err != io.EOF {
162 const dummyErrno = 42
163 return n, dummyErrno
164 }
165 return n, 0
166 }
167 }
168
169 func TestParseCPUCgroup(t *testing.T) {
170 veryLongPathName := strings.Repeat("a", cgroup.PathSize+10)
171 evenLongerPathName := strings.Repeat("a", cgroup.ParseSize+10)
172
173 tests := []struct {
174 name string
175 contents string
176 want string
177 wantVer cgroup.Version
178 wantErr bool
179 }{
180 {
181 name: "empty",
182 contents: "",
183 wantErr: true,
184 },
185 {
186 name: "too-long",
187 contents: "0::/" + veryLongPathName + "\n",
188 wantErr: true,
189 },
190 {
191 name: "too-long-line",
192 contents: "0::/" + evenLongerPathName + "\n",
193 wantErr: true,
194 },
195 {
196 name: "v1",
197 contents: `2:cpu,cpuacct:/a/b/cpu
198 1:blkio:/a/b/blkio
199 `,
200 want: "/a/b/cpu",
201 wantVer: cgroup.V1,
202 },
203 {
204 name: "v2",
205 contents: "0::/a/b/c\n",
206 want: "/a/b/c",
207 wantVer: cgroup.V2,
208 },
209 {
210 name: "mixed",
211 contents: `2:cpu,cpuacct:/a/b/cpu
212 1:blkio:/a/b/blkio
213 0::/a/b/v2
214 `,
215 want: "/a/b/cpu",
216 wantVer: cgroup.V1,
217 },
218 }
219
220 for _, tc := range tests {
221 t.Run(tc.name, func(t *testing.T) {
222 var got [cgroup.PathSize]byte
223 var scratch [cgroup.ParseSize]byte
224 n, gotVer, err := cgroup.ParseCPUCgroup(0, readString(tc.contents), got[:], scratch[:])
225 if (err != nil) != tc.wantErr {
226 t.Fatalf("parseCPURelativePath got err %v want %v", err, tc.wantErr)
227 }
228
229 if gotVer != tc.wantVer {
230 t.Errorf("parseCPURelativePath got cgroup version %d want %d", gotVer, tc.wantVer)
231 }
232
233 if string(got[:n]) != tc.want {
234 t.Errorf("parseCPURelativePath got %q want %q", string(got[:n]), tc.want)
235 }
236 })
237 }
238 }
239
240 func TestParseCPUCgroupMalformed(t *testing.T) {
241 for _, contents := range []string{
242 "\n",
243 "0\n",
244 "0:\n",
245 "0::\n",
246 "0::a\n",
247 } {
248 t.Run("", func(t *testing.T) {
249 var got [cgroup.PathSize]byte
250 var scratch [cgroup.ParseSize]byte
251 n, v, err := cgroup.ParseCPUCgroup(0, readString(contents), got[:], scratch[:])
252 if err != cgroup.ErrMalformedFile {
253 t.Errorf("ParseCPUCgroup got %q (v%d), %v, want ErrMalformedFile", string(got[:n]), v, err)
254 }
255 })
256 }
257 }
258
259 func TestContainsCPU(t *testing.T) {
260 tests := []struct {
261 in string
262 want bool
263 }{
264 {
265 in: "",
266 want: false,
267 },
268 {
269 in: ",",
270 want: false,
271 },
272 {
273 in: "cpu",
274 want: true,
275 },
276 {
277 in: "memory,cpu",
278 want: true,
279 },
280 {
281 in: "cpu,memory",
282 want: true,
283 },
284 {
285 in: "memory,cpu,block",
286 want: true,
287 },
288 {
289 in: "memory,cpuacct,block",
290 want: false,
291 },
292 }
293
294 for _, tc := range tests {
295 t.Run(tc.in, func(t *testing.T) {
296 got := cgroup.ContainsCPU([]byte(tc.in))
297 if got != tc.want {
298 t.Errorf("containsCPU(%q) got %v want %v", tc.in, got, tc.want)
299 }
300 })
301 }
302 }
303
304 func TestParseCPUMount(t *testing.T) {
305
306
307
308 const lowerPath = "/so/many/overlay/layers"
309 overlayLongLowerDir := lowerPath
310 for i := 0; len(overlayLongLowerDir) < cgroup.ScratchSize; i++ {
311 overlayLongLowerDir += fmt.Sprintf(":%s%d", lowerPath, i)
312 }
313
314 var longPath [4090]byte
315 for i := range longPath {
316 longPath[i] = byte(i)
317 }
318 escapedLongPath := escapePath(string(longPath[:]))
319 if len(escapedLongPath) <= cgroup.PathSize {
320
321 t.Fatalf("escapedLongPath is too short to test")
322 }
323
324 tests := []struct {
325 name string
326 contents string
327 cgroup string
328 version cgroup.Version
329 want string
330 wantErr bool
331 }{
332 {
333 name: "empty",
334 contents: "",
335 wantErr: true,
336 },
337 {
338 name: "invalid-root",
339 contents: "56 22 0:40 /\\1 /sys/fs/cgroup/cpu rw - cgroup cgroup rw,cpu,cpuacct\n",
340 cgroup: "/",
341 version: cgroup.V1,
342 wantErr: true,
343 },
344 {
345 name: "invalid-mount",
346 contents: "56 22 0:40 / /sys/fs/cgroup/\\1 rw - cgroup cgroup rw,cpu,cpuacct\n",
347 cgroup: "/",
348 version: cgroup.V1,
349 wantErr: true,
350 },
351 {
352 name: "v1",
353 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
354 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
355 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
356 49 22 0:37 / /sys/fs/cgroup/memory rw - cgroup cgroup rw,memory
357 54 22 0:38 / /sys/fs/cgroup/io rw - cgroup cgroup rw,io
358 56 22 0:40 / /sys/fs/cgroup/cpu rw - cgroup cgroup rw,cpu,cpuacct
359 58 22 0:42 / /sys/fs/cgroup/net rw - cgroup cgroup rw,net
360 59 22 0:43 / /sys/fs/cgroup/cpuset rw - cgroup cgroup rw,cpuset
361 `,
362 cgroup: "/",
363 version: cgroup.V1,
364 want: "/sys/fs/cgroup/cpu",
365 },
366 {
367 name: "v2",
368 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
369 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
370 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
371 25 21 0:22 / /sys/fs/cgroup rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
372 `,
373 cgroup: "/",
374 version: cgroup.V2,
375 want: "/sys/fs/cgroup",
376 },
377 {
378 name: "mixed",
379 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
380 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
381 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
382 25 21 0:22 / /sys/fs/cgroup rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
383 49 22 0:37 / /sys/fs/cgroup/memory rw - cgroup cgroup rw,memory
384 54 22 0:38 / /sys/fs/cgroup/io rw - cgroup cgroup rw,io
385 56 22 0:40 / /sys/fs/cgroup/cpu rw - cgroup cgroup rw,cpu,cpuacct
386 58 22 0:42 / /sys/fs/cgroup/net rw - cgroup cgroup rw,net
387 59 22 0:43 / /sys/fs/cgroup/cpuset rw - cgroup cgroup rw,cpuset
388 `,
389 cgroup: "/",
390 version: cgroup.V1,
391 want: "/sys/fs/cgroup/cpu",
392 },
393 {
394 name: "mixed-choose-v2",
395 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
396 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
397 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
398 25 21 0:22 / /sys/fs/cgroup rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
399 49 22 0:37 / /sys/fs/cgroup/memory rw - cgroup cgroup rw,memory
400 54 22 0:38 / /sys/fs/cgroup/io rw - cgroup cgroup rw,io
401 56 22 0:40 / /sys/fs/cgroup/cpu rw - cgroup cgroup rw,cpu,cpuacct
402 58 22 0:42 / /sys/fs/cgroup/net rw - cgroup cgroup rw,net
403 59 22 0:43 / /sys/fs/cgroup/cpuset rw - cgroup cgroup rw,cpuset
404 `,
405 cgroup: "/",
406 version: cgroup.V2,
407 want: "/sys/fs/cgroup",
408 },
409 {
410 name: "v2-escaped",
411 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
412 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
413 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
414 25 21 0:22 / /sys/fs/cgroup/tab\011tab rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
415 `,
416 cgroup: "/",
417 version: cgroup.V2,
418 want: `/sys/fs/cgroup/tab tab`,
419 },
420 {
421
422 name: "v2-longline",
423 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
424 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
425 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
426 262 31 0:72 / /tmp/overlay2/0143e063b02f4801de9c847ad1c5ddc21fd2ead00653064d0c72ea967b248870/merged rw,relatime shared:729 - overlay overlay rw,lowerdir=` + overlayLongLowerDir + `,upperdir=/tmp/diff,workdir=/tmp/work
427 25 21 0:22 / /sys/fs/cgroup rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
428 `,
429 cgroup: "/",
430 version: cgroup.V2,
431 want: "/sys/fs/cgroup",
432 },
433 {
434 name: "long-escaped-path",
435 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
436 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
437 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
438 25 21 0:22 / /sys/` + escapedLongPath + ` rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
439 `,
440 cgroup: "/",
441 version: cgroup.V2,
442 want: "/sys/" + string(longPath[:]),
443 },
444 {
445 name: "too-long-escaped-path",
446 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
447 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
448 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
449 25 21 0:22 / /sys/` + escapedLongPath + ` rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
450 `,
451 cgroup: "/container",
452 version: cgroup.V2,
453 wantErr: true,
454 },
455 {
456 name: "non-root_mount",
457 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
458 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
459 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
460 25 21 0:22 /sand /unrelated/cgroup1 rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
461 25 21 0:22 /stone /unrelated/cgroup2 rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
462 25 21 0:22 /sandbox/container/group /sys/fs/cgroup/mygroup rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
463 25 21 0:22 /sandbox /sys/fs/cgroup rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
464 25 21 0:22 / /ignored/second/match rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
465 `,
466 cgroup: "/sandbox/container",
467 version: cgroup.V2,
468 want: "/sys/fs/cgroup/container",
469 },
470 {
471 name: "v2-escaped-root",
472 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
473 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
474 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
475 25 21 0:22 /tab\011tab /sys/fs/cgroup rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
476 `,
477 cgroup: "/tab tab/container",
478 version: cgroup.V2,
479 want: `/sys/fs/cgroup/container`,
480 },
481 {
482 name: "non-root_cgroup",
483 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
484 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
485 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
486 25 21 0:22 / /sys/fs/cgroup rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
487 `,
488 cgroup: "/sandbox/container",
489 version: cgroup.V2,
490 want: "/sys/fs/cgroup/sandbox/container",
491 },
492 {
493 name: "mixed_non-root",
494 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
495 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
496 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
497 25 21 0:22 /sandbox /sys/fs/cgroup rw,nosuid,nodev,noexec - cgroup2 cgroup2 rw
498 49 22 0:37 /sandbox /sys/fs/cgroup/memory rw - cgroup cgroup rw,memory
499 54 22 0:38 /sandbox /sys/fs/cgroup/io rw - cgroup cgroup rw,io
500 56 22 0:40 /sand /unrelated/cgroup1 rw - cgroup cgroup rw,cpu,cpuacct
501 56 22 0:40 /stone /unrelated/cgroup2 rw - cgroup cgroup rw,cpu,cpuacct
502 56 22 0:40 /sandbox /sys/fs/cgroup/cpu rw - cgroup cgroup rw,cpu,cpuacct
503 56 22 0:40 /sandbox/container/group /sys/fs/cgroup/cpu/mygroup rw - cgroup cgroup rw,cpu,cpuacct
504 56 22 0:40 / /ignored/second/match rw - cgroup cgroup rw,cpu,cpuacct
505 58 22 0:42 /sandbox /sys/fs/cgroup/net rw - cgroup cgroup rw,net
506 59 22 0:43 /sandbox /sys/fs/cgroup/cpuset rw - cgroup cgroup rw,cpuset
507 `,
508 cgroup: "/sandbox/container",
509 version: cgroup.V1,
510 want: "/sys/fs/cgroup/cpu/container",
511 },
512 {
513
514
515
516
517
518 name: "out_of_namespace",
519 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
520 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
521 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
522 1243 61 0:26 /../../.. /mnt rw,nosuid,nodev,noexec,relatime shared:4 - cgroup2 cgroup2 rw
523 29 22 0:26 /../../../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:4 - cgroup2 cgroup2 rw`,
524 cgroup: "/../../../../init.scope",
525 version: cgroup.V2,
526 want: "/sys/fs/cgroup/init.scope",
527 },
528 {
529 name: "out_of_namespace-root",
530 contents: `22 1 8:1 / / rw,relatime - ext4 /dev/root rw
531 20 22 0:19 / /proc rw,nosuid,nodev,noexec - proc proc rw
532 21 22 0:20 / /sys rw,nosuid,nodev,noexec - sysfs sysfs rw
533 1243 61 0:26 /../../.. /mnt rw,nosuid,nodev,noexec,relatime shared:4 - cgroup2 cgroup2 rw
534 29 22 0:26 /../../../.. /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:4 - cgroup2 cgroup2 rw`,
535 cgroup: "/../../../..",
536 version: cgroup.V2,
537 want: "/sys/fs/cgroup",
538 },
539 }
540
541 for _, tc := range tests {
542 t.Run(tc.name, func(t *testing.T) {
543 var got [cgroup.PathSize]byte
544 var scratch [cgroup.ParseSize]byte
545 n := copy(got[:], tc.cgroup)
546 n, err := cgroup.ParseCPUMount(0, readString(tc.contents), got[:],
547 got[:n], tc.version, scratch[:])
548 if (err != nil) != tc.wantErr {
549 t.Fatalf("parseCPUMount got err %v want %v", err, tc.wantErr)
550 }
551
552 if string(got[:n]) != tc.want {
553 t.Errorf("parseCPUMount got %q want %q", string(got[:n]), tc.want)
554 }
555 })
556 }
557 }
558
559 func TestParseCPUMountMalformed(t *testing.T) {
560 for _, contents := range []string{
561 "\n",
562 "22\n",
563 "22 1 8:1\n",
564 "22 1 8:1 /\n",
565 "22 1 8:1 / /cgroup\n",
566 "22 1 8:1 / /cgroup rw\n",
567 "22 1 8:1 / /cgroup rw -\n",
568 "22 1 8:1 / /cgroup rw - \n",
569 "22 1 8:1 / /cgroup rw - cgroup\n",
570 "22 1 8:1 / /cgroup rw - cgroup cgroup\n",
571 "22 1 8:1 a /cgroup rw - cgroup cgroup cpu\n",
572 } {
573 t.Run("", func(t *testing.T) {
574 var got [cgroup.PathSize]byte
575 var scratch [cgroup.ParseSize]byte
576 n, err := cgroup.ParseCPUMount(0, readString(contents), got[:], []byte("/"), cgroup.V1, scratch[:])
577 if err != cgroup.ErrMalformedFile {
578 t.Errorf("parseCPUMount got %q, %v, want ErrMalformedFile", string(got[:n]), err)
579 }
580 })
581 }
582 }
583
584
585
586
587
588 func escapePath(s string) string {
589 out := make([]byte, 0, len(s))
590 for _, c := range []byte(s) {
591 switch c {
592 case '\\', ' ', '\t', '\n':
593 out = fmt.Appendf(out, "\\%03o", c)
594 default:
595 out = append(out, c)
596 }
597 }
598 return string(out)
599 }
600
601 func TestEscapePath(t *testing.T) {
602 tests := []struct {
603 name string
604 unescaped string
605 escaped string
606 }{
607 {
608 name: "boring",
609 unescaped: `/a/b/c`,
610 escaped: `/a/b/c`,
611 },
612 {
613 name: "space",
614 unescaped: `/a/b b/c`,
615 escaped: `/a/b\040b/c`,
616 },
617 {
618 name: "tab",
619 unescaped: `/a/b b/c`,
620 escaped: `/a/b\011b/c`,
621 },
622 {
623 name: "newline",
624 unescaped: `/a/b
625 b/c`,
626 escaped: `/a/b\012b/c`,
627 },
628 {
629 name: "slash",
630 unescaped: `/a/b\b/c`,
631 escaped: `/a/b\134b/c`,
632 },
633 {
634 name: "beginning",
635 unescaped: `\b/c`,
636 escaped: `\134b/c`,
637 },
638 {
639 name: "ending",
640 unescaped: `/a/\`,
641 escaped: `/a/\134`,
642 },
643 {
644 name: "non-utf8",
645 unescaped: "/a/b\xff\x20/c",
646 escaped: "/a/b\xff\\040/c",
647 },
648 }
649
650 t.Run("escapePath", func(t *testing.T) {
651 for _, tc := range tests {
652 t.Run(tc.name, func(t *testing.T) {
653 got := escapePath(tc.unescaped)
654 if got != tc.escaped {
655 t.Errorf("escapePath got %q want %q", got, tc.escaped)
656 }
657 })
658 }
659 })
660
661 t.Run("unescapePath", func(t *testing.T) {
662 for _, tc := range tests {
663 runTest := func(in, out []byte) {
664 n, err := cgroup.UnescapePath(out, in)
665 if err != nil {
666 t.Errorf("unescapePath got err %v want nil", err)
667 }
668 got := string(out[:n])
669 if got != tc.unescaped {
670 t.Errorf("unescapePath got %q want %q", got, tc.escaped)
671 }
672 }
673 t.Run(tc.name, func(t *testing.T) {
674 in := []byte(tc.escaped)
675 out := make([]byte, len(in))
676 runTest(in, out)
677 })
678 t.Run("inplace/"+tc.name, func(t *testing.T) {
679 in := []byte(tc.escaped)
680 runTest(in, in)
681 })
682 }
683 })
684 }
685
686 func TestUnescapeInvalidPath(t *testing.T) {
687 for _, in := range []string{
688 `/a/b\c`,
689 `/a/b\01`,
690 `/a/b\018`,
691 `/a/b\01c`,
692 `/a/b\777`,
693 `01234567890123456789`,
694 `\001\002\003\004\005\006\007\010\011`,
695 } {
696 out := make([]byte, 8)
697 t.Run(in, func(t *testing.T) {
698 _, err := cgroup.UnescapePath(out, []byte(in))
699 if err == nil {
700 t.Errorf("unescapePath got nil err, want non-nil")
701 }
702 })
703 }
704 }
705
View as plain text