@@ -156,6 +156,164 @@ func setupDPUConfigFileForPort(t *testing.T, uplink, portName, fileContent strin
156156 assert .NoError (t , err )
157157}
158158
159+ func setupRepresentorEnvForGetVfRepresentor (t * testing.T , uplink * repContext , vfReps []* repContext ) func () {
160+ var err error
161+ teardown := setupFakeFs (t )
162+
163+ defer func () {
164+ if err != nil {
165+ teardown ()
166+ t .Errorf ("setupRepresentorEnvForGetVfRepresentor, got %v" , err )
167+ }
168+ }()
169+
170+ // Create the uplink device/net directory structure
171+ pfNetPath := filepath .Join (NetSysDir , uplink .Name , "device" , "net" )
172+ err = utilfs .Fs .MkdirAll (pfNetPath , os .FileMode (0755 ))
173+ if err != nil {
174+ teardown ()
175+ t .Errorf ("setupRepresentorEnvForGetVfRepresentor, got %v" , err )
176+ }
177+
178+ for _ , rep := range vfReps {
179+ // Create representor directory under the uplink's device/net path
180+ repPath := filepath .Join (pfNetPath , rep .Name )
181+ repLink := filepath .Join (NetSysDir , rep .Name )
182+
183+ err = utilfs .Fs .MkdirAll (repPath , os .FileMode (0755 ))
184+ if err != nil {
185+ teardown ()
186+ t .Errorf ("setupRepresentorEnvForGetVfRepresentor, got %v" , err )
187+ }
188+
189+ // Create symlink from /sys/class/net/<rep_name> to the rep path
190+ _ = utilfs .Fs .Symlink (repPath , repLink )
191+
192+ if err = setUpRepPhysFiles (rep ); err != nil {
193+ teardown ()
194+ t .Errorf ("setupRepresentorEnvForGetVfRepresentor, got %v" , err )
195+ }
196+ }
197+
198+ // create phys_port_name and phys_switch_id files for the uplink
199+ if err = setUpRepPhysFiles (uplink ); err != nil {
200+ teardown ()
201+ t .Errorf ("setupRepresentorEnvForGetVfRepresentor, got %v" , err )
202+ }
203+
204+ return teardown
205+ }
206+
207+ func TestGetVfRepresentor (t * testing.T ) {
208+ tcases := []struct {
209+ name string
210+ uplink * repContext
211+ vfReps []* repContext
212+ vfIndex int
213+ expectedVFRep string
214+ shouldFail bool
215+ }{
216+ {
217+ name : "VF representor found" ,
218+ uplink : & repContext {Name : "p0" , PhysPortName : "p0" , PhysSwitchID : "c2cfc60003a1420c" },
219+ vfReps : []* repContext {
220+ {Name : "eth0" , PhysPortName : "pf0vf0" , PhysSwitchID : "c2cfc60003a1420c" },
221+ {Name : "eth1" , PhysPortName : "pf0vf1" , PhysSwitchID : "c2cfc60003a1420c" },
222+ {Name : "eth2" , PhysPortName : "pf0vf2" , PhysSwitchID : "c2cfc60003a1420c" },
223+ },
224+ vfIndex : 2 ,
225+ expectedVFRep : "eth2" ,
226+ shouldFail : false ,
227+ },
228+ {
229+ name : "VF representor not found - index doesn't exist" ,
230+ uplink : & repContext {Name : "p0" , PhysPortName : "p0" , PhysSwitchID : "c2cfc60003a1420c" },
231+ vfReps : []* repContext {
232+ {Name : "eth0" , PhysPortName : "pf0vf0" , PhysSwitchID : "c2cfc60003a1420c" },
233+ {Name : "eth1" , PhysPortName : "pf0vf1" , PhysSwitchID : "c2cfc60003a1420c" },
234+ {Name : "eth2" , PhysPortName : "pf0vf2" , PhysSwitchID : "c2cfc60003a1420c" },
235+ },
236+ vfIndex : 5 ,
237+ expectedVFRep : "" ,
238+ shouldFail : true ,
239+ },
240+ {
241+ name : "VF representor not found - no representors" ,
242+ uplink : & repContext {Name : "p0" , PhysPortName : "p0" , PhysSwitchID : "c2cfc60003a1420c" },
243+ vfReps : []* repContext {},
244+ vfIndex : 0 ,
245+ expectedVFRep : "" ,
246+ shouldFail : true ,
247+ },
248+ {
249+ name : "VF representor not found - invalid phys_port_name" ,
250+ uplink : & repContext {Name : "p0" , PhysPortName : "p0" , PhysSwitchID : "c2cfc60003a1420c" },
251+ vfReps : []* repContext {
252+ {Name : "eth0" , PhysPortName : "invalid" , PhysSwitchID : "c2cfc60003a1420c" },
253+ {Name : "eth1" , PhysPortName : "pf0sf1" , PhysSwitchID : "c2cfc60003a1420c" }, // SF instead of VF
254+ },
255+ vfIndex : 0 ,
256+ expectedVFRep : "" ,
257+ shouldFail : true ,
258+ },
259+ {
260+ name : "VF representor not found - missing phys_port_name" ,
261+ uplink : & repContext {Name : "p0" , PhysPortName : "p0" , PhysSwitchID : "c2cfc60003a1420c" },
262+ vfReps : []* repContext {
263+ {Name : "eth0" , PhysPortName : "" , PhysSwitchID : "c2cfc60003a1420c" }, // No phys_port_name
264+ {Name : "eth1" , PhysPortName : "pf0vf1" , PhysSwitchID : "c2cfc60003a1420c" },
265+ },
266+ vfIndex : 0 ,
267+ expectedVFRep : "" ,
268+ shouldFail : true ,
269+ },
270+ {
271+ name : "uplink is not switchdev" ,
272+ uplink : & repContext {Name : "eth0" , PhysPortName : "" , PhysSwitchID : "" },
273+ vfReps : []* repContext {},
274+ vfIndex : 0 ,
275+ expectedVFRep : "" ,
276+ shouldFail : true ,
277+ },
278+ {
279+ name : "VF representor found with mixed representors" ,
280+ uplink : & repContext {Name : "p0" , PhysPortName : "p0" , PhysSwitchID : "c2cfc60003a1420c" },
281+ vfReps : []* repContext {
282+ {Name : "eth0" , PhysPortName : "invalid" , PhysSwitchID : "c2cfc60003a1420c" }, // Invalid
283+ {Name : "eth1" , PhysPortName : "pf0vf0" , PhysSwitchID : "c2cfc60003a1420c" },
284+ {Name : "eth2" , PhysPortName : "pf0sf1" , PhysSwitchID : "c2cfc60003a1420c" }, // SF rep
285+ {Name : "eth3" , PhysPortName : "pf0vf2" , PhysSwitchID : "c2cfc60003a1420c" },
286+ },
287+ vfIndex : 2 ,
288+ expectedVFRep : "eth3" ,
289+ shouldFail : false ,
290+ },
291+ }
292+
293+ for _ , tcase := range tcases {
294+ t .Run (tcase .name , func (t * testing.T ) {
295+ teardown := setupRepresentorEnvForGetVfRepresentor (t , tcase .uplink , tcase .vfReps )
296+ defer teardown ()
297+ vfRep , err := GetVfRepresentor (tcase .uplink .Name , tcase .vfIndex )
298+ if tcase .shouldFail {
299+ assert .Error (t , err )
300+ } else {
301+ assert .NoError (t , err )
302+ assert .Equal (t , tcase .expectedVFRep , vfRep )
303+ }
304+ })
305+ }
306+
307+ // Test edge case: uplink directory doesn't exist (filesystem error)
308+ t .Run ("uplink directory doesn't exist" , func (t * testing.T ) {
309+ teardown := setupFakeFs (t )
310+ defer teardown ()
311+
312+ _ , err := GetVfRepresentor ("nonexistent_uplink" , 0 )
313+ assert .Error (t , err )
314+ })
315+ }
316+
159317func TestGetUplinkRepresentorWithPhysPortName (t * testing.T ) {
160318 tcases := []struct {
161319 name string
0 commit comments