1 package xorg
2
3 import (
4 "context"
5 "encoding/hex"
6 "errors"
7 "fmt"
8 "testing"
9
10 edidparser "github.com/anoopengineer/edidparser/edid"
11 "github.com/go-logr/logr"
12 xgbdpms "github.com/jezek/xgb/dpms"
13 "github.com/jezek/xgb/randr"
14 "github.com/jezek/xgb/xproto"
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/require"
17 corev1 "k8s.io/api/core/v1"
18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19 "k8s.io/apimachinery/pkg/runtime"
20 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
21 clientgoscheme "k8s.io/client-go/kubernetes/scheme"
22 "sigs.k8s.io/controller-runtime/pkg/client"
23 "sigs.k8s.io/controller-runtime/pkg/client/fake"
24
25 "edge-infra.dev/pkg/lib/kernel/drm"
26 "edge-infra.dev/pkg/sds/display/constants"
27 "edge-infra.dev/pkg/sds/display/displaymanager/reader"
28 v2 "edge-infra.dev/pkg/sds/display/k8s/apis/v2"
29 "edge-infra.dev/pkg/sds/lib/edid"
30 "edge-infra.dev/pkg/sds/lib/xorg"
31 "edge-infra.dev/pkg/sds/lib/xorg/dpms"
32 fakedpms "edge-infra.dev/pkg/sds/lib/xorg/dpms/fake"
33 "edge-infra.dev/pkg/sds/lib/xorg/xinput"
34 fakexinput "edge-infra.dev/pkg/sds/lib/xorg/xinput/fake"
35 "edge-infra.dev/pkg/sds/lib/xorg/xrandr"
36 fakexrandr "edge-infra.dev/pkg/sds/lib/xorg/xrandr/fake"
37 )
38
39 var (
40 dp2 = xorg.OutputID("DP2")
41 hdmi2 = xorg.OutputID("HDMI2")
42 hdmi3 = xorg.OutputID("HDMI3")
43 virtual1 = xorg.OutputID("VIRTUAL1")
44
45 card0DP2 = v2.DisplayPort("card0-DP2")
46 card0HDMI2 = v2.DisplayPort("card0-HDMI-A-2")
47 card0HDMI3 = v2.DisplayPort("card0-HDMI-A-3")
48 card1Virtual1 = v2.DisplayPort("card1-Virtual-1")
49
50 pln5376 = v2.MPID("PLN-5376")
51 wnx32 = v2.MPID("WNX-32")
52 tgc18464 = v2.MPID("TGC-18464")
53
54 pln5376EDID, _ = hex.DecodeString("00ffffffffffff00418e0015010101012c170104a51e177822bf95a4554c9524145054bfee000101010101010101010101010101010164190040410026301888360030e410000018000000fd00384b183d08000a202020202020000000ff00504c3334345444313032393434000000fc00504c313530304d0a20202020200037")
55 wnx32EDID, _ = hex.DecodeString("00ffffffffffff005dd820000094357730170103811e1700eaa6ed9454539927194c4fa108000101010101010101010101010101010164190040410026301888360030e410000018000000ff00393643503333333537300a2020000000fc004241383356344456490a202020000000fd00384b1f4508000a202020202020002e")
56 tgc18464EDID, _ = hex.DecodeString("00ffffffffffff0050e3204801010101161a010308191299e800029959529226215759afc00045400101010101010101010101010101a00f200031581c2028801400f6b800000006000000ff00343132584848500a2020202020000000fd00384b1e3005000a202020202020000000fc0034383230324c470a2020202020005c")
57
58 inputDeviceID1 = xorg.InputDeviceID("1")
59 inputDeviceID2 = xorg.InputDeviceID("2")
60 inputDeviceID3 = xorg.InputDeviceID("3")
61 inputDeviceID4 = xorg.InputDeviceID("4")
62 inputDeviceID5 = xorg.InputDeviceID("5")
63
64 eloTouchSolutionsUSB = "Elo Touch Solutions Elo Touch Solutions Pcap USB Interface"
65 eloTouchSolutions2107 = "Elo Touch Solutions Elo Touch Solutions 2107"
66 eGalaxEXC3189 = "eGalax Inc. eGalaxTouch EXC3189-2506-09.00.00.00"
67 eGalaxEXC3189Mouse = "eGalax Inc. eGalaxTouch EXC3189-2506-09.00.00.00 Mouse"
68
69 primary = v2.Primary(true)
70 notPrimary = v2.Primary(false)
71 enabled = true
72 thirtySeconds = 30
73
74 dpmsInfo = &dpms.Info{Capable: true}
75 )
76
77 var displayPortOverrideConfigMap = &corev1.ConfigMap{
78 ObjectMeta: metav1.ObjectMeta{
79 Name: constants.DisplayPortOverride,
80 Namespace: constants.Namespace,
81 },
82 Data: map[string]string{
83 dp2.String(): card0DP2.String(),
84 hdmi2.String(): card0HDMI2.String(),
85 hdmi3.String(): card0HDMI3.String(),
86 virtual1.String(): card1Virtual1.String(),
87 },
88 }
89
90 var displayPortOverride = map[xorg.OutputID]v2.DisplayPort{
91 dp2: card0DP2,
92 hdmi2: card0HDMI2,
93 hdmi3: card0HDMI3,
94 virtual1: card1Virtual1,
95 }
96
97 func TestFindConnectorForOutputByEDID(t *testing.T) {
98 r := &displayReader{}
99
100 card0DP1, _ := drm.NewConnector("card0-DP1")
101 card0DP1.WithEDID(&edid.EDID{Edid: &edidparser.Edid{ManufacturerId: "PLN", ProductCode: 5376}})
102
103 card0HDMI1, _ := drm.NewConnector("card0-HDMI-A-1")
104 card0HDMI1.WithEDID(&edid.EDID{Edid: &edidparser.Edid{ManufacturerId: "WNX", ProductCode: 32}})
105
106 card1DP4, _ := drm.NewConnector("card1-DP4")
107 card1VGA, _ := drm.NewConnector("card1-vga-1")
108 card1HDMI4, _ := drm.NewConnector("card1-HDMI-A-4")
109
110 connectors := []drm.Connector{
111 *card0DP1,
112 *card0HDMI1,
113 *card1DP4,
114 *card1VGA,
115 *card1HDMI4,
116 }
117
118 tcs := []struct {
119 output xrandr.Output
120 expectedDisplayPort v2.DisplayPort
121 }{
122 {
123
124 xrandr.Output{
125 Output: &randr.GetOutputInfoReply{Name: []byte("DP1")},
126 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: pln5376EDID}},
127 },
128 "card0-DP1",
129 },
130 {
131
132 xrandr.Output{
133 Output: &randr.GetOutputInfoReply{Name: []byte("HDMI1")},
134 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: wnx32EDID}},
135 },
136 "card0-HDMI-A-1",
137 },
138 {
139
140 xrandr.Output{
141 Output: &randr.GetOutputInfoReply{Name: []byte("DP4")},
142 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: tgc18464EDID}},
143 },
144 "card1-DP4",
145 },
146 {
147
148 xrandr.Output{
149 Output: &randr.GetOutputInfoReply{Name: []byte("VGA1")},
150 },
151 "card1-vga-1",
152 },
153 {
154
155 xrandr.Output{
156 Output: &randr.GetOutputInfoReply{Name: []byte("HDMI4")},
157 },
158 "card1-HDMI-A-4",
159 },
160 {
161
162 xrandr.Output{
163 Output: &randr.GetOutputInfoReply{Name: []byte("VIRTUAL1")},
164 },
165 "card1-Virtual-1",
166 },
167 {
168
169 xrandr.Output{
170 Output: &randr.GetOutputInfoReply{Name: []byte("DUMMY0")},
171 },
172 "unknown-DUMMY0",
173 },
174 }
175 for idx, tc := range tcs {
176 dp := r.findDisplayPortForOutput(tc.output, connectors, displayPortOverride)
177 assert.Equal(t, tc.expectedDisplayPort, dp, "test case %d failed: display-port not matched correctly", idx+1)
178 }
179 }
180
181 func TestXorgReaderReturnsExpectedDisplayConfig(t *testing.T) {
182 fakeXrandr, randrMock := fakexrandr.NewFakeXrandr()
183 fakeXinput, xinputMock := fakexinput.NewFakeXinput()
184 fakeDPMS, dpmsMock := fakedpms.NewFakeDPMS()
185
186 tcs := []struct {
187 outputs xrandr.Outputs
188 expectedDisplayConfig *v2.DisplayConfig
189 }{
190 {
191
192 xrandr.Outputs{},
193 &v2.DisplayConfig{
194 Displays: []v2.Display{},
195 Layout: v2.Layout{},
196 DPMS: &v2.DPMS{},
197 },
198 },
199 {
200
201 xrandr.Outputs{
202 {
203 Output: &randr.GetOutputInfoReply{Name: []byte(dp2)},
204 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: pln5376EDID}},
205 PreferredMode: &xrandr.Mode{
206 Name: "800x600",
207 Info: &randr.ModeInfo{Width: 800, Height: 600},
208 },
209 CurrentMode: &xrandr.Mode{
210 Name: "800x600",
211 Info: &randr.ModeInfo{Width: 800, Height: 600},
212 },
213 Modes: []xrandr.Mode{
214 {
215 Name: "800x600",
216 Info: &randr.ModeInfo{Width: 800, Height: 600},
217 },
218 {
219 Name: "400x300",
220 Info: &randr.ModeInfo{Width: 400, Height: 300},
221 },
222 },
223 Crtc: &randr.GetCrtcInfoReply{X: 1024, Y: 0},
224 Primary: false,
225 },
226 {
227 Output: &randr.GetOutputInfoReply{Name: []byte(hdmi2)},
228 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: wnx32EDID}},
229 PreferredMode: &xrandr.Mode{
230 Name: "1024x768",
231 Info: &randr.ModeInfo{Width: 1024, Height: 768},
232 },
233 CurrentMode: &xrandr.Mode{
234 Name: "1024x768",
235 Info: &randr.ModeInfo{Width: 1024, Height: 768},
236 },
237 Modes: []xrandr.Mode{
238 {
239 Name: "1024x768",
240 Info: &randr.ModeInfo{Width: 1024, Height: 768},
241 },
242 {
243 Name: "1024x768",
244 Info: &randr.ModeInfo{Width: 512, Height: 384},
245 },
246 },
247 Crtc: &randr.GetCrtcInfoReply{Rotation: randr.RotationRotate270, X: 0, Y: 0},
248 Primary: true,
249 },
250 {
251 Output: &randr.GetOutputInfoReply{Name: []byte(hdmi3)},
252 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: tgc18464EDID}},
253 PreferredMode: &xrandr.Mode{
254 Name: "1024x768",
255 Info: &randr.ModeInfo{Width: 1024, Height: 768},
256 },
257 CurrentMode: &xrandr.Mode{
258 Name: "1024x768",
259 Info: &randr.ModeInfo{Width: 1024, Height: 768},
260 },
261 Modes: []xrandr.Mode{
262 {
263 Name: "1024x768",
264 Info: &randr.ModeInfo{Width: 1024, Height: 768},
265 },
266 },
267 Crtc: &randr.GetCrtcInfoReply{Rotation: randr.RotationRotate90, X: 2048, Y: 0},
268 Primary: false,
269 },
270 },
271 &v2.DisplayConfig{
272 Displays: []v2.Display{
273 {
274 DisplayPort: card0HDMI2,
275 MPID: &wnx32,
276 Resolution: &v2.Resolution{Width: 1024, Height: 768},
277 SupportedResolutions: []v2.Resolution{
278 {Width: 1024, Height: 768},
279 {Width: 512, Height: 384},
280 },
281 Primary: &primary,
282 Orientation: &v2.LeftOrientation,
283 },
284 {
285 DisplayPort: card0DP2,
286 MPID: &pln5376,
287 Resolution: &v2.Resolution{Width: 800, Height: 600},
288 SupportedResolutions: []v2.Resolution{
289 {Width: 800, Height: 600},
290 {Width: 400, Height: 300},
291 },
292 Primary: ¬Primary,
293 Orientation: &v2.NormalOrientation,
294 },
295 {
296 DisplayPort: card0HDMI3,
297 MPID: &tgc18464,
298 Resolution: &v2.Resolution{Width: 1024, Height: 768},
299 SupportedResolutions: []v2.Resolution{
300 {Width: 1024, Height: 768},
301 },
302 Primary: ¬Primary,
303 Orientation: &v2.RightOrientation,
304 },
305 },
306 Layout: v2.Layout{card0HDMI2, card0DP2, card0HDMI3},
307 DPMS: &v2.DPMS{},
308 },
309 },
310 {
311
312 xrandr.Outputs{
313 {
314 Output: &randr.GetOutputInfoReply{Name: []byte(dp2)},
315 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: pln5376EDID}},
316 CurrentMode: &xrandr.Mode{
317 Name: "1024x768",
318 Info: &randr.ModeInfo{Width: 1024, Height: 768},
319 },
320 Crtc: &randr.GetCrtcInfoReply{X: 1024, Y: 0},
321 Primary: true,
322 },
323 {
324 Output: &randr.GetOutputInfoReply{Name: []byte(hdmi2)},
325 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {}},
326 CurrentMode: &xrandr.Mode{
327 Name: "1024x768",
328 Info: &randr.ModeInfo{Width: 1024, Height: 768},
329 },
330 Crtc: &randr.GetCrtcInfoReply{X: 0, Y: 0},
331 Primary: false,
332 },
333 },
334 &v2.DisplayConfig{
335 Displays: []v2.Display{
336 {
337 DisplayPort: card0HDMI2,
338 Resolution: &v2.Resolution{Width: 1024, Height: 768},
339 Orientation: &v2.NormalOrientation,
340 Primary: ¬Primary,
341 },
342 {
343 DisplayPort: card0DP2,
344 MPID: &pln5376,
345 Resolution: &v2.Resolution{Width: 1024, Height: 768},
346 Orientation: &v2.NormalOrientation,
347 Primary: &primary,
348 },
349 },
350 Layout: v2.Layout{card0HDMI2, card0DP2},
351 DPMS: &v2.DPMS{},
352 },
353 },
354 }
355
356 for idx, tc := range tcs {
357 outputIDs := map[v2.DisplayPort]xorg.OutputID{}
358 inputIDs := map[v2.InputDeviceName][]xorg.InputDeviceID{}
359 r := NewXorgDisplayReader(outputIDs, inputIDs, fakeXrandr, fakeXinput, fakeDPMS, getFakeClientWithObjects(displayPortOverrideConfigMap), logr.Discard())
360
361 randrMock.On("GetOutputs").Return(tc.outputs, nil).Once()
362 xinputMock.On("GetInputDeviceInfos").Return(nil, nil).Once()
363 dpmsMock.On("GetDPMSInfo").Return(dpmsInfo, nil).Once()
364
365 displayConfig, _, err := r.Read(context.Background())
366 assert.NoError(t, err, fmt.Sprintf("test case %d failed", idx+1))
367 assert.Equal(t, tc.expectedDisplayConfig, displayConfig, fmt.Sprintf("test case %d failed", idx+1))
368 }
369 }
370
371 func TestXorgReaderReturnsExpectedDPMS(t *testing.T) {
372 fakeXrandr, randrMock := fakexrandr.NewFakeXrandr()
373 fakeXinput, xinputMock := fakexinput.NewFakeXinput()
374 fakeDPMS, dpmsMock := fakedpms.NewFakeDPMS()
375
376 tcs := []struct {
377 dpmsInfo *dpms.Info
378 expectedDisplayConfig *v2.DisplayConfig
379 }{
380 {
381
382 &dpms.Info{
383 ScreenSaver: &xproto.GetScreenSaverReply{
384 Timeout: uint16(thirtySeconds),
385 },
386 Timeouts: &xgbdpms.GetTimeoutsReply{
387 StandbyTimeout: uint16(thirtySeconds),
388 SuspendTimeout: uint16(thirtySeconds),
389 OffTimeout: uint16(thirtySeconds),
390 },
391 Capable: true,
392 Info: &xgbdpms.InfoReply{
393 State: enabled,
394 },
395 },
396 &v2.DisplayConfig{
397 Displays: []v2.Display{},
398 Layout: v2.Layout{},
399 DPMS: &v2.DPMS{
400 Enabled: &enabled,
401 BlankTime: &thirtySeconds,
402 StandbyTime: &thirtySeconds,
403 SuspendTime: &thirtySeconds,
404 OffTime: &thirtySeconds,
405 },
406 },
407 },
408 {
409
410 &dpms.Info{
411 ScreenSaver: &xproto.GetScreenSaverReply{
412 Timeout: uint16(thirtySeconds),
413 },
414 Capable: false,
415 },
416 &v2.DisplayConfig{
417 Displays: []v2.Display{},
418 Layout: v2.Layout{},
419 DPMS: &v2.DPMS{
420 Enabled: nil,
421 BlankTime: &thirtySeconds,
422 StandbyTime: nil,
423 SuspendTime: nil,
424 OffTime: nil,
425 },
426 },
427 },
428 }
429
430 for idx, tc := range tcs {
431 outputIDs := map[v2.DisplayPort]xorg.OutputID{}
432 inputIDs := map[v2.InputDeviceName][]xorg.InputDeviceID{}
433 r := NewXorgDisplayReader(outputIDs, inputIDs, fakeXrandr, fakeXinput, fakeDPMS, getFakeClientWithObjects(displayPortOverrideConfigMap), logr.Discard())
434
435 randrMock.On("GetOutputs").Return(nil, nil).Once()
436 xinputMock.On("GetInputDeviceInfos").Return(nil, nil).Once()
437 dpmsMock.On("GetDPMSInfo").Return(tc.dpmsInfo, nil).Once()
438
439 displayConfig, _, err := r.Read(context.Background())
440 assert.NoError(t, err, fmt.Sprintf("test case %d failed", idx+1))
441 assert.Equal(t, tc.expectedDisplayConfig, displayConfig, fmt.Sprintf("test case %d failed", idx+1))
442 }
443 }
444
445 func TestXorgReaderReturnsExpectedInputDevices(t *testing.T) {
446 fakeXrandr, randrMock := fakexrandr.NewFakeXrandr()
447 fakeXinput, xinputMock := fakexinput.NewFakeXinput()
448 fakeDPMS, dpmsMock := fakedpms.NewFakeDPMS()
449
450 outputIDs := map[v2.DisplayPort]xorg.OutputID{}
451 inputIDs := map[v2.InputDeviceName][]xorg.InputDeviceID{}
452 r := NewXorgDisplayReader(outputIDs, inputIDs, fakeXrandr, fakeXinput, fakeDPMS, getFakeClientWithObjects(displayPortOverrideConfigMap), logr.Discard())
453
454 inputDeviceInfos := xinput.InputDeviceInfos{
455 {ID: inputDeviceID1, Name: eloTouchSolutionsUSB, Absolute: true},
456 {ID: inputDeviceID2, Name: eloTouchSolutions2107, Absolute: true},
457 {ID: inputDeviceID3, Name: eGalaxEXC3189, Absolute: true},
458 {ID: inputDeviceID4, Name: eGalaxEXC3189Mouse, Absolute: true},
459 {ID: inputDeviceID5, Name: eloTouchSolutionsUSB, Absolute: true},
460 }
461
462 randrMock.On("GetOutputs").Return(nil, nil).Once()
463 dpmsMock.On("GetDPMSInfo").Return(dpmsInfo, nil).Once()
464 xinputMock.On("GetInputDeviceInfos").Return(inputDeviceInfos, nil).Once()
465
466 expectedDisplayConfig := &v2.DisplayConfig{
467 Displays: []v2.Display{},
468 Layout: v2.Layout{},
469 DPMS: &v2.DPMS{},
470 }
471 expectedInputDevices := []v2.InputDeviceName{
472 v2.InputDeviceName(eloTouchSolutionsUSB),
473 v2.InputDeviceName(eloTouchSolutions2107),
474 v2.InputDeviceName(eGalaxEXC3189),
475 v2.InputDeviceName(eGalaxEXC3189Mouse),
476 v2.InputDeviceName(eloTouchSolutionsUSB),
477 }
478 expectedInputIDs := map[v2.InputDeviceName][]xorg.InputDeviceID{
479 v2.InputDeviceName(eloTouchSolutionsUSB): {inputDeviceID1, inputDeviceID5},
480 v2.InputDeviceName(eloTouchSolutions2107): {inputDeviceID2},
481 v2.InputDeviceName(eGalaxEXC3189): {inputDeviceID3},
482 v2.InputDeviceName(eGalaxEXC3189Mouse): {inputDeviceID4},
483 }
484
485 displayConfig, inputDevices, err := r.Read(context.Background())
486 assert.NoError(t, err)
487 assert.Equal(t, expectedInputDevices, inputDevices)
488 assert.Equal(t, expectedDisplayConfig, displayConfig)
489
490 xorgDisplayReader, ok := r.(*displayReader)
491 require.True(t, ok)
492 assert.Equal(t, expectedInputIDs, xorgDisplayReader.inputIDs)
493 }
494
495 func TestXorgReaderWithPreferredResolution(t *testing.T) {
496 fakeXrandr, randrMock := fakexrandr.NewFakeXrandr()
497 fakeXinput, xinputMock := fakexinput.NewFakeXinput()
498 fakeDPMS, dpmsMock := fakedpms.NewFakeDPMS()
499
500 outputs := xrandr.Outputs{
501 {
502 Output: &randr.GetOutputInfoReply{Name: []byte(dp2)},
503 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: pln5376EDID}},
504 PreferredMode: &xrandr.Mode{
505 Name: "800x600",
506 Info: &randr.ModeInfo{Width: 800, Height: 600},
507 },
508 CurrentMode: &xrandr.Mode{
509 Name: "1600x800",
510 Info: &randr.ModeInfo{Width: 1600, Height: 800},
511 },
512 Modes: []xrandr.Mode{
513 {
514 Name: "400x300",
515 Info: &randr.ModeInfo{Width: 1600, Height: 800},
516 },
517 {
518 Name: "800x600",
519 Info: &randr.ModeInfo{Width: 800, Height: 600},
520 },
521 },
522 Crtc: &randr.GetCrtcInfoReply{X: 0, Y: 0},
523 },
524 }
525
526 expectedDisplayConfig := &v2.DisplayConfig{
527 Displays: []v2.Display{
528 {
529 DisplayPort: card0DP2,
530 MPID: &pln5376,
531 Resolution: &v2.Resolution{Width: 800, Height: 600},
532 SupportedResolutions: []v2.Resolution{
533 {Width: 1600, Height: 800},
534 {Width: 800, Height: 600},
535 },
536 Primary: ¬Primary,
537 Orientation: &v2.NormalOrientation,
538 },
539 },
540 Layout: v2.Layout{card0DP2},
541 DPMS: &v2.DPMS{},
542 }
543
544 randrMock.On("GetOutputs").Return(outputs, nil).Once()
545 xinputMock.On("GetInputDeviceInfos").Return(nil, nil).Once()
546 dpmsMock.On("GetDPMSInfo").Return(dpmsInfo, nil).Once()
547
548 outputIDs := map[v2.DisplayPort]xorg.OutputID{}
549 inputIDs := map[v2.InputDeviceName][]xorg.InputDeviceID{}
550 r := NewXorgDisplayReader(outputIDs, inputIDs, fakeXrandr, fakeXinput, fakeDPMS, getFakeClientWithObjects(displayPortOverrideConfigMap), logr.Discard())
551
552 displayConfig, _, err := r.Read(context.Background(), reader.WithPreferredResolution())
553 assert.NoError(t, err)
554 assert.Equal(t, expectedDisplayConfig, displayConfig)
555 }
556
557 func TestXorgReaderWithDefaultLayout(t *testing.T) {
558 fakeXrandr, randrMock := fakexrandr.NewFakeXrandr()
559 fakeXinput, xinputMock := fakexinput.NewFakeXinput()
560 fakeDPMS, dpmsMock := fakedpms.NewFakeDPMS()
561
562 outputs := xrandr.Outputs{
563 {
564 Output: &randr.GetOutputInfoReply{Name: []byte(dp2)},
565 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: pln5376EDID}},
566 CurrentMode: &xrandr.Mode{
567 Name: "800x600",
568 Info: &randr.ModeInfo{Width: 800, Height: 600},
569 },
570 Crtc: &randr.GetCrtcInfoReply{X: 1024, Y: 0},
571 Primary: false,
572 },
573 {
574 Output: &randr.GetOutputInfoReply{Name: []byte(hdmi2)},
575 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: wnx32EDID}},
576 CurrentMode: &xrandr.Mode{
577 Name: "1024x768",
578 Info: &randr.ModeInfo{Width: 1024, Height: 768},
579 },
580 Crtc: &randr.GetCrtcInfoReply{Rotation: randr.RotationRotate270, X: 0, Y: 0},
581 Primary: true,
582 },
583 {
584 Output: &randr.GetOutputInfoReply{Name: []byte(hdmi3)},
585 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: tgc18464EDID}},
586 CurrentMode: &xrandr.Mode{
587 Name: "1024x768",
588 Info: &randr.ModeInfo{Width: 1024, Height: 768},
589 },
590 Crtc: &randr.GetCrtcInfoReply{Rotation: randr.RotationRotate90, X: 2048, Y: 0},
591 Primary: false,
592 },
593 }
594 expectedDisplayConfig := &v2.DisplayConfig{
595 Displays: []v2.Display{
596 {
597 DisplayPort: card0HDMI3,
598 MPID: &tgc18464,
599 Resolution: &v2.Resolution{Width: 1024, Height: 768},
600 Primary: ¬Primary,
601 Orientation: &v2.RightOrientation,
602 },
603 {
604 DisplayPort: card0HDMI2,
605 MPID: &wnx32,
606 Resolution: &v2.Resolution{Width: 1024, Height: 768},
607 Primary: &primary,
608 Orientation: &v2.LeftOrientation,
609 },
610 {
611 DisplayPort: card0DP2,
612 MPID: &pln5376,
613 Resolution: &v2.Resolution{Width: 800, Height: 600},
614 Primary: ¬Primary,
615 Orientation: &v2.NormalOrientation,
616 },
617 },
618 Layout: v2.Layout{card0HDMI3, card0HDMI2, card0DP2},
619 DPMS: &v2.DPMS{},
620 }
621
622 randrMock.On("GetOutputs").Return(outputs, nil).Once()
623 xinputMock.On("GetInputDeviceInfos").Return(nil, nil).Once()
624 dpmsMock.On("GetDPMSInfo").Return(dpmsInfo, nil).Once()
625
626 outputIDs := map[v2.DisplayPort]xorg.OutputID{}
627 inputIDs := map[v2.InputDeviceName][]xorg.InputDeviceID{}
628 r := NewXorgDisplayReader(outputIDs, inputIDs, fakeXrandr, fakeXinput, fakeDPMS, getFakeClientWithObjects(displayPortOverrideConfigMap), logr.Discard())
629
630 displayConfig, _, err := r.Read(context.Background(), reader.WithDefaultLayout())
631 assert.NoError(t, err)
632 assert.Equal(t, expectedDisplayConfig, displayConfig)
633 }
634
635 func TestXorgReaderWithIgnoreSupportedResolutions(t *testing.T) {
636 fakeXrandr, randrMock := fakexrandr.NewFakeXrandr()
637 fakeXinput, xinputMock := fakexinput.NewFakeXinput()
638 fakeDPMS, dpmsMock := fakedpms.NewFakeDPMS()
639
640 outputs := xrandr.Outputs{
641 {
642 Output: &randr.GetOutputInfoReply{Name: []byte(dp2)},
643 Properties: map[string]*randr.GetOutputPropertyReply{"EDID": {Data: pln5376EDID}},
644 PreferredMode: &xrandr.Mode{
645 Name: "800x600",
646 Info: &randr.ModeInfo{Width: 800, Height: 600},
647 },
648 CurrentMode: &xrandr.Mode{
649 Name: "1600x800",
650 Info: &randr.ModeInfo{Width: 1600, Height: 800},
651 },
652 Modes: []xrandr.Mode{
653 {
654 Name: "400x300",
655 Info: &randr.ModeInfo{Width: 1600, Height: 800},
656 },
657 {
658 Name: "800x600",
659 Info: &randr.ModeInfo{Width: 800, Height: 600},
660 },
661 },
662 Crtc: &randr.GetCrtcInfoReply{X: 0, Y: 0},
663 },
664 }
665
666 expectedDisplayConfig := &v2.DisplayConfig{
667 Displays: []v2.Display{
668 {
669 DisplayPort: card0DP2,
670 MPID: &pln5376,
671 Resolution: &v2.Resolution{Width: 1600, Height: 800},
672 Primary: ¬Primary,
673 Orientation: &v2.NormalOrientation,
674 },
675 },
676 Layout: v2.Layout{card0DP2},
677 DPMS: &v2.DPMS{},
678 }
679
680 randrMock.On("GetOutputs").Return(outputs, nil).Once()
681 xinputMock.On("GetInputDeviceInfos").Return(nil, nil).Once()
682 dpmsMock.On("GetDPMSInfo").Return(dpmsInfo, nil).Once()
683
684 outputIDs := map[v2.DisplayPort]xorg.OutputID{}
685 inputIDs := map[v2.InputDeviceName][]xorg.InputDeviceID{}
686 r := NewXorgDisplayReader(outputIDs, inputIDs, fakeXrandr, fakeXinput, fakeDPMS, getFakeClientWithObjects(displayPortOverrideConfigMap), logr.Discard())
687
688 displayConfig, _, err := r.Read(context.Background(), reader.WithIgnoreSupportedResolutions())
689 assert.NoError(t, err)
690 assert.Equal(t, expectedDisplayConfig, displayConfig)
691 }
692
693 func TestXorgReaderReturnsErrorsFromXrandrAsExpected(t *testing.T) {
694 fakeXrandr, randrMock := fakexrandr.NewFakeXrandr()
695 fakeXinput, xinputMock := fakexinput.NewFakeXinput()
696 fakeDPMS, dpmsMock := fakedpms.NewFakeDPMS()
697
698 r := NewXorgDisplayReader(map[v2.DisplayPort]xorg.OutputID{}, map[v2.InputDeviceName][]xorg.InputDeviceID{}, fakeXrandr, fakeXinput, fakeDPMS, getFakeClientWithObjects(displayPortOverrideConfigMap), logr.Discard())
699
700 expectedError := errors.New("failed to get outputs")
701 randrMock.On("GetOutputs").Return(nil, expectedError).Once()
702 xinputMock.On("GetInputDeviceInfos").Return(nil, nil).Once()
703 dpmsMock.On("GetDPMSInfo").Return(dpmsInfo, nil).Once()
704
705 displayConfig, inputDevices, err := r.Read(context.Background())
706 assert.ErrorIs(t, err, expectedError)
707 assert.Nil(t, displayConfig)
708 assert.Nil(t, inputDevices)
709 }
710
711 func TestXorgReaderReturnsErrorsFromXinputAsExpected(t *testing.T) {
712 fakeXrandr, randrMock := fakexrandr.NewFakeXrandr()
713 fakeXinput, xinputMock := fakexinput.NewFakeXinput()
714 fakeDPMS, dpmsMock := fakedpms.NewFakeDPMS()
715
716 r := NewXorgDisplayReader(map[v2.DisplayPort]xorg.OutputID{}, map[v2.InputDeviceName][]xorg.InputDeviceID{}, fakeXrandr, fakeXinput, fakeDPMS, getFakeClientWithObjects(displayPortOverrideConfigMap), logr.Discard())
717
718 expectedError := errors.New("failed to get input devices")
719
720 randrMock.On("GetOutputs").Return(nil, nil).Once()
721 dpmsMock.On("GetDPMSInfo").Return(dpmsInfo, nil).Once()
722 xinputMock.On("GetInputDeviceInfos").Return(nil, expectedError).Once()
723
724 displayConfig, inputDevices, err := r.Read(context.Background())
725 assert.ErrorIs(t, err, expectedError)
726 assert.Nil(t, displayConfig)
727 assert.Nil(t, inputDevices)
728 }
729
730
731 func getFakeClientWithObjects(objs ...client.Object) client.Client {
732 return fake.NewClientBuilder().WithScheme(createScheme()).WithObjects(objs...).Build()
733 }
734
735 func createScheme() *runtime.Scheme {
736 scheme := runtime.NewScheme()
737 utilruntime.Must(clientgoscheme.AddToScheme(scheme))
738 return scheme
739 }
740
View as plain text