1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
package memlimit
import (
"reflect"
"testing"
)
func TestParseMountInfoLine(t *testing.T) {
tests := []struct {
name string
input string
want mountInfo
wantErr string
}{
{
name: "valid line with optional field",
input: "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue",
want: mountInfo{
Root: "/mnt1",
MountPoint: "/mnt2",
FilesystemType: "ext3",
SuperOptions: "rw,errors=continue",
},
},
{
name: "valid line without optional field",
input: "731 771 0:59 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw",
want: mountInfo{
Root: "/sysrq-trigger",
MountPoint: "/proc/sysrq-trigger",
FilesystemType: "proc",
SuperOptions: "rw",
},
},
{
name: "valid line with minimal fields (no optional fields)",
input: "25 1 0:22 / /dev rw - devtmpfs udev rw",
want: mountInfo{
Root: "/",
MountPoint: "/dev",
FilesystemType: "devtmpfs",
SuperOptions: "rw",
},
},
{
name: "no separator",
input: "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 ext3 /dev/root rw,errors=continue",
wantErr: `invalid separator`,
},
{
name: "not enough fields on left side",
input: "36 35 98:0 /mnt1 /mnt2 - ext3 /dev/root rw,errors=continue",
wantErr: `not enough fields before separator: [36 35 98:0 /mnt1 /mnt2]`,
},
{
name: "not enough fields on right side",
input: "36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3",
wantErr: `not enough fields after separator: [ext3]`,
},
{
name: "empty line",
input: "",
wantErr: `empty line`,
},
{
name: "6 fields on left side (no optional field), should add empty optional field",
input: "100 1 8:2 / /data rw - ext4 /dev/sda2 rw,relatime",
want: mountInfo{
Root: "/",
MountPoint: "/data",
FilesystemType: "ext4",
SuperOptions: "rw,relatime",
},
},
{
name: "multiple optional fields on left side (issue #26)",
input: "465 34 253:0 / / rw,relatime shared:409 master:1 - xfs /dev/mapper/fedora-root rw,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota",
want: mountInfo{
Root: "/",
MountPoint: "/",
FilesystemType: "xfs",
SuperOptions: "rw,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota",
},
},
{
name: "super options have spaces (issue #28)",
input: `1391 1160 0:151 / /Docker/host rw,noatime - 9p C:\134Program\040Files\134Docker\134Docker\134resources rw,dirsync,aname=drvfs;path=C:\Program Files\Docker\Docker\resources;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=3,wfd=3`,
want: mountInfo{
Root: "/",
MountPoint: "/Docker/host",
FilesystemType: "9p",
SuperOptions: `rw,dirsync,aname=drvfs;path=C:\Program Files\Docker\Docker\resources;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=3,wfd=3`,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseMountInfoLine(tt.input)
if tt.wantErr != "" {
if err == nil {
t.Fatalf("expected an error containing %q, got nil", tt.wantErr)
}
if err.Error() != tt.wantErr {
t.Fatalf("expected error containing %q, got %q", tt.wantErr, err.Error())
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("expected %+v, got %+v", tt.want, got)
}
})
}
}
func TestParseCgroupHierarchyLine(t *testing.T) {
tests := []struct {
name string
input string
want cgroupHierarchy
wantErr string
}{
{
name: "valid line with multiple controllers",
input: "5:cpuacct,cpu,cpuset:/daemons",
want: cgroupHierarchy{
HierarchyID: "5",
ControllerList: "cpuacct,cpu,cpuset",
CgroupPath: "/daemons",
},
},
{
name: "valid line with no controllers (cgroup v2)",
input: "0::/system.slice/docker.service",
want: cgroupHierarchy{
HierarchyID: "0",
ControllerList: "",
CgroupPath: "/system.slice/docker.service",
},
},
{
name: "invalid line - only two fields",
input: "5:cpuacct,cpu,cpuset",
wantErr: "not enough fields: [5 cpuacct,cpu,cpuset]",
},
{
name: "invalid line - too many fields",
input: "5:cpuacct,cpu:cpuset:/daemons:extra",
wantErr: "too many fields: [5 cpuacct,cpu cpuset /daemons extra]",
},
{
name: "empty line",
input: "",
wantErr: "empty line",
},
{
name: "line with empty controller list but valid fields",
input: "2::/my_cgroup",
want: cgroupHierarchy{
HierarchyID: "2",
ControllerList: "",
CgroupPath: "/my_cgroup",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseCgroupHierarchyLine(tt.input)
if tt.wantErr != "" {
if err == nil {
t.Fatalf("expected an error containing %q, got nil", tt.wantErr)
}
if err.Error() != tt.wantErr {
t.Fatalf("expected error containing %q, got %q", tt.wantErr, err.Error())
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("expected %+v, got %+v", tt.want, got)
}
})
}
}
func TestResolveCgroupPath(t *testing.T) {
tests := []struct {
name string
mountpoint string
root string
cgroupRelPath string
want string
wantErr string
}{
{
name: "exact match with both root and cgroupRelPath as '/'",
mountpoint: "/fake/mount",
root: "/",
cgroupRelPath: "/",
want: "/fake/mount",
},
{
name: "exact match with a non-root path",
mountpoint: "/fake/mount",
root: "/container0",
cgroupRelPath: "/container0",
want: "/fake/mount",
},
{
name: "valid subpath under root",
mountpoint: "/fake/mount",
root: "/container0",
cgroupRelPath: "/container0/group1",
want: "/fake/mount/group1",
},
{
name: "invalid cgroup path outside root",
mountpoint: "/fake/mount",
root: "/container0",
cgroupRelPath: "/other_container",
wantErr: "invalid cgroup path: /other_container is not under root /container0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := resolveCgroupPath(tt.mountpoint, tt.root, tt.cgroupRelPath)
if tt.wantErr != "" {
if err == nil {
t.Fatalf("expected an error containing %q, got nil", tt.wantErr)
}
if err.Error() != tt.wantErr {
t.Fatalf("expected error containing %q, got %q", tt.wantErr, err.Error())
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != tt.want {
t.Fatalf("expected path %q, got %q", tt.want, got)
}
})
}
}
|