github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/sysfs/syspath_test.go (about)

     1  /*
     2  Copyright 2020 The OpenEBS Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package sysfs
    18  
    19  import (
    20  	"os"
    21  	"path/filepath"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  
    26  	"github.com/openebs/node-disk-manager/blockdevice"
    27  )
    28  
    29  func TestGetParent(t *testing.T) {
    30  	tests := map[string]struct {
    31  		sysfsDevice      *Device
    32  		wantedDeviceName string
    33  		wantOk           bool
    34  	}{
    35  		"[block] given block device is a parent": {
    36  			sysfsDevice: &Device{
    37  				deviceName: "sda",
    38  				path:       "/dev/sda",
    39  				sysPath:    "/sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/",
    40  			},
    41  			wantedDeviceName: "",
    42  			wantOk:           false,
    43  		},
    44  		"[block] given blockdevice is a partition": {
    45  			sysfsDevice: &Device{
    46  				deviceName: "sda4",
    47  				path:       "/dev/sda4",
    48  				sysPath:    "/sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda4/",
    49  			},
    50  			wantedDeviceName: "sda",
    51  			wantOk:           true,
    52  		},
    53  		"[nvme] given blockdevice is a parent": {
    54  			sysfsDevice: &Device{
    55  				deviceName: "nvme0n1",
    56  				path:       "/dev/nvme0n1",
    57  				sysPath:    "/sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1/",
    58  			},
    59  			wantedDeviceName: "",
    60  			wantOk:           false,
    61  		},
    62  		"[nvme] given blockdevice is a partition": {
    63  			sysfsDevice: &Device{
    64  				deviceName: "nvme0n1p1",
    65  				path:       "/dev/nvme0n1p1",
    66  				sysPath:    "/sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1/nvme0n1p1/",
    67  			},
    68  			wantedDeviceName: "nvme0n1",
    69  			wantOk:           true,
    70  		},
    71  		"[nvme-subsystem] given blockdevice is a parent": {
    72  			sysfsDevice: &Device{
    73  				deviceName: "nvme0n1",
    74  				path:       "/dev/nvme0n1",
    75  				sysPath:    "/sys/devices/virtual/nvme-subsystem/nvme-subsys0/nvme0n1/",
    76  			},
    77  			wantedDeviceName: "",
    78  			wantOk:           false,
    79  		},
    80  		"[nvme-subsystem] given blockdevice is a partition": {
    81  			sysfsDevice: &Device{
    82  				deviceName: "nvme0n1p1",
    83  				path:       "/dev/nvme0n1p1",
    84  				sysPath:    "/sys/devices/virtual/nvme-subsystem/nvme-subsys0/nvme0n1/nvme0n1p1/",
    85  			},
    86  			wantedDeviceName: "nvme0n1",
    87  			wantOk:           true,
    88  		},
    89  	}
    90  	for name, test := range tests {
    91  		t.Run(name, func(t *testing.T) {
    92  			gotDeviceName, gotOk := test.sysfsDevice.getParent()
    93  			assert.Equal(t, test.wantedDeviceName, gotDeviceName)
    94  			assert.Equal(t, test.wantOk, gotOk)
    95  		})
    96  	}
    97  }
    98  
    99  func TestGetDeviceSysPath(t *testing.T) {
   100  	tmp := sysFSDirectoryPath
   101  	sysFSDirectoryPath = filepath.Join(t.TempDir(), "sys") + "/"
   102  	t.Cleanup(func() {
   103  		sysFSDirectoryPath = tmp
   104  	})
   105  	pciPath := "devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda"
   106  
   107  	// create top level sys directory
   108  	os.MkdirAll(sysFSDirectoryPath, 0700)
   109  	// create block directory
   110  	os.MkdirAll(filepath.Join(sysFSDirectoryPath, "class", "block"), 0700)
   111  	// create devices directory
   112  	os.MkdirAll(filepath.Join(sysFSDirectoryPath, "devices"), 0700)
   113  
   114  	// create device directory
   115  	os.MkdirAll(filepath.Join(sysFSDirectoryPath, pciPath), 0700)
   116  	os.Symlink(filepath.Join(sysFSDirectoryPath, pciPath),
   117  		filepath.Join(sysFSDirectoryPath, "class", "block", "sda"))
   118  
   119  	tests := map[string]struct {
   120  		devicePath string
   121  		want       string
   122  		wantErr    bool
   123  	}{
   124  		"devicenode name is used": {
   125  			devicePath: "/dev/sda",
   126  			want:       filepath.Join(sysFSDirectoryPath, pciPath) + "/",
   127  			wantErr:    false,
   128  		},
   129  		"actual syspath is used": {
   130  			devicePath: filepath.Join(sysFSDirectoryPath, pciPath) + "/",
   131  			want:       filepath.Join(sysFSDirectoryPath, pciPath) + "/",
   132  			wantErr:    false,
   133  		},
   134  		"class/block path is used": {
   135  			devicePath: filepath.Join(sysFSDirectoryPath, "class",
   136  				"block", "sda"),
   137  			want:    filepath.Join(sysFSDirectoryPath, pciPath) + "/",
   138  			wantErr: false,
   139  		},
   140  	}
   141  	for name, tt := range tests {
   142  		t.Run(name, func(t *testing.T) {
   143  			gotSysPath, err := getDeviceSysPath(tt.devicePath)
   144  			if err != nil {
   145  				t.Errorf("error getDeviceSysPath() for %s, error: %v", tt.devicePath, err)
   146  			}
   147  			assert.Equal(t, tt.want, gotSysPath)
   148  		})
   149  	}
   150  }
   151  
   152  func TestSysFsDeviceGetPartitions(t *testing.T) {
   153  	tmpDir := t.TempDir()
   154  	tests := map[string]struct {
   155  		fileEntries []string
   156  		sysfsDevice *Device
   157  		want        []string
   158  		wantOk      bool
   159  	}{
   160  		"device is a partition": {
   161  			fileEntries: nil,
   162  			sysfsDevice: &Device{
   163  				deviceName: "sda1",
   164  				sysPath: filepath.Join(tmpDir,
   165  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/",
   166  				path: "/dev/sda1",
   167  			},
   168  			want:   []string{},
   169  			wantOk: true,
   170  		},
   171  		"device is a parent disk, with no partition": {
   172  			fileEntries: nil,
   173  			sysfsDevice: &Device{
   174  				deviceName: "sda",
   175  				sysPath: filepath.Join(tmpDir,
   176  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/",
   177  				path: "/dev/sda",
   178  			},
   179  			want:   []string{},
   180  			wantOk: true,
   181  		},
   182  		"device is a parent disk, with multiple partitions": {
   183  			fileEntries: []string{"sdb1", "sdb2", "sdb3"},
   184  			sysfsDevice: &Device{
   185  				deviceName: "sdb",
   186  				sysPath: filepath.Join(tmpDir,
   187  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb") + "/",
   188  				path: "/dev/sdb",
   189  			},
   190  			want:   []string{"sdb1", "sdb2", "sdb3"},
   191  			wantOk: true,
   192  		},
   193  		"nvme device, with multiple partitions": {
   194  			fileEntries: []string{"nvme0n1p1", "nvme0n1p2", "nvme0n1p3"},
   195  			sysfsDevice: &Device{
   196  				deviceName: "nvme0n1",
   197  				sysPath: filepath.Join(tmpDir,
   198  					"sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1") + "/",
   199  				path: "/dev/nvme0n1",
   200  			},
   201  			want:   []string{"nvme0n1p1", "nvme0n1p2", "nvme0n1p3"},
   202  			wantOk: true,
   203  		},
   204  	}
   205  	for name, tt := range tests {
   206  		t.Run(name, func(t *testing.T) {
   207  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   208  			for _, file := range tt.fileEntries {
   209  				os.Create(filepath.Join(tt.sysfsDevice.sysPath, file))
   210  			}
   211  			got, gotOk := tt.sysfsDevice.getPartitions()
   212  			assert.Equal(t, tt.wantOk, gotOk)
   213  			assert.Equal(t, tt.want, got)
   214  			os.RemoveAll(tt.sysfsDevice.sysPath)
   215  		})
   216  	}
   217  }
   218  
   219  func TestSysFsDeviceGetHolders(t *testing.T) {
   220  	tmpDir := t.TempDir()
   221  	tests := map[string]struct {
   222  		fileEntries     []string
   223  		sysfsDevice     *Device
   224  		createHolderDir bool
   225  		want            []string
   226  		wantOk          bool
   227  	}{
   228  		"device with empty holder directory": {
   229  			fileEntries: []string{},
   230  			sysfsDevice: &Device{
   231  				deviceName: "sda",
   232  				sysPath: filepath.Join(tmpDir,
   233  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/",
   234  				path: "/dev/sda",
   235  			},
   236  			createHolderDir: true,
   237  			want:            []string{},
   238  			wantOk:          true,
   239  		},
   240  		"device with holders": {
   241  			fileEntries: []string{"dm-0", "dm-1"},
   242  			sysfsDevice: &Device{
   243  				deviceName: "sdb",
   244  				sysPath: filepath.Join(tmpDir,
   245  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb") + "/",
   246  				path: "/dev/sdb",
   247  			},
   248  			createHolderDir: true,
   249  			want:            []string{"dm-0", "dm-1"},
   250  			wantOk:          true,
   251  		},
   252  		"device without holders and no holder directory": {
   253  			fileEntries: []string{},
   254  			sysfsDevice: &Device{
   255  				deviceName: "sdc",
   256  				sysPath: filepath.Join(tmpDir,
   257  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdc") + "/",
   258  				path: "/dev/sdc",
   259  			},
   260  			createHolderDir: false,
   261  			want:            nil,
   262  			wantOk:          false,
   263  		},
   264  	}
   265  	for name, tt := range tests {
   266  		t.Run(name, func(t *testing.T) {
   267  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   268  			if tt.createHolderDir {
   269  				os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath,
   270  					"holders"), 0700)
   271  				for _, file := range tt.fileEntries {
   272  					os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   273  						"holders", file))
   274  				}
   275  			}
   276  			got, gotOk := tt.sysfsDevice.getHolders()
   277  			assert.Equal(t, tt.wantOk, gotOk)
   278  			assert.Equal(t, tt.want, got)
   279  			os.RemoveAll(tt.sysfsDevice.sysPath)
   280  		})
   281  	}
   282  }
   283  
   284  func TestSysFsDeviceGetSlaves(t *testing.T) {
   285  	tmpDir := t.TempDir()
   286  	tests := map[string]struct {
   287  		fileEntries    []string
   288  		sysfsDevice    *Device
   289  		createSlaveDir bool
   290  		want           []string
   291  		wantOk         bool
   292  	}{
   293  		"device with empty slave directory": {
   294  			fileEntries: []string{},
   295  			sysfsDevice: &Device{
   296  				deviceName: "dm-0",
   297  				sysPath: filepath.Join(tmpDir,
   298  					"sys/devices/virtual/block/dm-0") + "/",
   299  				path: "/dev/dm-0",
   300  			},
   301  			createSlaveDir: true,
   302  			want:           []string{},
   303  			wantOk:         true,
   304  		},
   305  		"device with slaves": {
   306  			fileEntries: []string{"sda", "sdb"},
   307  			sysfsDevice: &Device{
   308  				deviceName: "dm-1",
   309  				sysPath: filepath.Join(tmpDir,
   310  					"sys/devices/virtual/block/dm-1") + "/",
   311  				path: "/dev/dm-1",
   312  			},
   313  			createSlaveDir: true,
   314  			want:           []string{"sda", "sdb"},
   315  			wantOk:         true,
   316  		},
   317  		"device without slaves and no slave directory": {
   318  			fileEntries: []string{},
   319  			sysfsDevice: &Device{
   320  				deviceName: "dm-2",
   321  				sysPath: filepath.Join(tmpDir,
   322  					"sys/devices/virtual/block/dm-2") + "/",
   323  				path: "/dev/dm-2",
   324  			},
   325  			createSlaveDir: false,
   326  			want:           nil,
   327  			wantOk:         false,
   328  		},
   329  	}
   330  	for name, tt := range tests {
   331  		t.Run(name, func(t *testing.T) {
   332  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   333  			if tt.createSlaveDir {
   334  				os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath,
   335  					"slaves"), 0700)
   336  				for _, file := range tt.fileEntries {
   337  					os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   338  						"slaves", file))
   339  				}
   340  			}
   341  			got, gotOk := tt.sysfsDevice.getSlaves()
   342  			assert.Equal(t, tt.wantOk, gotOk)
   343  			assert.Equal(t, tt.want, got)
   344  			os.RemoveAll(tt.sysfsDevice.sysPath)
   345  		})
   346  	}
   347  }
   348  
   349  func TestSysFsDeviceGetLogicalBlockSize(t *testing.T) {
   350  	tmpDir := t.TempDir()
   351  	tests := map[string]struct {
   352  		sysfsDevice    *Device
   353  		createQueueDir bool
   354  		lbSize         string
   355  		want           int64
   356  		wantErr        bool
   357  	}{
   358  		"no queue directory in syspath": {
   359  			sysfsDevice: &Device{
   360  				deviceName: "sda1",
   361  				sysPath: filepath.Join(tmpDir,
   362  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/",
   363  				path: "/dev/sda1",
   364  			},
   365  			createQueueDir: false,
   366  			lbSize:         "0",
   367  			want:           0,
   368  			wantErr:        true,
   369  		},
   370  		"valid blocksize present in syspath": {
   371  			sysfsDevice: &Device{
   372  				deviceName: "sda",
   373  				sysPath: filepath.Join(tmpDir,
   374  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/",
   375  				path: "/dev/sda",
   376  			},
   377  			createQueueDir: true,
   378  			lbSize:         "512",
   379  			want:           512,
   380  			wantErr:        false,
   381  		},
   382  	}
   383  	for name, tt := range tests {
   384  		t.Run(name, func(t *testing.T) {
   385  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   386  			if tt.createQueueDir {
   387  				os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath,
   388  					"queue"), 0700)
   389  				file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   390  					"queue", "logical_block_size"))
   391  				file.Write([]byte(tt.lbSize))
   392  				file.Close()
   393  			}
   394  			got, err := tt.sysfsDevice.GetLogicalBlockSize()
   395  			if (err != nil) != tt.wantErr {
   396  				t.Errorf("GetLogicalBlockSize() error = %v, wantErr %v", err, tt.wantErr)
   397  				return
   398  			}
   399  			assert.Equal(t, tt.want, got)
   400  			os.RemoveAll(tt.sysfsDevice.sysPath)
   401  		})
   402  	}
   403  }
   404  
   405  func TestSysFsDeviceGetPhysicalBlockSize(t *testing.T) {
   406  	tmpDir := t.TempDir()
   407  	tests := map[string]struct {
   408  		sysfsDevice    *Device
   409  		createQueueDir bool
   410  		pbSize         string
   411  		want           int64
   412  		wantErr        bool
   413  	}{
   414  		"no queue directory in syspath": {
   415  			sysfsDevice: &Device{
   416  				deviceName: "sda1",
   417  				sysPath: filepath.Join(tmpDir,
   418  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/",
   419  				path: "/dev/sda1",
   420  			},
   421  			createQueueDir: false,
   422  			pbSize:         "0",
   423  			want:           0,
   424  			wantErr:        true,
   425  		},
   426  		"valid blocksize present in syspath": {
   427  			sysfsDevice: &Device{
   428  				deviceName: "sda",
   429  				sysPath: filepath.Join(tmpDir,
   430  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/",
   431  				path: "/dev/sda",
   432  			},
   433  			createQueueDir: true,
   434  			pbSize:         "512",
   435  			want:           512,
   436  			wantErr:        false,
   437  		},
   438  	}
   439  	for name, tt := range tests {
   440  		t.Run(name, func(t *testing.T) {
   441  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   442  			if tt.createQueueDir {
   443  				os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath,
   444  					"queue"), 0700)
   445  				file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   446  					"queue", "physical_block_size"))
   447  				file.Write([]byte(tt.pbSize))
   448  				file.Close()
   449  			}
   450  			got, err := tt.sysfsDevice.GetPhysicalBlockSize()
   451  			if (err != nil) != tt.wantErr {
   452  				t.Errorf("GetPhysicalBlockSize() error = %v, wantErr %v", err, tt.wantErr)
   453  				return
   454  			}
   455  			assert.Equal(t, tt.want, got)
   456  			os.RemoveAll(tt.sysfsDevice.sysPath)
   457  		})
   458  	}
   459  }
   460  
   461  func TestSysFsDeviceGetHardwareSectorSize(t *testing.T) {
   462  	tmpDir := t.TempDir()
   463  	tests := map[string]struct {
   464  		sysfsDevice    *Device
   465  		createQueueDir bool
   466  		hwSize         string
   467  		want           int64
   468  		wantErr        bool
   469  	}{
   470  		"no queue directory in syspath": {
   471  			sysfsDevice: &Device{
   472  				deviceName: "sda1",
   473  				sysPath: filepath.Join(tmpDir,
   474  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/",
   475  				path: "/dev/sda1",
   476  			},
   477  			createQueueDir: false,
   478  			hwSize:         "0",
   479  			want:           0,
   480  			wantErr:        true,
   481  		},
   482  		"valid blocksize present in syspath": {
   483  			sysfsDevice: &Device{
   484  				deviceName: "sda",
   485  				sysPath: filepath.Join(tmpDir,
   486  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/",
   487  				path: "/dev/sda",
   488  			},
   489  			createQueueDir: true,
   490  			hwSize:         "512",
   491  			want:           512,
   492  			wantErr:        false,
   493  		},
   494  	}
   495  	for name, tt := range tests {
   496  		t.Run(name, func(t *testing.T) {
   497  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   498  			if tt.createQueueDir {
   499  				os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath,
   500  					"queue"), 0700)
   501  				file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   502  					"queue", "hw_sector_size"))
   503  				file.Write([]byte(tt.hwSize))
   504  				file.Close()
   505  			}
   506  			got, err := tt.sysfsDevice.GetHardwareSectorSize()
   507  			if (err != nil) != tt.wantErr {
   508  				t.Errorf("GetHardwareSectorSize() error = %v, wantErr %v", err, tt.wantErr)
   509  				return
   510  			}
   511  			assert.Equal(t, tt.want, got)
   512  			os.RemoveAll(tt.sysfsDevice.sysPath)
   513  		})
   514  	}
   515  }
   516  
   517  func TestSysFsDeviceGetDriveType(t *testing.T) {
   518  	tmpDir := t.TempDir()
   519  	tests := map[string]struct {
   520  		sysfsDevice    *Device
   521  		createQueueDir bool
   522  		rotational     string
   523  		want           string
   524  		wantErr        bool
   525  	}{
   526  		"no queue directory in syspath": {
   527  			sysfsDevice: &Device{
   528  				deviceName: "sda1",
   529  				sysPath: filepath.Join(tmpDir,
   530  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/",
   531  				path: "/dev/sda1",
   532  			},
   533  			createQueueDir: false,
   534  			rotational:     "0",
   535  			want:           blockdevice.DriveTypeUnknown,
   536  			wantErr:        true,
   537  		},
   538  		"valid rotational value (1) present in syspath": {
   539  			sysfsDevice: &Device{
   540  				deviceName: "sda",
   541  				sysPath: filepath.Join(tmpDir,
   542  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/",
   543  				path: "/dev/sda",
   544  			},
   545  			createQueueDir: true,
   546  			rotational:     "1",
   547  			want:           blockdevice.DriveTypeHDD,
   548  			wantErr:        false,
   549  		},
   550  		"valid rotational value (0) present in syspath": {
   551  			sysfsDevice: &Device{
   552  				deviceName: "sdb",
   553  				sysPath: filepath.Join(tmpDir,
   554  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb") + "/",
   555  				path: "/dev/sdb",
   556  			},
   557  			createQueueDir: true,
   558  			rotational:     "0",
   559  			want:           blockdevice.DriveTypeSSD,
   560  			wantErr:        false,
   561  		},
   562  	}
   563  	for name, tt := range tests {
   564  		t.Run(name, func(t *testing.T) {
   565  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   566  			if tt.createQueueDir {
   567  				os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath,
   568  					"queue"), 0700)
   569  				file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   570  					"queue", "rotational"))
   571  				file.Write([]byte(tt.rotational))
   572  				file.Close()
   573  			}
   574  			got, err := tt.sysfsDevice.GetDriveType()
   575  			if (err != nil) != tt.wantErr {
   576  				t.Errorf("GetDriveType() error = %v, wantErr %v", err, tt.wantErr)
   577  				return
   578  			}
   579  			assert.Equal(t, tt.want, got)
   580  			os.RemoveAll(tt.sysfsDevice.sysPath)
   581  		})
   582  	}
   583  }
   584  
   585  func TestSysFsDeviceGetCapacityInBytes(t *testing.T) {
   586  	tmpDir := t.TempDir()
   587  	tests := map[string]struct {
   588  		sysfsDevice *Device
   589  		size        string
   590  		want        int64
   591  		wantErr     bool
   592  	}{
   593  		"size 0 in syspath": {
   594  			sysfsDevice: &Device{
   595  				deviceName: "sda1",
   596  				sysPath: filepath.Join(tmpDir,
   597  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/",
   598  				path: "/dev/sda1",
   599  			},
   600  			size:    "0",
   601  			want:    0,
   602  			wantErr: true,
   603  		},
   604  		"non zero size present in syspath": {
   605  			sysfsDevice: &Device{
   606  				deviceName: "sda",
   607  				sysPath: filepath.Join(tmpDir,
   608  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/",
   609  				path: "/dev/sda",
   610  			},
   611  			size:    "976773168",
   612  			want:    500107862016,
   613  			wantErr: false,
   614  		},
   615  	}
   616  	for name, tt := range tests {
   617  		t.Run(name, func(t *testing.T) {
   618  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   619  
   620  			file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   621  				"size"))
   622  			file.Write([]byte(tt.size))
   623  			file.Close()
   624  
   625  			got, err := tt.sysfsDevice.GetCapacityInBytes()
   626  			if (err != nil) != tt.wantErr {
   627  				t.Errorf("GetCapacityInBytes() error = %v, wantErr %v", err, tt.wantErr)
   628  				return
   629  			}
   630  			assert.Equal(t, tt.want, got)
   631  			os.RemoveAll(tt.sysfsDevice.sysPath)
   632  		})
   633  	}
   634  }
   635  
   636  func TestSysFsDeviceGetDeviceType(t *testing.T) {
   637  	tmpDir := t.TempDir()
   638  	tests := map[string]struct {
   639  		sysfsDevice *Device
   640  		// should be either disk / partition
   641  		devType string
   642  
   643  		// used for dm and md devices
   644  		subDirectoryName string
   645  		subFileName      string
   646  		subFileContent   string
   647  
   648  		want    string
   649  		wantErr bool
   650  	}{
   651  		"device is a normal SCSI disk": {
   652  			sysfsDevice: &Device{
   653  				deviceName: "sda",
   654  				path:       "/dev/sda",
   655  				sysPath: filepath.Join(tmpDir,
   656  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/",
   657  			},
   658  			devType:          blockdevice.BlockDeviceTypeDisk,
   659  			subDirectoryName: "",
   660  			subFileName:      "",
   661  			subFileContent:   "",
   662  			want:             blockdevice.BlockDeviceTypeDisk,
   663  			wantErr:          false,
   664  		},
   665  		"device is SCSI disk partition": {
   666  			sysfsDevice: &Device{
   667  				deviceName: "sdb1",
   668  				path:       "/dev/sdb1",
   669  				sysPath: filepath.Join(tmpDir,
   670  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb/sdb1") + "/",
   671  			},
   672  			devType:          blockdevice.BlockDeviceTypePartition,
   673  			subDirectoryName: "",
   674  			subFileName:      "",
   675  			subFileContent:   "",
   676  			want:             blockdevice.BlockDeviceTypePartition,
   677  			wantErr:          false,
   678  		},
   679  		"device is an LVM": {
   680  			sysfsDevice: &Device{
   681  				deviceName: "dm-0",
   682  				path:       "/dev/dm-0",
   683  				sysPath: filepath.Join(tmpDir,
   684  					"sys/devices/virtual/block/dm-0") + "/",
   685  			},
   686  			devType:          blockdevice.BlockDeviceTypeDisk,
   687  			subDirectoryName: "dm",
   688  			subFileName:      "uuid",
   689  			subFileContent:   "LVM-OSlVs5gIXuqSKVPukc2aGPh0AeJw31TJqYIRuRHoodYg9Jwkmyvvk0QNYK4YulHt",
   690  			want:             blockdevice.BlockDeviceTypeLVM,
   691  			wantErr:          false,
   692  		},
   693  		"device is a partition on an LVM device": {
   694  			sysfsDevice: &Device{
   695  				deviceName: "dm-1",
   696  				path:       "/dev/dm-1",
   697  				sysPath: filepath.Join(tmpDir,
   698  					"sys/devices/virtual/block/dm-1") + "/",
   699  			},
   700  			devType:          blockdevice.BlockDeviceTypeDisk,
   701  			subDirectoryName: "dm",
   702  			subFileName:      "uuid",
   703  			subFileContent:   "part1-LVM-OSlVs5gIXuqSKVPukc2aGPh0AeJw31TJqYIRuRHoodYg9Jwkmyvvk0QNYK4YulHt",
   704  			want:             blockdevice.BlockDeviceTypePartition,
   705  			wantErr:          false,
   706  		},
   707  		"device is LUKS encrypted device": {
   708  			sysfsDevice: &Device{
   709  				deviceName: "dm-2",
   710  				path:       "/dev/dm-2",
   711  				sysPath: filepath.Join(tmpDir,
   712  					"sys/devices/virtual/block/dm-2") + "/",
   713  			},
   714  			devType:          blockdevice.BlockDeviceTypeDisk,
   715  			subDirectoryName: "dm",
   716  			subFileName:      "uuid",
   717  			subFileContent:   "CRYPT-LUKS1-ecc7566437ed483996273d3f50dc5871-backup",
   718  			want:             blockdevice.BlockDeviceTypeCrypt,
   719  			wantErr:          false,
   720  		},
   721  		"device is a partition on LUKS encrypted device": {
   722  			sysfsDevice: &Device{
   723  				deviceName: "dm-3",
   724  				path:       "/dev/dm-3",
   725  				sysPath: filepath.Join(tmpDir,
   726  					"sys/devices/virtual/block/dm-3") + "/",
   727  			},
   728  			devType:          blockdevice.BlockDeviceTypeDisk,
   729  			subDirectoryName: "dm",
   730  			subFileName:      "uuid",
   731  			subFileContent:   "part1-CRYPT-LUKS1-ecc7566437ed483996273d3f50dc5871-backup",
   732  			want:             blockdevice.BlockDeviceTypePartition,
   733  			wantErr:          false,
   734  		},
   735  		"device is a loop device": {
   736  			sysfsDevice: &Device{
   737  				deviceName: "loop7",
   738  				path:       "/dev/loop7",
   739  				sysPath: filepath.Join(tmpDir,
   740  					"/devices/virtual/block/loop7") + "/",
   741  			},
   742  			devType:          blockdevice.BlockDeviceTypeDisk,
   743  			subDirectoryName: "",
   744  			subFileName:      "",
   745  			subFileContent:   "",
   746  			want:             blockdevice.BlockDeviceTypeLoop,
   747  			wantErr:          false,
   748  		},
   749  		"device is an md device (software raid)": {
   750  			sysfsDevice: &Device{
   751  				deviceName: "md0",
   752  				path:       "/dev/md0",
   753  				sysPath: filepath.Join(tmpDir,
   754  					"sys/devices/virtual/block/md0") + "/",
   755  			},
   756  			devType:          blockdevice.BlockDeviceTypeDisk,
   757  			subDirectoryName: "md",
   758  			subFileName:      "level",
   759  			subFileContent:   "raid0",
   760  			want:             "raid0",
   761  			wantErr:          false,
   762  		},
   763  		"device is a dm device with empty uuid": {
   764  			sysfsDevice: &Device{
   765  				deviceName: "dm-16",
   766  				path:       "/dev/dm-16",
   767  				sysPath: filepath.Join(tmpDir,
   768  					"sys/devices/virtual/block/dm-16") + "/",
   769  			},
   770  			devType:          blockdevice.BlockDeviceTypeDisk,
   771  			subDirectoryName: "dm",
   772  			subFileName:      "uuid",
   773  			subFileContent:   "\n",
   774  			want:             blockdevice.BlockDeviceTypeDMDevice,
   775  			wantErr:          false,
   776  		},
   777  	}
   778  	for name, tt := range tests {
   779  		t.Run(name, func(t *testing.T) {
   780  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   781  			if len(tt.subDirectoryName) != 0 {
   782  				os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath,
   783  					tt.subDirectoryName), 0700)
   784  				f, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   785  					tt.subDirectoryName, tt.subFileName))
   786  				f.Write([]byte(tt.subFileContent))
   787  				f.Close()
   788  			}
   789  			got, err := tt.sysfsDevice.GetDeviceType(tt.devType)
   790  			if (err != nil) != tt.wantErr {
   791  				t.Errorf("GetDeviceType() error = %v, wantErr %v", err, tt.wantErr)
   792  				return
   793  			}
   794  			assert.Equal(t, tt.want, got)
   795  			os.RemoveAll(tt.sysfsDevice.sysPath)
   796  		})
   797  	}
   798  }
   799  
   800  func TestSysFsDeviceGetDependents(t *testing.T) {
   801  	tmpDir := t.TempDir()
   802  	tests := map[string]struct {
   803  		partitionEntries []string
   804  		createHolderDir  bool
   805  		holderEntries    []string
   806  		createSlaveDir   bool
   807  		slaveEntries     []string
   808  		sysfsDevice      *Device
   809  		want             blockdevice.DependentBlockDevices
   810  		wantErr          bool
   811  	}{
   812  		"parent disk with no dependents": {
   813  			partitionEntries: []string{},
   814  			createHolderDir:  false,
   815  			holderEntries:    []string{},
   816  			createSlaveDir:   false,
   817  			slaveEntries:     []string{},
   818  			sysfsDevice: &Device{
   819  				deviceName: "sda",
   820  				path:       "/dev/sda",
   821  				sysPath: filepath.Join(tmpDir,
   822  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/",
   823  			},
   824  			want: blockdevice.DependentBlockDevices{
   825  				Partitions: []string{},
   826  				Holders:    []string{},
   827  				Slaves:     []string{},
   828  			},
   829  			wantErr: false,
   830  		},
   831  		"parent disk with partitions": {
   832  			partitionEntries: []string{"sdb1", "sdb2"},
   833  			createHolderDir:  false,
   834  			holderEntries:    []string{},
   835  			createSlaveDir:   false,
   836  			slaveEntries:     []string{},
   837  			sysfsDevice: &Device{
   838  				deviceName: "sdb",
   839  				path:       "/dev/sdb",
   840  				sysPath: filepath.Join(tmpDir,
   841  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb") + "/",
   842  			},
   843  			want: blockdevice.DependentBlockDevices{
   844  				Partitions: []string{"/dev/sdb1", "/dev/sdb2"},
   845  				Holders:    []string{},
   846  				Slaves:     []string{},
   847  			},
   848  			wantErr: false,
   849  		},
   850  		"parent disk with holders": {
   851  			partitionEntries: []string{},
   852  			createHolderDir:  true,
   853  			holderEntries:    []string{"dm-0", "dm-1"},
   854  			createSlaveDir:   false,
   855  			slaveEntries:     []string{},
   856  			sysfsDevice: &Device{
   857  				deviceName: "sdc",
   858  				path:       "/dev/sdc",
   859  				sysPath: filepath.Join(tmpDir,
   860  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdc") + "/",
   861  			},
   862  			want: blockdevice.DependentBlockDevices{
   863  				Partitions: []string{},
   864  				Holders:    []string{"/dev/dm-0", "/dev/dm-1"},
   865  				Slaves:     []string{},
   866  			},
   867  			wantErr: false,
   868  		},
   869  		"partition device without holders": {
   870  			partitionEntries: []string{},
   871  			createHolderDir:  false,
   872  			holderEntries:    []string{},
   873  			createSlaveDir:   false,
   874  			slaveEntries:     []string{},
   875  			sysfsDevice: &Device{
   876  				deviceName: "sdd1",
   877  				path:       "/dev/sdd1",
   878  				sysPath: filepath.Join(tmpDir,
   879  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdd/sdd1") + "/",
   880  			},
   881  			want: blockdevice.DependentBlockDevices{
   882  				Parent:     "/dev/sdd",
   883  				Partitions: []string{},
   884  				Holders:    []string{},
   885  				Slaves:     []string{},
   886  			},
   887  			wantErr: false,
   888  		},
   889  		"partition device with holders": {
   890  			partitionEntries: []string{},
   891  			createHolderDir:  true,
   892  			holderEntries:    []string{"dm-0"},
   893  			createSlaveDir:   false,
   894  			slaveEntries:     []string{},
   895  			sysfsDevice: &Device{
   896  				deviceName: "sde1",
   897  				path:       "/dev/sde1",
   898  				sysPath: filepath.Join(tmpDir,
   899  					"sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sde/sde1") + "/",
   900  			},
   901  			want: blockdevice.DependentBlockDevices{
   902  				Parent:     "/dev/sde",
   903  				Partitions: []string{},
   904  				Holders:    []string{"/dev/dm-0"},
   905  				Slaves:     []string{},
   906  			},
   907  			wantErr: false,
   908  		},
   909  		"device with both slaves and holders": {
   910  			partitionEntries: []string{},
   911  			createHolderDir:  true,
   912  			holderEntries:    []string{"dm-1", "dm-2"},
   913  			createSlaveDir:   true,
   914  			slaveEntries:     []string{"sdb", "sdc"},
   915  			sysfsDevice: &Device{
   916  				deviceName: "dm-0",
   917  				path:       "/dev/dm-0",
   918  				sysPath: filepath.Join(tmpDir,
   919  					"sys/devices/virtual/block/dm-0") + "/",
   920  			},
   921  			want: blockdevice.DependentBlockDevices{
   922  				Partitions: []string{},
   923  				Holders:    []string{"/dev/dm-1", "/dev/dm-2"},
   924  				Slaves:     []string{"/dev/sdb", "/dev/sdc"},
   925  			},
   926  			wantErr: false,
   927  		},
   928  	}
   929  	for name, tt := range tests {
   930  		t.Run(name, func(t *testing.T) {
   931  
   932  			os.MkdirAll(tt.sysfsDevice.sysPath, 0700)
   933  
   934  			// partition entries
   935  			for _, file := range tt.partitionEntries {
   936  				os.Create(filepath.Join(tt.sysfsDevice.sysPath, file))
   937  			}
   938  
   939  			// holder entries
   940  			if tt.createHolderDir {
   941  				os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath,
   942  					"holders"), 0700)
   943  				for _, file := range tt.holderEntries {
   944  					os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   945  						"holders", file))
   946  				}
   947  			}
   948  
   949  			// slave entries
   950  			if tt.createSlaveDir {
   951  				os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath,
   952  					"slaves"), 0700)
   953  				for _, file := range tt.slaveEntries {
   954  					os.Create(filepath.Join(tt.sysfsDevice.sysPath,
   955  						"slaves", file))
   956  				}
   957  			}
   958  
   959  			got, err := tt.sysfsDevice.GetDependents()
   960  			if (err != nil) != tt.wantErr {
   961  				t.Errorf("GetDependents() error = %v, wantErr %v", err, tt.wantErr)
   962  				return
   963  			}
   964  			assert.Equal(t, tt.want, got)
   965  		})
   966  	}
   967  }