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  }