github.com/jaypipes/ghw@v0.21.1/pkg/pci/pci_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 package pci_test 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "os" 13 "path/filepath" 14 "testing" 15 16 "github.com/jaypipes/ghw/pkg/context" 17 "github.com/jaypipes/ghw/pkg/marshal" 18 "github.com/jaypipes/ghw/pkg/option" 19 "github.com/jaypipes/ghw/pkg/pci" 20 "github.com/jaypipes/ghw/pkg/util" 21 22 "github.com/jaypipes/ghw/testdata" 23 ) 24 25 type pciTestCase struct { 26 addr string 27 parentAddr string 28 node int 29 revision string 30 driver string 31 iommuGroup string 32 } 33 34 // nolint: gocyclo 35 func TestPCINUMANode(t *testing.T) { 36 info := pciTestSetupXeon(t) 37 38 tCases := []pciTestCase{ 39 { 40 addr: "0000:07:03.0", 41 // -1 is actually what we get out of the box on the snapshotted box 42 node: -1, 43 }, 44 { 45 addr: "0000:05:11.0", 46 node: 0, 47 }, 48 { 49 addr: "0000:05:00.1", 50 node: 1, 51 }, 52 } 53 for _, tCase := range tCases { 54 t.Run(fmt.Sprintf("%s (%d)", tCase.addr, tCase.node), func(t *testing.T) { 55 dev := info.GetDevice(tCase.addr) 56 if dev == nil { 57 t.Fatalf("got nil device for address %q", tCase.addr) 58 } 59 if dev.Node == nil { 60 if tCase.node != -1 { 61 t.Fatalf("got nil numa NODE for address %q", tCase.addr) 62 } 63 } else { 64 if dev.Node.ID != tCase.node { 65 t.Errorf("got NUMA node info %#v, expected on node %d", dev.Node, tCase.node) 66 } 67 } 68 }) 69 } 70 } 71 72 // nolint: gocyclo 73 func TestPCIDeviceRevision(t *testing.T) { 74 info := pciTestSetupXeon(t) 75 76 var tCases []pciTestCase = []pciTestCase{ 77 { 78 addr: "0000:07:03.0", 79 revision: "0x0a", 80 }, 81 { 82 addr: "0000:05:00.0", 83 revision: "0x01", 84 }, 85 } 86 for _, tCase := range tCases { 87 t.Run(tCase.addr, func(t *testing.T) { 88 dev := info.GetDevice(tCase.addr) 89 if dev == nil { 90 t.Fatalf("got nil device for address %q", tCase.addr) 91 } 92 if dev.Revision != tCase.revision { 93 t.Errorf("device %q got revision %q expected %q", tCase.addr, dev.Revision, tCase.revision) 94 } 95 }) 96 } 97 } 98 99 // nolint: gocyclo 100 func TestPCIParent(t *testing.T) { 101 info := pciTestSetupI7(t) 102 tCases := []pciTestCase{ 103 { 104 addr: "0000:04:00.0", 105 parentAddr: "0000:00:06.0", 106 }, 107 } 108 for _, tCase := range tCases { 109 t.Run(fmt.Sprintf("%s (%s)", tCase.addr, tCase.parentAddr), func(t *testing.T) { 110 dev := info.GetDevice(tCase.addr) 111 if dev == nil { 112 t.Fatalf("got nil device for address %q", tCase.addr) 113 } 114 if dev.ParentAddress != tCase.parentAddr { 115 t.Errorf("got parent %q expected %q", dev.ParentAddress, tCase.parentAddr) 116 } 117 }) 118 } 119 } 120 121 // nolint: gocyclo 122 func TestPCIIommuGroup(t *testing.T) { 123 info := pciTestSetupI7(t) 124 tCases := []pciTestCase{ 125 { 126 addr: "0000:00:1f.0", 127 iommuGroup: "13", 128 }, 129 { 130 addr: "0000:00:1f.5", 131 iommuGroup: "13", 132 }, 133 { 134 addr: "0000:04:00.0", 135 iommuGroup: "14", 136 }, 137 } 138 for _, tCase := range tCases { 139 t.Run(fmt.Sprintf("%s (%s)", tCase.addr, tCase.iommuGroup), func(t *testing.T) { 140 dev := info.GetDevice(tCase.addr) 141 if dev == nil { 142 t.Fatalf("got nil device for address %q", tCase.addr) 143 } 144 if dev.IOMMUGroup != tCase.iommuGroup { 145 t.Errorf("got iommu_group %q expected %q", dev.IOMMUGroup, tCase.iommuGroup) 146 } 147 }) 148 } 149 } 150 151 // nolint: gocyclo 152 func TestPCIDriver(t *testing.T) { 153 info := pciTestSetupXeon(t) 154 155 tCases := []pciTestCase{ 156 { 157 addr: "0000:07:03.0", 158 driver: "mgag200", 159 }, 160 { 161 addr: "0000:05:11.0", 162 driver: "igbvf", 163 }, 164 { 165 addr: "0000:05:00.1", 166 driver: "igb", 167 }, 168 } 169 for _, tCase := range tCases { 170 t.Run(fmt.Sprintf("%s (%s)", tCase.addr, tCase.driver), func(t *testing.T) { 171 dev := info.GetDevice(tCase.addr) 172 if dev == nil { 173 t.Fatalf("got nil device for address %q", tCase.addr) 174 } 175 if dev.Driver != tCase.driver { 176 t.Errorf("got driver %q expected %q", dev.Driver, tCase.driver) 177 } 178 }) 179 } 180 } 181 182 func TestPCIMarshalJSON(t *testing.T) { 183 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_PCI"); ok { 184 t.Skip("Skipping PCI tests.") 185 } 186 info, err := pci.New() 187 if err != nil { 188 t.Fatalf("Expected no error creating PciInfo, but got %v", err) 189 } 190 191 dev := info.ParseDevice("0000:3c:00.0", "pci:v0000144Dd0000A804sv0000144Dsd0000A801bc01sc08i02\n") 192 if dev == nil { 193 t.Fatalf("Failed to parse valid modalias") 194 } 195 s := marshal.SafeJSON(context.FromEnv(), dev, true) 196 if s == "" { 197 t.Fatalf("Error marshalling device: %v", dev) 198 } 199 } 200 201 // the sriov-device-plugin code has a test like this 202 func TestPCIMalformedModalias(t *testing.T) { 203 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_PCI"); ok { 204 t.Skip("Skipping PCI tests.") 205 } 206 info, err := pci.New() 207 if err != nil { 208 t.Fatalf("Expected no error creating PciInfo, but got %v", err) 209 } 210 211 var dev *pci.Device 212 dev = info.ParseDevice("0000:00:01.0", "pci:junk") 213 if dev != nil { 214 t.Fatalf("Parsed successfully junk data") 215 } 216 217 dev = info.ParseDevice("0000:00:01.0", "pci:v00008086d00005916sv000017AAsd0000224Bbc03sc00i00extrajunkextradataextraextra") 218 if dev == nil { 219 t.Fatalf("Failed to parse valid modalias with extra data") 220 } 221 } 222 223 func pciTestSetupXeon(t *testing.T) *pci.Info { 224 const snapshotFilename = "linux-amd64-intel-xeon-L5640.tar.gz" 225 return pciTestSetup(t, snapshotFilename) 226 } 227 228 func pciTestSetupI7(t *testing.T) *pci.Info { 229 const snapshotFilename = "linux-amd64-intel-i7-1270P.tar.gz" 230 return pciTestSetup(t, snapshotFilename) 231 } 232 233 func pciTestSetup(t *testing.T, snapshotFilename string) *pci.Info { 234 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_PCI"); ok { 235 t.Skip("Skipping PCI tests.") 236 } 237 238 testdataPath, err := testdata.SnapshotsDirectory() 239 if err != nil { 240 t.Fatalf("Expected nil err, but got %v", err) 241 } 242 243 snapshot := filepath.Join(testdataPath, snapshotFilename) 244 // from now on we use constants reflecting the content of the snapshot we requested, 245 // which we reviewed beforehand. IOW, you need to know the content of the 246 // snapshot to fully understand this test. Inspect it using 247 // GHW_SNAPSHOT_PATH="/path/to/linux-amd64-intel-xeon-L5640.tar.gz" ghwc topology 248 249 info, err := pci.New(option.WithSnapshot(option.SnapshotOptions{ 250 Path: snapshot, 251 })) 252 253 if err != nil { 254 t.Fatalf("Expected nil err, but got %v", err) 255 } 256 if info == nil { 257 t.Fatalf("Expected non-nil PCIInfo, but got nil") 258 } 259 return info 260 } 261 262 // we have this test in pci_linux_test.go (and not in pci_test.go) because `pciFillInfo` is implemented 263 // only on linux; so having it in the platform-independent tests would lead to false negatives. 264 func TestPCIMarshalUnmarshal(t *testing.T) { 265 data, err := pci.New(option.WithNullAlerter()) 266 if err != nil { 267 t.Fatalf("Expected no error creating pci.Info, but got %v", err) 268 } 269 270 jdata, err := json.Marshal(data) 271 if err != nil { 272 t.Fatalf("Expected no error marshaling pci.Info, but got %v", err) 273 } 274 275 var topo *pci.Info 276 277 err = json.Unmarshal(jdata, &topo) 278 if err != nil { 279 t.Fatalf("Expected no error unmarshaling pci.Info, but got %v", err) 280 } 281 } 282 283 func TestPCIModaliasWithUpperCaseClassID(t *testing.T) { 284 if _, ok := os.LookupEnv("GHW_TESTING_SKIP_PCI"); ok { 285 t.Skip("Skipping PCI tests.") 286 } 287 info, err := pci.New() 288 if err != nil { 289 t.Fatalf("Expected no error creating PciInfo, but got %v", err) 290 } 291 292 dev := info.ParseDevice("0000:00:1f.4", "pci:v00008086d00009D23sv00001028sd000007EAbc0Csc05i00\n") 293 if dev == nil { 294 t.Fatalf("Failed to parse valid modalias") 295 } 296 if dev.Class.Name == util.UNKNOWN { 297 t.Fatalf("Failed to lookup class name") 298 } 299 }