...

Source file src/edge-infra.dev/pkg/sds/display/displaymanager/reader/xorg/xorg_reader_test.go

Documentation: edge-infra.dev/pkg/sds/display/displaymanager/reader/xorg

     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  			// test case 1: match by EDID
   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  			// test case 2: match by EDID
   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  			// test case 3: output has EDID but connector does not, so match by name
   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  			// test case 4: match by santisied name
   148  			xrandr.Output{
   149  				Output: &randr.GetOutputInfoReply{Name: []byte("VGA1")},
   150  			},
   151  			"card1-vga-1",
   152  		},
   153  		{
   154  			// test case 5: match by santisied HDMI name
   155  			xrandr.Output{
   156  				Output: &randr.GetOutputInfoReply{Name: []byte("HDMI4")},
   157  			},
   158  			"card1-HDMI-A-4",
   159  		},
   160  		{
   161  			// test case 6: overridden by display-port-override
   162  			xrandr.Output{
   163  				Output: &randr.GetOutputInfoReply{Name: []byte("VIRTUAL1")},
   164  			},
   165  			"card1-Virtual-1",
   166  		},
   167  		{
   168  			// test case 7: unmatched display is marked unknown
   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  			// test case 1: empty outputs returns empty display config
   192  			xrandr.Outputs{},
   193  			&v2.DisplayConfig{
   194  				Displays: []v2.Display{},
   195  				Layout:   v2.Layout{},
   196  				DPMS:     &v2.DPMS{},
   197  			},
   198  		},
   199  		{
   200  			// test case 2: multiple outputs represented in display config
   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:     &notPrimary,
   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:     &notPrimary,
   303  						Orientation: &v2.RightOrientation,
   304  					},
   305  				},
   306  				Layout: v2.Layout{card0HDMI2, card0DP2, card0HDMI3},
   307  				DPMS:   &v2.DPMS{},
   308  			},
   309  		},
   310  		{
   311  			// test case 3: outputs without EDIDs do not have MPID field
   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:     &notPrimary,
   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  			// test case 1: capable queries all fields
   382  			&dpms.Info{
   383  				ScreenSaver: &xproto.GetScreenSaverReply{
   384  					Timeout: uint16(thirtySeconds), /* #nosec G115 */
   385  				},
   386  				Timeouts: &xgbdpms.GetTimeoutsReply{
   387  					StandbyTimeout: uint16(thirtySeconds), /* #nosec G115 */
   388  					SuspendTimeout: uint16(thirtySeconds), /* #nosec G115 */
   389  					OffTimeout:     uint16(thirtySeconds), /* #nosec G115 */
   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  			// test case 2: non-capable only queries blank time
   410  			&dpms.Info{
   411  				ScreenSaver: &xproto.GetScreenSaverReply{
   412  					Timeout: uint16(thirtySeconds), /* #nosec G115 */
   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}, // duplicate device with unique ID
   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), // duplicate device
   477  	}
   478  	expectedInputIDs := map[v2.InputDeviceName][]xorg.InputDeviceID{
   479  		v2.InputDeviceName(eloTouchSolutionsUSB):  {inputDeviceID1, inputDeviceID5}, // multiple IDs for device name
   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:     &notPrimary,
   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:     &notPrimary,
   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:     &notPrimary,
   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:     &notPrimary,
   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  // Create a fake K8s client with initialised with objects.
   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