github.com/vmware/govmomi@v0.51.0/vim25/types/helpers_test.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package types
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"slices"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  
    15  	"github.com/vmware/govmomi/vim25/xml"
    16  )
    17  
    18  func TestVirtualMachineConfigInfoToConfigSpec(t *testing.T) {
    19  	testCases := []struct {
    20  		name string
    21  		conf VirtualMachineConfigInfo
    22  		spec VirtualMachineConfigSpec
    23  		fail bool
    24  	}{
    25  		{
    26  			name: "default value",
    27  			conf: VirtualMachineConfigInfo{},
    28  			spec: VirtualMachineConfigSpec{},
    29  		},
    30  		{
    31  			name: "matching names",
    32  			conf: VirtualMachineConfigInfo{
    33  				Name: "Hello, world.",
    34  			},
    35  			spec: VirtualMachineConfigSpec{
    36  				Name: "Hello, world.",
    37  			},
    38  		},
    39  		{
    40  			name: "matching nics",
    41  			conf: VirtualMachineConfigInfo{
    42  				Name: "Hello, world.",
    43  				Hardware: VirtualHardware{
    44  					Device: []BaseVirtualDevice{
    45  						&VirtualVmxnet3{
    46  							VirtualVmxnet: VirtualVmxnet{
    47  								VirtualEthernetCard: VirtualEthernetCard{
    48  									VirtualDevice: VirtualDevice{
    49  										Key: 3,
    50  									},
    51  									MacAddress: "00:11:22:33:44:55:66:77",
    52  								},
    53  							},
    54  						},
    55  					},
    56  				},
    57  			},
    58  			spec: VirtualMachineConfigSpec{
    59  				Name: "Hello, world.",
    60  				DeviceChange: []BaseVirtualDeviceConfigSpec{
    61  					&VirtualDeviceConfigSpec{
    62  						Operation:     VirtualDeviceConfigSpecOperationAdd,
    63  						FileOperation: VirtualDeviceConfigSpecFileOperationCreate,
    64  						Device: &VirtualVmxnet3{
    65  							VirtualVmxnet: VirtualVmxnet{
    66  								VirtualEthernetCard: VirtualEthernetCard{
    67  									VirtualDevice: VirtualDevice{
    68  										Key: 3,
    69  									},
    70  									MacAddress: "00:11:22:33:44:55:66:77",
    71  								},
    72  							},
    73  						},
    74  					},
    75  				},
    76  			},
    77  		},
    78  		{
    79  			name: "nics with different mac addresses",
    80  			fail: true,
    81  			conf: VirtualMachineConfigInfo{
    82  				Name: "Hello, world.",
    83  				Hardware: VirtualHardware{
    84  					Device: []BaseVirtualDevice{
    85  						&VirtualVmxnet3{
    86  							VirtualVmxnet: VirtualVmxnet{
    87  								VirtualEthernetCard: VirtualEthernetCard{
    88  									VirtualDevice: VirtualDevice{
    89  										Key: 3,
    90  									},
    91  									MacAddress: "00:11:22:33:44:55:66:77",
    92  								},
    93  							},
    94  						},
    95  					},
    96  				},
    97  			},
    98  			spec: VirtualMachineConfigSpec{
    99  				Name: "Hello, world.",
   100  				DeviceChange: []BaseVirtualDeviceConfigSpec{
   101  					&VirtualDeviceConfigSpec{
   102  						Operation:     VirtualDeviceConfigSpecOperationAdd,
   103  						FileOperation: VirtualDeviceConfigSpecFileOperationCreate,
   104  						Device: &VirtualVmxnet3{
   105  							VirtualVmxnet: VirtualVmxnet{
   106  								VirtualEthernetCard: VirtualEthernetCard{
   107  									VirtualDevice: VirtualDevice{
   108  										Key: 3,
   109  									},
   110  									MacAddress: "00:11:22:33:44:55:66:88",
   111  								},
   112  							},
   113  						},
   114  					},
   115  				},
   116  			},
   117  		},
   118  		{
   119  			name: "vAppConfig",
   120  			conf: VirtualMachineConfigInfo{
   121  				Name: "Hello, world.",
   122  				VAppConfig: &VmConfigInfo{
   123  					InstallBootRequired: false,
   124  					IpAssignment:        VAppIPAssignmentInfo{},
   125  					Product: []VAppProductInfo{
   126  						{
   127  							Key:  1,
   128  							Name: "P1",
   129  						},
   130  					},
   131  				},
   132  			},
   133  			spec: VirtualMachineConfigSpec{
   134  				Name: "Hello, world.",
   135  				VAppConfig: &VmConfigSpec{
   136  					InstallBootRequired: NewBool(false),
   137  					IpAssignment:        &VAppIPAssignmentInfo{},
   138  					Product: []VAppProductSpec{
   139  						{
   140  							ArrayUpdateSpec: ArrayUpdateSpec{
   141  								Operation: ArrayUpdateOperationAdd,
   142  							},
   143  							Info: &VAppProductInfo{
   144  								Key:  1,
   145  								Name: "P1",
   146  							},
   147  						},
   148  					},
   149  				},
   150  			},
   151  		},
   152  		{
   153  			name: "really big config",
   154  			conf: VirtualMachineConfigInfo{
   155  				Name:    "vm-001",
   156  				GuestId: "otherGuest",
   157  				Files:   VirtualMachineFileInfo{VmPathName: "[datastore1]"},
   158  				Hardware: VirtualHardware{
   159  					NumCPU:   1,
   160  					MemoryMB: 128,
   161  					Device: []BaseVirtualDevice{
   162  						&VirtualLsiLogicController{
   163  							VirtualSCSIController: VirtualSCSIController{
   164  								SharedBus: VirtualSCSISharingNoSharing,
   165  								VirtualController: VirtualController{
   166  									BusNumber: 0,
   167  									VirtualDevice: VirtualDevice{
   168  										Key: 1000,
   169  									},
   170  								},
   171  							},
   172  						},
   173  						&VirtualDisk{
   174  							VirtualDevice: VirtualDevice{
   175  								Key:           0,
   176  								ControllerKey: 1000,
   177  								UnitNumber:    new(int32), // zero default value
   178  								Backing: &VirtualDiskFlatVer2BackingInfo{
   179  									DiskMode:        string(VirtualDiskModePersistent),
   180  									ThinProvisioned: NewBool(true),
   181  									VirtualDeviceFileBackingInfo: VirtualDeviceFileBackingInfo{
   182  										FileName: "[datastore1]",
   183  									},
   184  								},
   185  							},
   186  							CapacityInKB: 4000000,
   187  						},
   188  						&VirtualE1000{
   189  							VirtualEthernetCard: VirtualEthernetCard{
   190  								VirtualDevice: VirtualDevice{
   191  									Key: 0,
   192  									DeviceInfo: &Description{
   193  										Label:   "Network Adapter 1",
   194  										Summary: "VM Network",
   195  									},
   196  									Backing: &VirtualEthernetCardNetworkBackingInfo{
   197  										VirtualDeviceDeviceBackingInfo: VirtualDeviceDeviceBackingInfo{
   198  											DeviceName: "VM Network",
   199  										},
   200  									},
   201  								},
   202  								AddressType: string(VirtualEthernetCardMacTypeGenerated),
   203  							},
   204  						},
   205  					},
   206  				},
   207  				ExtraConfig: []BaseOptionValue{
   208  					&OptionValue{Key: "bios.bootOrder", Value: "ethernet0"},
   209  				},
   210  			},
   211  			spec: VirtualMachineConfigSpec{
   212  				Name:     "vm-001",
   213  				GuestId:  "otherGuest",
   214  				Files:    &VirtualMachineFileInfo{VmPathName: "[datastore1]"},
   215  				NumCPUs:  1,
   216  				MemoryMB: 128,
   217  				DeviceChange: []BaseVirtualDeviceConfigSpec{
   218  					&VirtualDeviceConfigSpec{
   219  						Operation:     VirtualDeviceConfigSpecOperationAdd,
   220  						FileOperation: VirtualDeviceConfigSpecFileOperationCreate,
   221  						Device: &VirtualLsiLogicController{VirtualSCSIController{
   222  							SharedBus: VirtualSCSISharingNoSharing,
   223  							VirtualController: VirtualController{
   224  								BusNumber: 0,
   225  								VirtualDevice: VirtualDevice{
   226  									Key: 1000,
   227  								},
   228  							},
   229  						}},
   230  					},
   231  					&VirtualDeviceConfigSpec{
   232  						Operation:     VirtualDeviceConfigSpecOperationAdd,
   233  						FileOperation: VirtualDeviceConfigSpecFileOperationCreate,
   234  						Device: &VirtualDisk{
   235  							VirtualDevice: VirtualDevice{
   236  								Key:           0,
   237  								ControllerKey: 1000,
   238  								UnitNumber:    new(int32), // zero default value
   239  								Backing: &VirtualDiskFlatVer2BackingInfo{
   240  									DiskMode:        string(VirtualDiskModePersistent),
   241  									ThinProvisioned: NewBool(true),
   242  									VirtualDeviceFileBackingInfo: VirtualDeviceFileBackingInfo{
   243  										FileName: "[datastore1]",
   244  									},
   245  								},
   246  							},
   247  							CapacityInKB: 4000000,
   248  						},
   249  					},
   250  					&VirtualDeviceConfigSpec{
   251  						Operation:     VirtualDeviceConfigSpecOperationAdd,
   252  						FileOperation: VirtualDeviceConfigSpecFileOperationCreate,
   253  						Device: &VirtualE1000{VirtualEthernetCard{
   254  							VirtualDevice: VirtualDevice{
   255  								Key: 0,
   256  								DeviceInfo: &Description{
   257  									Label:   "Network Adapter 1",
   258  									Summary: "VM Network",
   259  								},
   260  								Backing: &VirtualEthernetCardNetworkBackingInfo{
   261  									VirtualDeviceDeviceBackingInfo: VirtualDeviceDeviceBackingInfo{
   262  										DeviceName: "VM Network",
   263  									},
   264  								},
   265  							},
   266  							AddressType: string(VirtualEthernetCardMacTypeGenerated),
   267  						}},
   268  					},
   269  				},
   270  				ExtraConfig: []BaseOptionValue{
   271  					&OptionValue{Key: "bios.bootOrder", Value: "ethernet0"},
   272  				},
   273  			},
   274  		},
   275  	}
   276  
   277  	for i := range testCases {
   278  		tc := testCases[i]
   279  		t.Run(tc.name, func(t *testing.T) {
   280  			e, a := tc.spec, tc.conf.ToConfigSpec()
   281  			ed, err := xml.MarshalIndent(e, "", "  ")
   282  			if err != nil {
   283  				t.Fatalf("failed to marshal expected ConfigSpec: %v", err)
   284  			}
   285  			ad, err := xml.MarshalIndent(a, "", "  ")
   286  			if err != nil {
   287  				t.Fatalf("failed to marshal actual   ConfigSpec: %v", err)
   288  			}
   289  			eds, ads := string(ed), string(ad)
   290  			if eds != ads && !tc.fail {
   291  				t.Errorf("unexpected error: \n\n"+
   292  					"exp=%+v\n\nact=%+v\n\n"+
   293  					"exp.s=%s\n\nact.s=%s\n\n", e, a, eds, ads)
   294  			} else if eds == ads && tc.fail {
   295  				t.Errorf("expected error did not occur: \n\n"+
   296  					"exp=%+v\n\nact=%+v\n\n"+
   297  					"exp.s=%s\n\nact.s=%s\n\n", e, a, eds, ads)
   298  			}
   299  		})
   300  	}
   301  }
   302  
   303  type toStringTestCase struct {
   304  	name     string
   305  	in       any
   306  	expected string
   307  }
   308  
   309  func newToStringTestCases[T any](in T, expected string) []toStringTestCase {
   310  	return newToStringTestCasesWithTestCaseName(
   311  		in, expected, reflect.TypeOf(in).Name())
   312  }
   313  
   314  func newToStringTestCasesWithTestCaseName[T any](
   315  	in T, expected, testCaseName string) []toStringTestCase {
   316  
   317  	return []toStringTestCase{
   318  		{
   319  			name:     testCaseName,
   320  			in:       in,
   321  			expected: expected,
   322  		},
   323  		{
   324  			name:     "*" + testCaseName,
   325  			in:       &[]T{in}[0],
   326  			expected: expected,
   327  		},
   328  		{
   329  			name:     "(any)(" + testCaseName + ")",
   330  			in:       (any)(in),
   331  			expected: expected,
   332  		},
   333  		{
   334  			name:     "(any)(*" + testCaseName + ")",
   335  			in:       (any)(&[]T{in}[0]),
   336  			expected: expected,
   337  		},
   338  		{
   339  			name:     "(any)((*" + testCaseName + ")(nil))",
   340  			in:       (any)((*T)(nil)),
   341  			expected: "null",
   342  		},
   343  	}
   344  }
   345  
   346  type toStringTypeWithErr struct {
   347  	errOnCall []int
   348  	callCount *int
   349  	doPanic   bool
   350  }
   351  
   352  func (t toStringTypeWithErr) String() string {
   353  	return "{}"
   354  }
   355  
   356  func (t toStringTypeWithErr) MarshalJSON() ([]byte, error) {
   357  	defer func() {
   358  		*t.callCount++
   359  	}()
   360  	if !slices.Contains(t.errOnCall, *t.callCount) {
   361  		return []byte{'{', '}'}, nil
   362  	}
   363  	if t.doPanic {
   364  		panic(fmt.Errorf("marshal json panic'd"))
   365  	}
   366  	return nil, fmt.Errorf("marshal json failed")
   367  }
   368  
   369  func TestToString(t *testing.T) {
   370  	const (
   371  		helloWorld = "Hello, world."
   372  	)
   373  
   374  	testCases := []toStringTestCase{
   375  		{
   376  			name:     "nil",
   377  			in:       nil,
   378  			expected: "null",
   379  		},
   380  	}
   381  
   382  	testCases = append(testCases, newToStringTestCases(
   383  		"Hello, world.", "Hello, world.")...)
   384  
   385  	testCases = append(testCases, newToStringTestCasesWithTestCaseName(
   386  		byte(1), "1", "byte")...)
   387  	testCases = append(testCases, newToStringTestCasesWithTestCaseName(
   388  		'a', "97", "rune")...)
   389  
   390  	testCases = append(testCases, newToStringTestCases(
   391  		true, "true")...)
   392  
   393  	testCases = append(testCases, newToStringTestCases(
   394  		complex(float32(1), float32(4)), "(1+4i)")...)
   395  	testCases = append(testCases, newToStringTestCases(
   396  		complex(float64(1), float64(4)), "(1+4i)")...)
   397  
   398  	testCases = append(testCases, newToStringTestCases(
   399  		float32(1.1), "1.1")...)
   400  	testCases = append(testCases, newToStringTestCases(
   401  		float64(1.1), "1.1")...)
   402  
   403  	testCases = append(testCases, newToStringTestCases(
   404  		int(1), "1")...)
   405  	testCases = append(testCases, newToStringTestCases(
   406  		int8(1), "1")...)
   407  	testCases = append(testCases, newToStringTestCases(
   408  		int16(1), "1")...)
   409  	testCases = append(testCases, newToStringTestCases(
   410  		int32(1), "1")...)
   411  	testCases = append(testCases, newToStringTestCases(
   412  		int64(1), "1")...)
   413  
   414  	testCases = append(testCases, newToStringTestCases(
   415  		uint(1), "1")...)
   416  	testCases = append(testCases, newToStringTestCases(
   417  		uint8(1), "1")...)
   418  	testCases = append(testCases, newToStringTestCases(
   419  		uint16(1), "1")...)
   420  	testCases = append(testCases, newToStringTestCases(
   421  		uint32(1), "1")...)
   422  	testCases = append(testCases, newToStringTestCases(
   423  		uint64(1), "1")...)
   424  
   425  	testCases = append(testCases, newToStringTestCases(
   426  		VirtualMachineConfigSpec{},
   427  		`{"_typeName":"VirtualMachineConfigSpec"}`)...)
   428  	testCases = append(testCases, newToStringTestCasesWithTestCaseName(
   429  		VirtualMachineConfigSpec{
   430  			VAppConfig: (*VmConfigSpec)(nil),
   431  		},
   432  		`{"_typeName":"VirtualMachineConfigSpec","vAppConfig":null}`,
   433  		"VirtualMachineConfigSpec w nil iface")...)
   434  
   435  	testCases = append(testCases, toStringTestCase{
   436  		name:     "MarshalJSON returns error on special encode",
   437  		in:       toStringTypeWithErr{callCount: new(int), errOnCall: []int{0}},
   438  		expected: "{}",
   439  	})
   440  	testCases = append(testCases, toStringTestCase{
   441  		name:     "MarshalJSON returns error on special and stdlib encode",
   442  		in:       toStringTypeWithErr{callCount: new(int), errOnCall: []int{0, 1}},
   443  		expected: "{}",
   444  	})
   445  	testCases = append(testCases, toStringTestCase{
   446  		name:     "MarshalJSON panics on special encode",
   447  		in:       toStringTypeWithErr{callCount: new(int), doPanic: true, errOnCall: []int{0}},
   448  		expected: "{}",
   449  	})
   450  	testCases = append(testCases, toStringTestCase{
   451  		name:     "MarshalJSON panics on special and stdlib encode",
   452  		in:       toStringTypeWithErr{callCount: new(int), doPanic: true, errOnCall: []int{0, 1}},
   453  		expected: "{}",
   454  	})
   455  
   456  	for i := range testCases {
   457  		tc := testCases[i]
   458  		t.Run(tc.name, func(t *testing.T) {
   459  			t.Parallel()
   460  			assert.Equal(t, tc.expected, ToString(tc.in))
   461  		})
   462  	}
   463  }