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

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