github.com/netdata/go.d.plugin@v0.58.1/modules/nvme/nvme_test.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package nvme 4 5 import ( 6 "encoding/json" 7 "errors" 8 "fmt" 9 "os" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 var ( 17 dataNVMeListJSON, _ = os.ReadFile("testdata/nvme-list.json") 18 dataNVMeListEmptyJSON, _ = os.ReadFile("testdata/nvme-list-empty.json") 19 dataNVMeSmartLogJSON, _ = os.ReadFile("testdata/nvme-smart-log.json") 20 dataNVMeSmartLogStringJSON, _ = os.ReadFile("testdata/nvme-smart-log-string.json") 21 dataNVMeSmartLogFloatJSON, _ = os.ReadFile("testdata/nvme-smart-log-float.json") 22 ) 23 24 func Test_testDataIsValid(t *testing.T) { 25 for name, data := range map[string][]byte{ 26 "dataNVMeListJSON": dataNVMeListJSON, 27 "dataNVMeListEmptyJSON": dataNVMeListEmptyJSON, 28 "dataNVMeSmartLogStringJSON": dataNVMeSmartLogStringJSON, 29 "dataNVMeSmartLogFloatJSON": dataNVMeSmartLogFloatJSON, 30 } { 31 require.NotNilf(t, data, name) 32 } 33 } 34 35 func TestNVMe_Init(t *testing.T) { 36 tests := map[string]struct { 37 prepare func(n *NVMe) 38 wantFail bool 39 }{ 40 "fails if 'binary_path' not set": { 41 wantFail: true, 42 prepare: func(n *NVMe) { 43 n.BinaryPath = "" 44 }, 45 }, 46 "fails if can't locate nvme-cli": { 47 wantFail: true, 48 prepare: func(n *NVMe) { 49 n.BinaryPath += "!!!" 50 }, 51 }, 52 } 53 54 for name, test := range tests { 55 t.Run(name, func(t *testing.T) { 56 nv := New() 57 58 test.prepare(nv) 59 60 if test.wantFail { 61 assert.False(t, nv.Init()) 62 } else { 63 assert.True(t, nv.Init()) 64 } 65 }) 66 } 67 } 68 69 func TestNVMe_Charts(t *testing.T) { 70 assert.NotNil(t, New().Charts()) 71 } 72 73 func TestNVMe_Cleanup(t *testing.T) { 74 assert.NotPanics(t, New().Cleanup) 75 } 76 77 func TestNVMe_Check(t *testing.T) { 78 tests := map[string]struct { 79 wantFail bool 80 prepare func(n *NVMe) 81 }{ 82 "success if all calls successful": { 83 wantFail: false, 84 prepare: prepareCaseOK, 85 }, 86 "fails if 'nvme list' returns an empty list": { 87 wantFail: true, 88 prepare: prepareCaseEmptyList, 89 }, 90 "fails if 'nvme list' returns an error": { 91 wantFail: true, 92 prepare: prepareCaseErrOnList, 93 }, 94 "fails if 'nvme smart-log' returns an error": { 95 wantFail: true, 96 prepare: prepareCaseErrOnSmartLog, 97 }, 98 } 99 100 for name, test := range tests { 101 t.Run(name, func(t *testing.T) { 102 n := New() 103 104 test.prepare(n) 105 106 if test.wantFail { 107 assert.False(t, n.Check()) 108 } else { 109 assert.True(t, n.Check()) 110 } 111 }) 112 } 113 } 114 115 func TestNVMe_Collect(t *testing.T) { 116 type testCaseStep struct { 117 prepare func(n *NVMe) 118 check func(t *testing.T, n *NVMe) 119 } 120 121 tests := map[string][]testCaseStep{ 122 "success if all calls successful": { 123 { 124 prepare: prepareCaseOK, 125 check: func(t *testing.T, n *NVMe) { 126 mx := n.Collect() 127 128 expected := map[string]int64{ 129 "device_nvme0n1_available_spare": 100, 130 "device_nvme0n1_controller_busy_time": 497040, 131 "device_nvme0n1_critical_comp_time": 0, 132 "device_nvme0n1_critical_warning_available_spare": 0, 133 "device_nvme0n1_critical_warning_nvm_subsystem_reliability": 0, 134 "device_nvme0n1_critical_warning_persistent_memory_read_only": 0, 135 "device_nvme0n1_critical_warning_read_only": 0, 136 "device_nvme0n1_critical_warning_temp_threshold": 0, 137 "device_nvme0n1_critical_warning_volatile_mem_backup_failed": 0, 138 "device_nvme0n1_data_units_read": 5068041216000, 139 "device_nvme0n1_data_units_written": 69712734208000, 140 "device_nvme0n1_host_read_commands": 313528805, 141 "device_nvme0n1_host_write_commands": 1928062610, 142 "device_nvme0n1_media_errors": 0, 143 "device_nvme0n1_num_err_log_entries": 110, 144 "device_nvme0n1_percentage_used": 2, 145 "device_nvme0n1_power_cycles": 64, 146 "device_nvme0n1_power_on_time": 17906400, 147 "device_nvme0n1_temperature": 36, 148 "device_nvme0n1_thm_temp1_total_time": 0, 149 "device_nvme0n1_thm_temp1_trans_count": 0, 150 "device_nvme0n1_thm_temp2_total_time": 0, 151 "device_nvme0n1_thm_temp2_trans_count": 0, 152 "device_nvme0n1_unsafe_shutdowns": 39, 153 "device_nvme0n1_warning_temp_time": 0, 154 "device_nvme1n1_available_spare": 100, 155 "device_nvme1n1_controller_busy_time": 497040, 156 "device_nvme1n1_critical_comp_time": 0, 157 "device_nvme1n1_critical_warning_available_spare": 0, 158 "device_nvme1n1_critical_warning_nvm_subsystem_reliability": 0, 159 "device_nvme1n1_critical_warning_persistent_memory_read_only": 0, 160 "device_nvme1n1_critical_warning_read_only": 0, 161 "device_nvme1n1_critical_warning_temp_threshold": 0, 162 "device_nvme1n1_critical_warning_volatile_mem_backup_failed": 0, 163 "device_nvme1n1_data_units_read": 5068041216000, 164 "device_nvme1n1_data_units_written": 69712734208000, 165 "device_nvme1n1_host_read_commands": 313528805, 166 "device_nvme1n1_host_write_commands": 1928062610, 167 "device_nvme1n1_media_errors": 0, 168 "device_nvme1n1_num_err_log_entries": 110, 169 "device_nvme1n1_percentage_used": 2, 170 "device_nvme1n1_power_cycles": 64, 171 "device_nvme1n1_power_on_time": 17906400, 172 "device_nvme1n1_temperature": 36, 173 "device_nvme1n1_thm_temp1_total_time": 0, 174 "device_nvme1n1_thm_temp1_trans_count": 0, 175 "device_nvme1n1_thm_temp2_total_time": 0, 176 "device_nvme1n1_thm_temp2_trans_count": 0, 177 "device_nvme1n1_unsafe_shutdowns": 39, 178 "device_nvme1n1_warning_temp_time": 0, 179 } 180 181 assert.Equal(t, expected, mx) 182 }, 183 }, 184 }, 185 "success if all calls successful with string values": { 186 { 187 prepare: prepareCaseStringValuesOK, 188 check: func(t *testing.T, n *NVMe) { 189 mx := n.Collect() 190 191 expected := map[string]int64{ 192 "device_nvme0n1_available_spare": 100, 193 "device_nvme0n1_controller_busy_time": 497040, 194 "device_nvme0n1_critical_comp_time": 0, 195 "device_nvme0n1_critical_warning_available_spare": 0, 196 "device_nvme0n1_critical_warning_nvm_subsystem_reliability": 0, 197 "device_nvme0n1_critical_warning_persistent_memory_read_only": 0, 198 "device_nvme0n1_critical_warning_read_only": 0, 199 "device_nvme0n1_critical_warning_temp_threshold": 0, 200 "device_nvme0n1_critical_warning_volatile_mem_backup_failed": 0, 201 "device_nvme0n1_data_units_read": 5068041216000, 202 "device_nvme0n1_data_units_written": 69712734208000, 203 "device_nvme0n1_host_read_commands": 313528805, 204 "device_nvme0n1_host_write_commands": 1928062610, 205 "device_nvme0n1_media_errors": 0, 206 "device_nvme0n1_num_err_log_entries": 110, 207 "device_nvme0n1_percentage_used": 2, 208 "device_nvme0n1_power_cycles": 64, 209 "device_nvme0n1_power_on_time": 17906400, 210 "device_nvme0n1_temperature": 36, 211 "device_nvme0n1_thm_temp1_total_time": 0, 212 "device_nvme0n1_thm_temp1_trans_count": 0, 213 "device_nvme0n1_thm_temp2_total_time": 0, 214 "device_nvme0n1_thm_temp2_trans_count": 0, 215 "device_nvme0n1_unsafe_shutdowns": 39, 216 "device_nvme0n1_warning_temp_time": 0, 217 "device_nvme1n1_available_spare": 100, 218 "device_nvme1n1_controller_busy_time": 497040, 219 "device_nvme1n1_critical_comp_time": 0, 220 "device_nvme1n1_critical_warning_available_spare": 0, 221 "device_nvme1n1_critical_warning_nvm_subsystem_reliability": 0, 222 "device_nvme1n1_critical_warning_persistent_memory_read_only": 0, 223 "device_nvme1n1_critical_warning_read_only": 0, 224 "device_nvme1n1_critical_warning_temp_threshold": 0, 225 "device_nvme1n1_critical_warning_volatile_mem_backup_failed": 0, 226 "device_nvme1n1_data_units_read": 5068041216000, 227 "device_nvme1n1_data_units_written": 69712734208000, 228 "device_nvme1n1_host_read_commands": 313528805, 229 "device_nvme1n1_host_write_commands": 1928062610, 230 "device_nvme1n1_media_errors": 0, 231 "device_nvme1n1_num_err_log_entries": 110, 232 "device_nvme1n1_percentage_used": 2, 233 "device_nvme1n1_power_cycles": 64, 234 "device_nvme1n1_power_on_time": 17906400, 235 "device_nvme1n1_temperature": 36, 236 "device_nvme1n1_thm_temp1_total_time": 0, 237 "device_nvme1n1_thm_temp1_trans_count": 0, 238 "device_nvme1n1_thm_temp2_total_time": 0, 239 "device_nvme1n1_thm_temp2_trans_count": 0, 240 "device_nvme1n1_unsafe_shutdowns": 39, 241 "device_nvme1n1_warning_temp_time": 0, 242 } 243 244 assert.Equal(t, expected, mx) 245 }, 246 }, 247 }, 248 "success if all calls successful with float values": { 249 { 250 prepare: prepareCaseFloatValuesOK, 251 check: func(t *testing.T, n *NVMe) { 252 mx := n.Collect() 253 254 expected := map[string]int64{ 255 "device_nvme0n1_available_spare": 100, 256 "device_nvme0n1_controller_busy_time": 497040, 257 "device_nvme0n1_critical_comp_time": 0, 258 "device_nvme0n1_critical_warning_available_spare": 0, 259 "device_nvme0n1_critical_warning_nvm_subsystem_reliability": 0, 260 "device_nvme0n1_critical_warning_persistent_memory_read_only": 0, 261 "device_nvme0n1_critical_warning_read_only": 0, 262 "device_nvme0n1_critical_warning_temp_threshold": 0, 263 "device_nvme0n1_critical_warning_volatile_mem_backup_failed": 0, 264 "device_nvme0n1_data_units_read": 5068041216000, 265 "device_nvme0n1_data_units_written": 69712734208000, 266 "device_nvme0n1_host_read_commands": 313528805, 267 "device_nvme0n1_host_write_commands": 1928062610, 268 "device_nvme0n1_media_errors": 0, 269 "device_nvme0n1_num_err_log_entries": 110, 270 "device_nvme0n1_percentage_used": 2, 271 "device_nvme0n1_power_cycles": 64, 272 "device_nvme0n1_power_on_time": 17906400, 273 "device_nvme0n1_temperature": 36, 274 "device_nvme0n1_thm_temp1_total_time": 0, 275 "device_nvme0n1_thm_temp1_trans_count": 0, 276 "device_nvme0n1_thm_temp2_total_time": 0, 277 "device_nvme0n1_thm_temp2_trans_count": 0, 278 "device_nvme0n1_unsafe_shutdowns": 39, 279 "device_nvme0n1_warning_temp_time": 0, 280 "device_nvme1n1_available_spare": 100, 281 "device_nvme1n1_controller_busy_time": 497040, 282 "device_nvme1n1_critical_comp_time": 0, 283 "device_nvme1n1_critical_warning_available_spare": 0, 284 "device_nvme1n1_critical_warning_nvm_subsystem_reliability": 0, 285 "device_nvme1n1_critical_warning_persistent_memory_read_only": 0, 286 "device_nvme1n1_critical_warning_read_only": 0, 287 "device_nvme1n1_critical_warning_temp_threshold": 0, 288 "device_nvme1n1_critical_warning_volatile_mem_backup_failed": 0, 289 "device_nvme1n1_data_units_read": 5068041216000, 290 "device_nvme1n1_data_units_written": 69712734208000, 291 "device_nvme1n1_host_read_commands": 313528805, 292 "device_nvme1n1_host_write_commands": 1928062610, 293 "device_nvme1n1_media_errors": 0, 294 "device_nvme1n1_num_err_log_entries": 110, 295 "device_nvme1n1_percentage_used": 2, 296 "device_nvme1n1_power_cycles": 64, 297 "device_nvme1n1_power_on_time": 17906400, 298 "device_nvme1n1_temperature": 36, 299 "device_nvme1n1_thm_temp1_total_time": 0, 300 "device_nvme1n1_thm_temp1_trans_count": 0, 301 "device_nvme1n1_thm_temp2_total_time": 0, 302 "device_nvme1n1_thm_temp2_trans_count": 0, 303 "device_nvme1n1_unsafe_shutdowns": 39, 304 "device_nvme1n1_warning_temp_time": 0, 305 } 306 307 assert.Equal(t, expected, mx) 308 }, 309 }, 310 }, 311 "fail if 'nvme list' returns an empty list": { 312 { 313 prepare: prepareCaseEmptyList, 314 check: func(t *testing.T, n *NVMe) { 315 mx := n.Collect() 316 317 assert.Equal(t, (map[string]int64)(nil), mx) 318 }, 319 }, 320 }, 321 "fail if 'nvme list' returns an error": { 322 { 323 prepare: prepareCaseErrOnList, 324 check: func(t *testing.T, n *NVMe) { 325 mx := n.Collect() 326 327 assert.Equal(t, (map[string]int64)(nil), mx) 328 }, 329 }, 330 }, 331 "fail if 'nvme smart-log' returns an error": { 332 { 333 prepare: prepareCaseErrOnSmartLog, 334 check: func(t *testing.T, n *NVMe) { 335 mx := n.Collect() 336 337 assert.Equal(t, (map[string]int64)(nil), mx) 338 }, 339 }, 340 }, 341 } 342 343 for name, test := range tests { 344 t.Run(name, func(t *testing.T) { 345 n := New() 346 347 for i, step := range test { 348 t.Run(fmt.Sprintf("step[%d]", i), func(t *testing.T) { 349 step.prepare(n) 350 step.check(t, n) 351 }) 352 } 353 }) 354 } 355 } 356 357 func prepareCaseOK(n *NVMe) { 358 n.exec = &mockNVMeCLIExec{} 359 } 360 361 func prepareCaseStringValuesOK(n *NVMe) { 362 n.exec = &mockNVMeCLIExec{smartLogString: true} 363 } 364 365 func prepareCaseFloatValuesOK(n *NVMe) { 366 n.exec = &mockNVMeCLIExec{smartLogFloat: true} 367 } 368 369 func prepareCaseEmptyList(n *NVMe) { 370 n.exec = &mockNVMeCLIExec{emptyList: true} 371 } 372 373 func prepareCaseErrOnList(n *NVMe) { 374 n.exec = &mockNVMeCLIExec{errOnList: true} 375 } 376 377 func prepareCaseErrOnSmartLog(n *NVMe) { 378 n.exec = &mockNVMeCLIExec{errOnSmartLog: true} 379 } 380 381 type mockNVMeCLIExec struct { 382 errOnList bool 383 errOnSmartLog bool 384 emptyList bool 385 smartLogString bool 386 smartLogFloat bool 387 } 388 389 func (m *mockNVMeCLIExec) list() (*nvmeDeviceList, error) { 390 if m.errOnList { 391 return nil, errors.New("mock.list() error") 392 } 393 394 data := dataNVMeListJSON 395 if m.emptyList { 396 data = dataNVMeListEmptyJSON 397 } 398 399 var v nvmeDeviceList 400 if err := json.Unmarshal(data, &v); err != nil { 401 return nil, err 402 } 403 404 return &v, nil 405 } 406 407 func (m *mockNVMeCLIExec) smartLog(_ string) (*nvmeDeviceSmartLog, error) { 408 if m.errOnSmartLog { 409 return nil, errors.New("mock.smartLog() error") 410 } 411 if m.emptyList { 412 return nil, errors.New("mock.smartLog() no devices error") 413 } 414 415 data := dataNVMeSmartLogJSON 416 if m.smartLogString { 417 data = dataNVMeSmartLogStringJSON 418 } 419 if m.smartLogFloat { 420 data = dataNVMeSmartLogFloatJSON 421 } 422 423 var v nvmeDeviceSmartLog 424 if err := json.Unmarshal(data, &v); err != nil { 425 return nil, err 426 } 427 428 return &v, nil 429 }