github.com/jaypipes/ghw@v0.21.1/pkg/block/block_linux_test.go (about) 1 // 2 // Use and distribution licensed under the Apache license version 2. 3 // 4 // See the COPYING file in the root project directory for full text. 5 // 6 7 //go:build linux 8 // +build linux 9 10 package block 11 12 import ( 13 "fmt" 14 "os" 15 "path/filepath" 16 "reflect" 17 "testing" 18 19 "github.com/jaypipes/ghw/pkg/context" 20 "github.com/jaypipes/ghw/pkg/linuxpath" 21 "github.com/jaypipes/ghw/pkg/option" 22 "github.com/jaypipes/ghw/pkg/util" 23 ) 24 25 func TestParseMountEntry(t *testing.T) { 26 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_BLOCK"); ok { 27 t.Skip("Skipping block tests.") 28 } 29 30 tests := []struct { 31 line string 32 expected *mountEntry 33 }{ 34 { 35 line: "/dev/sda6 / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0", 36 expected: &mountEntry{ 37 Partition: "/dev/sda6", 38 Mountpoint: "/", 39 FilesystemType: "ext4", 40 Options: []string{ 41 "rw", 42 "relatime", 43 "errors=remount-ro", 44 "data=ordered", 45 }, 46 }, 47 }, 48 { 49 line: "/dev/sda8 /home/Name\\040with\\040spaces ext4 ro 0 0", 50 expected: &mountEntry{ 51 Partition: "/dev/sda8", 52 Mountpoint: "/home/Name with spaces", 53 FilesystemType: "ext4", 54 Options: []string{ 55 "ro", 56 }, 57 }, 58 }, 59 { 60 // Whoever might do this in real life should be quarantined and 61 // placed in administrative segregation 62 line: "/dev/sda8 /home/Name\\011with\\012tab&newline ext4 ro 0 0", 63 expected: &mountEntry{ 64 Partition: "/dev/sda8", 65 Mountpoint: "/home/Name\twith\ntab&newline", 66 FilesystemType: "ext4", 67 Options: []string{ 68 "ro", 69 }, 70 }, 71 }, 72 { 73 line: "/dev/sda1 /home/Name\\\\withslash ext4 ro 0 0", 74 expected: &mountEntry{ 75 Partition: "/dev/sda1", 76 Mountpoint: "/home/Name\\withslash", 77 FilesystemType: "ext4", 78 Options: []string{ 79 "ro", 80 }, 81 }, 82 }, 83 { 84 line: "Indy, bad dates", 85 expected: nil, 86 }, 87 } 88 89 for x, test := range tests { 90 actual := parseMountEntry(test.line) 91 if test.expected == nil { 92 if actual != nil { 93 t.Fatalf("Expected nil, but got %v", actual) 94 } 95 } else if !reflect.DeepEqual(test.expected, actual) { 96 t.Fatalf("In test %d, expected %v == %v", x, test.expected, actual) 97 } 98 } 99 } 100 101 func TestDiskTypes(t *testing.T) { 102 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_BLOCK"); ok { 103 t.Skip("Skipping block tests.") 104 } 105 106 type entry struct { 107 driveType DriveType 108 storageController StorageController 109 } 110 111 tests := []struct { 112 line string 113 expected entry 114 }{ 115 { 116 line: "sda6", 117 expected: entry{ 118 driveType: DRIVE_TYPE_HDD, 119 storageController: STORAGE_CONTROLLER_SCSI, 120 }, 121 }, 122 { 123 line: "nvme0n1", 124 expected: entry{ 125 driveType: DRIVE_TYPE_SSD, 126 storageController: STORAGE_CONTROLLER_NVME, 127 }, 128 }, 129 { 130 line: "vda1", 131 expected: entry{ 132 driveType: DRIVE_TYPE_HDD, 133 storageController: STORAGE_CONTROLLER_VIRTIO, 134 }, 135 }, 136 { 137 line: "xvda1", 138 expected: entry{ 139 driveType: DRIVE_TYPE_HDD, 140 storageController: STORAGE_CONTROLLER_SCSI, 141 }, 142 }, 143 { 144 line: "fda1", 145 expected: entry{ 146 driveType: DRIVE_TYPE_FDD, 147 storageController: STORAGE_CONTROLLER_UNKNOWN, 148 }, 149 }, 150 { 151 line: "sr0", 152 expected: entry{ 153 driveType: DRIVE_TYPE_ODD, 154 storageController: STORAGE_CONTROLLER_SCSI, 155 }, 156 }, 157 { 158 line: "mmcblk0", 159 expected: entry{ 160 driveType: DRIVE_TYPE_SSD, 161 storageController: STORAGE_CONTROLLER_MMC, 162 }, 163 }, 164 { 165 line: "Indy, bad dates", 166 expected: entry{ 167 driveType: DRIVE_TYPE_UNKNOWN, 168 storageController: STORAGE_CONTROLLER_UNKNOWN, 169 }, 170 }, 171 { 172 line: "loop0", 173 expected: entry{ 174 driveType: DRIVE_TYPE_VIRTUAL, 175 storageController: STORAGE_CONTROLLER_LOOP, 176 }, 177 }, 178 } 179 180 for _, test := range tests { 181 gotDriveType, gotStorageController := diskTypes(test.line) 182 if test.expected.driveType != gotDriveType { 183 t.Fatalf( 184 "For %s, expected drive type %s, but got %s", 185 test.line, test.expected.driveType, gotDriveType, 186 ) 187 } 188 if test.expected.storageController != gotStorageController { 189 t.Fatalf( 190 "For %s, expected storage controller %s, but got %s", 191 test.line, test.expected.storageController, gotStorageController, 192 ) 193 } 194 } 195 } 196 197 func TestDiskPartLabel(t *testing.T) { 198 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_BLOCK"); ok { 199 t.Skip("Skipping block tests.") 200 } 201 baseDir, _ := os.MkdirTemp("", "test") 202 defer os.RemoveAll(baseDir) 203 ctx := context.New() 204 ctx.Chroot = baseDir 205 paths := linuxpath.New(ctx) 206 partLabel := "TEST_LABEL_GHW" 207 208 _ = os.MkdirAll(paths.SysBlock, 0755) 209 _ = os.MkdirAll(paths.RunUdevData, 0755) 210 211 // Emulate a disk with one partition with label TEST_LABEL_GHW 212 _ = os.Mkdir(filepath.Join(paths.SysBlock, "sda"), 0755) 213 _ = os.Mkdir(filepath.Join(paths.SysBlock, "sda", "sda1"), 0755) 214 _ = os.WriteFile(filepath.Join(paths.SysBlock, "sda", "sda1", "dev"), []byte("259:0\n"), 0644) 215 _ = os.WriteFile(filepath.Join(paths.RunUdevData, "b259:0"), []byte(fmt.Sprintf("E:ID_PART_ENTRY_NAME=%s\n", partLabel)), 0644) 216 label := diskPartLabel(paths, "sda", "sda1") 217 if label != partLabel { 218 t.Fatalf("Got label %s but expected %s", label, partLabel) 219 } 220 221 // Check empty label if not found 222 label = diskPartLabel(paths, "sda", "sda2") 223 if label != util.UNKNOWN { 224 t.Fatalf("Got label %s, but expected %s label", label, util.UNKNOWN) 225 } 226 } 227 228 func TestDiskFSLabel(t *testing.T) { 229 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_BLOCK"); ok { 230 t.Skip("Skipping block tests.") 231 } 232 baseDir, _ := os.MkdirTemp("", "test") 233 defer os.RemoveAll(baseDir) 234 ctx := context.New() 235 ctx.Chroot = baseDir 236 paths := linuxpath.New(ctx) 237 fsLabel := "TEST_LABEL_GHW" 238 239 _ = os.MkdirAll(paths.SysBlock, 0755) 240 _ = os.MkdirAll(paths.RunUdevData, 0755) 241 242 // Emulate a disk with one partition with label TEST_LABEL_GHW 243 _ = os.Mkdir(filepath.Join(paths.SysBlock, "sda"), 0755) 244 _ = os.Mkdir(filepath.Join(paths.SysBlock, "sda", "sda1"), 0755) 245 _ = os.WriteFile(filepath.Join(paths.SysBlock, "sda", "sda1", "dev"), []byte("259:0\n"), 0644) 246 _ = os.WriteFile(filepath.Join(paths.RunUdevData, "b259:0"), []byte(fmt.Sprintf("E:ID_FS_LABEL=%s\n", fsLabel)), 0644) 247 label := diskFSLabel(paths, "sda", "sda1") 248 if label != fsLabel { 249 t.Fatalf("Got label %s but expected %s", label, fsLabel) 250 } 251 252 // Check empty label if not found 253 label = diskFSLabel(paths, "sda", "sda2") 254 if label != util.UNKNOWN { 255 t.Fatalf("Got label %s, but expected %s label", label, util.UNKNOWN) 256 } 257 } 258 259 func TestDiskTypeUdev(t *testing.T) { 260 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_BLOCK"); ok { 261 t.Skip("Skipping block tests.") 262 } 263 baseDir, _ := os.MkdirTemp("", "test") 264 defer os.RemoveAll(baseDir) 265 ctx := context.New() 266 ctx.Chroot = baseDir 267 paths := linuxpath.New(ctx) 268 expectedPartType := "ext4" 269 270 _ = os.MkdirAll(paths.SysBlock, 0755) 271 _ = os.MkdirAll(paths.RunUdevData, 0755) 272 273 // Emulate a disk with one partition with label TEST_LABEL_GHW 274 _ = os.Mkdir(filepath.Join(paths.SysBlock, "sda"), 0755) 275 _ = os.Mkdir(filepath.Join(paths.SysBlock, "sda", "sda1"), 0755) 276 _ = os.WriteFile(filepath.Join(paths.SysBlock, "sda", "sda1", "dev"), []byte("259:0\n"), 0644) 277 _ = os.WriteFile(filepath.Join(paths.RunUdevData, "b259:0"), []byte(fmt.Sprintf("E:ID_FS_TYPE=%s\n", expectedPartType)), 0644) 278 pt := diskPartTypeUdev(paths, "sda", "sda1") 279 if pt != expectedPartType { 280 t.Fatalf("Got partition type %s but expected %s", pt, expectedPartType) 281 } 282 283 // Check empty fs if not found 284 pt = diskPartTypeUdev(paths, "sda", "sda2") 285 if pt != util.UNKNOWN { 286 t.Fatalf("Got partition type %s, but expected %s", pt, util.UNKNOWN) 287 } 288 } 289 290 func TestDiskPartUUID(t *testing.T) { 291 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_BLOCK"); ok { 292 t.Skip("Skipping block tests.") 293 } 294 baseDir, _ := os.MkdirTemp("", "test") 295 defer os.RemoveAll(baseDir) 296 ctx := context.New() 297 ctx.Chroot = baseDir 298 paths := linuxpath.New(ctx) 299 partUUID := "11111111-1111-1111-1111-111111111111" 300 301 _ = os.MkdirAll(paths.SysBlock, 0755) 302 _ = os.MkdirAll(paths.RunUdevData, 0755) 303 304 // Emulate a disk with one partition with uuid 305 _ = os.Mkdir(filepath.Join(paths.SysBlock, "sda"), 0755) 306 _ = os.Mkdir(filepath.Join(paths.SysBlock, "sda", "sda1"), 0755) 307 _ = os.WriteFile(filepath.Join(paths.SysBlock, "sda", "sda1", "dev"), []byte("259:0\n"), 0644) 308 _ = os.WriteFile(filepath.Join(paths.RunUdevData, "b259:0"), []byte(fmt.Sprintf("E:ID_PART_ENTRY_UUID=%s\n", partUUID)), 0644) 309 uuid := diskPartUUID(paths, "sda", "sda1") 310 if uuid != partUUID { 311 t.Fatalf("Got uuid %s but expected %s", uuid, partUUID) 312 } 313 314 // Check empty uuid if not found 315 uuid = diskPartUUID(paths, "sda", "sda2") 316 if uuid != util.UNKNOWN { 317 t.Fatalf("Got uuid %s, but expected %s label", uuid, util.UNKNOWN) 318 } 319 } 320 321 // TestLoopDevicesWithOption tests to see if we find loop devices when the option is activated 322 func TestLoopDevicesWithOption(t *testing.T) { 323 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_BLOCK"); ok { 324 t.Skip("Skipping block tests.") 325 } 326 baseDir, _ := os.MkdirTemp("", "test") 327 defer os.RemoveAll(baseDir) 328 ctx := context.New(option.WithNullAlerter(), option.WithDisableTools()) 329 ctx.Chroot = baseDir 330 paths := linuxpath.New(ctx) 331 fsType := "ext4" 332 expectedLoopName := "loop0" 333 loopNotUsed := "loop1" 334 loopPartitionName := "loop0p1" 335 336 _ = os.MkdirAll(paths.SysBlock, 0755) 337 _ = os.MkdirAll(paths.RunUdevData, 0755) 338 339 // Emulate a loop device with one partition and another loop deviced not used 340 _ = os.Mkdir(filepath.Join(paths.SysBlock, expectedLoopName), 0755) 341 _ = os.Mkdir(filepath.Join(paths.SysBlock, loopNotUsed), 0755) 342 _ = os.Mkdir(filepath.Join(paths.SysBlock, expectedLoopName, "queue"), 0755) 343 _ = os.Mkdir(filepath.Join(paths.SysBlock, loopNotUsed, "queue"), 0755) 344 _ = os.WriteFile(filepath.Join(paths.SysBlock, expectedLoopName, "queue", "rotational"), []byte("1\n"), 0644) 345 _ = os.WriteFile(filepath.Join(paths.SysBlock, expectedLoopName, "size"), []byte("62810112\n"), 0644) 346 _ = os.WriteFile(filepath.Join(paths.SysBlock, loopNotUsed, "size"), []byte("0\n"), 0644) 347 _ = os.Mkdir(filepath.Join(paths.SysBlock, expectedLoopName, loopPartitionName), 0755) 348 _ = os.WriteFile(filepath.Join(paths.SysBlock, expectedLoopName, loopPartitionName, "dev"), []byte("259:0\n"), 0644) 349 _ = os.WriteFile(filepath.Join(paths.SysBlock, expectedLoopName, loopPartitionName, "size"), []byte("102400\n"), 0644) 350 _ = os.WriteFile(filepath.Join(paths.RunUdevData, "b259:0"), []byte(fmt.Sprintf("E:ID_FS_TYPE=%s\n", fsType)), 0644) 351 d := disks(ctx, paths) 352 // There should be one disk, the other should be ignored due to 0 size 353 if len(d) != 1 { 354 t.Fatalf("expected one disk device but the function reported %d", len(d)) 355 } 356 foundDisk := d[0] 357 // Should be the one we faked 358 if foundDisk.Name != expectedLoopName { 359 t.Fatalf("got loop device %s but expected %s", foundDisk.Name, expectedLoopName) 360 } 361 // Should have only one partition 362 if len(foundDisk.Partitions) != 1 { 363 t.Fatalf("expected one partition but the function reported %d", len(foundDisk.Partitions)) 364 } 365 // Name should match 366 if foundDisk.Partitions[0].Name != loopPartitionName { 367 t.Fatalf("got partition %s but expected %s", foundDisk.Partitions[0], loopPartitionName) 368 } 369 }