github.com/vmware/govmomi@v0.51.0/vim25/types/json_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  	"bytes"
     9  	"os"
    10  	"reflect"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/google/go-cmp/cmp"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  var serializationTests = []struct {
    20  	name      string
    21  	file      string
    22  	data      any
    23  	goType    reflect.Type
    24  	expDecErr string
    25  }{
    26  	{
    27  		name:   "vminfo",
    28  		file:   "./testdata/vminfo.json",
    29  		data:   &vmInfoObjForTests,
    30  		goType: reflect.TypeOf(VirtualMachineConfigInfo{}),
    31  	},
    32  	{
    33  		name:   "retrieveResult",
    34  		file:   "./testdata/retrieveResult.json",
    35  		data:   &retrieveResultForTests,
    36  		goType: reflect.TypeOf(RetrieveResult{}),
    37  	},
    38  	{
    39  		name:      "vminfo-invalid-type-name-value",
    40  		file:      "./testdata/vminfo-invalid-type-name-value.json",
    41  		data:      &vmInfoObjForTests,
    42  		goType:    reflect.TypeOf(VirtualMachineConfigInfo{}),
    43  		expDecErr: `json: cannot unmarshal bool into Go struct field VirtualMachineConfigInfo.extraConfig of type string`,
    44  	},
    45  }
    46  
    47  func TestSerialization(t *testing.T) {
    48  	for _, test := range serializationTests {
    49  		t.Run(test.name+" Decode", func(t *testing.T) {
    50  			f, err := os.Open(test.file)
    51  			if err != nil {
    52  				t.Fatal(err)
    53  			}
    54  			defer f.Close()
    55  
    56  			dec := NewJSONDecoder(f)
    57  
    58  			ee := test.expDecErr
    59  			data := reflect.New(test.goType).Interface()
    60  			if err := dec.Decode(data); err != nil {
    61  				if ee != err.Error() {
    62  					t.Errorf("expected error mismatch: e=%v, a=%v", ee, err)
    63  				} else if ee == "" {
    64  					t.Errorf("unexpected error: %v", err)
    65  				}
    66  			} else if ee != "" {
    67  				t.Errorf("expected error did not occur: %v", ee)
    68  			} else {
    69  				a, e := data, test.data
    70  				if diff := cmp.Diff(a, e); diff != "" {
    71  					t.Errorf("mismatched %v: %s", test.name, diff)
    72  				}
    73  			}
    74  		})
    75  
    76  		t.Run(test.name+" Encode", func(t *testing.T) {
    77  			if test.expDecErr != "" {
    78  				t.Skip("skipping due to expected decode error")
    79  			}
    80  
    81  			expJSON, err := os.ReadFile(test.file)
    82  			if err != nil {
    83  				t.Fatal(err)
    84  			}
    85  
    86  			var w bytes.Buffer
    87  			_ = w
    88  			enc := NewJSONEncoder(&w)
    89  
    90  			if err := enc.Encode(test.data); err != nil {
    91  				t.Fatal(err)
    92  			}
    93  
    94  			expected, actual := string(expJSON), w.String()
    95  			assert.JSONEq(t, expected, actual)
    96  		})
    97  	}
    98  
    99  	t.Run("ConfigSpec", func(t *testing.T) {
   100  		t.Run("Encode", func(t *testing.T) {
   101  
   102  			var testCases = []struct {
   103  				name        string
   104  				data        any
   105  				expected    string
   106  				expectPanic bool
   107  			}{
   108  				{
   109  					name:     "nil ConfigSpec",
   110  					data:     (*VirtualMachineConfigSpec)(nil),
   111  					expected: "null",
   112  				},
   113  				{
   114  					name: "ConfigSpec with nil OptionValue value",
   115  					data: &VirtualMachineConfigSpec{
   116  						ExtraConfig: []BaseOptionValue{
   117  							&OptionValue{
   118  								Key:   "key1",
   119  								Value: nil,
   120  							},
   121  						},
   122  					},
   123  					expected: `{"_typeName":"VirtualMachineConfigSpec","extraConfig":[{"_typeName":"OptionValue","key":"key1","value":null}]}`,
   124  				},
   125  				{
   126  					name: "ConfigSpec with nil OptionValue interface value",
   127  					data: &VirtualMachineConfigSpec{
   128  						ExtraConfig: []BaseOptionValue{
   129  							&OptionValue{
   130  								Key:   "key1",
   131  								Value: (any)(nil),
   132  							},
   133  						},
   134  					},
   135  					expected: `{"_typeName":"VirtualMachineConfigSpec","extraConfig":[{"_typeName":"OptionValue","key":"key1","value":null}]}`,
   136  				},
   137  				{
   138  					name: "ConfigSpec with nil element in OptionValues",
   139  					data: &VirtualMachineConfigSpec{
   140  						ExtraConfig: []BaseOptionValue{
   141  							(*OptionValue)(nil),
   142  						},
   143  					},
   144  					expected: `{"_typeName":"VirtualMachineConfigSpec","extraConfig":[null]}`,
   145  				},
   146  				{
   147  					name: "ConfigSpec with nil ToolsConfigInfo",
   148  					data: &VirtualMachineConfigSpec{
   149  						Tools: (*ToolsConfigInfo)(nil),
   150  					},
   151  					expected: `{"_typeName":"VirtualMachineConfigSpec"}`,
   152  				},
   153  				{
   154  					name: "ConfigSpec with nil vAppConfig",
   155  					data: &VirtualMachineConfigSpec{
   156  						VAppConfig: nil,
   157  					},
   158  					expected: `{"_typeName":"VirtualMachineConfigSpec"}`,
   159  				},
   160  				{
   161  					name: "ConfigSpec with nil pointer vAppConfig ",
   162  					data: &VirtualMachineConfigSpec{
   163  						VAppConfig: (*VmConfigSpec)(nil),
   164  					},
   165  					expected: `{"_typeName":"VirtualMachineConfigSpec","vAppConfig":null}`,
   166  				},
   167  			}
   168  
   169  			for i := range testCases {
   170  				tc := testCases[i]
   171  				t.Run(tc.name, func(t *testing.T) {
   172  					var w bytes.Buffer
   173  					enc := NewJSONEncoder(&w)
   174  
   175  					var panicErr any
   176  
   177  					func() {
   178  						defer func() {
   179  							panicErr = recover()
   180  						}()
   181  						if err := enc.Encode(tc.data); err != nil {
   182  							t.Fatal(err)
   183  						}
   184  					}()
   185  
   186  					if tc.expectPanic && panicErr == nil {
   187  						t.Fatalf("did not panic, w=%v", w.String())
   188  					} else if tc.expectPanic && panicErr != nil {
   189  						t.Logf("expected panic occurred: %v\n", panicErr)
   190  					} else if !tc.expectPanic && panicErr != nil {
   191  						t.Fatalf("unexpected panic occurred: %v\n", panicErr)
   192  					} else if a, e := w.String(), tc.expected+"\n"; a != e {
   193  						t.Fatalf("act=%s != exp=%s", a, e)
   194  					} else {
   195  						t.Log(a)
   196  					}
   197  				})
   198  			}
   199  		})
   200  	})
   201  }
   202  
   203  func TestOptionValueSerialization(t *testing.T) {
   204  	tv, e := time.Parse(time.RFC3339Nano, "2022-12-12T11:48:35.473645Z")
   205  	if e != nil {
   206  		t.Log("Cannot parse test timestamp. This is coding error.")
   207  		t.Fail()
   208  		return
   209  	}
   210  	options := []struct {
   211  		name    string
   212  		wire    string
   213  		binding OptionValue
   214  	}{
   215  		{
   216  			name: "boolean",
   217  			wire: `{"_typeName": "OptionValue","key": "option1",
   218  				"value": {"_typeName": "boolean","_value": true}
   219  			}`,
   220  			binding: OptionValue{Key: "option1", Value: true},
   221  		},
   222  		{
   223  			name: "byte",
   224  			wire: `{"_typeName": "OptionValue","key": "option1",
   225  				"value": {"_typeName": "byte","_value": 16}
   226  			}`,
   227  			binding: OptionValue{Key: "option1", Value: uint8(16)},
   228  		},
   229  		{
   230  			name: "short",
   231  			wire: `{"_typeName": "OptionValue","key": "option1",
   232  				"value": {"_typeName": "short","_value": 300}
   233  			}`,
   234  			binding: OptionValue{Key: "option1", Value: int16(300)},
   235  		},
   236  		{
   237  			name: "int",
   238  			wire: `{"_typeName": "OptionValue","key": "option1",
   239  				"value": {"_typeName": "int","_value": 300}}`,
   240  			binding: OptionValue{Key: "option1", Value: int32(300)},
   241  		},
   242  		{
   243  			name: "long",
   244  			wire: `{"_typeName": "OptionValue","key": "option1",
   245  				"value": {"_typeName": "long","_value": 300}}`,
   246  			binding: OptionValue{Key: "option1", Value: int64(300)},
   247  		},
   248  		{
   249  			name: "float",
   250  			wire: `{"_typeName": "OptionValue","key": "option1",
   251  				"value": {"_typeName": "float","_value": 30.5}}`,
   252  			binding: OptionValue{Key: "option1", Value: float32(30.5)},
   253  		},
   254  		{
   255  			name: "double",
   256  			wire: `{"_typeName": "OptionValue","key": "option1",
   257  				"value": {"_typeName": "double","_value": 12.2}}`,
   258  			binding: OptionValue{Key: "option1", Value: float64(12.2)},
   259  		},
   260  		{
   261  			name: "string",
   262  			wire: `{"_typeName": "OptionValue","key": "option1",
   263  				"value": {"_typeName": "string","_value": "test"}}`,
   264  			binding: OptionValue{Key: "option1", Value: "test"},
   265  		},
   266  		{
   267  			name: "dateTime", // time.Time
   268  			wire: `{"_typeName": "OptionValue","key": "option1",
   269  				"value": {"_typeName": "dateTime","_value": "2022-12-12T11:48:35.473645Z"}}`,
   270  			binding: OptionValue{Key: "option1", Value: tv},
   271  		},
   272  		{
   273  			name: "binary", // []byte
   274  			wire: `{"_typeName": "OptionValue","key": "option1",
   275  				"value": {"_typeName": "binary","_value": "SGVsbG8="}}`,
   276  			binding: OptionValue{Key: "option1", Value: []byte("Hello")},
   277  		},
   278  		// during serialization we have no way to guess that a string is to be
   279  		// converted to uri. Using net.URL solves this. It is a breaking change.
   280  		// See https://github.com/vmware/govmomi/pull/3123
   281  		// {
   282  		// 	name: "anyURI", // string
   283  		// 	wire: `{"_typeName": "OptionValue","key": "option1",
   284  		// 		"value": {"_typeName": "anyURI","_value": "http://hello"}}`,
   285  		// 	binding: OptionValue{Key: "option1", Value: "test"},
   286  		// },
   287  		{
   288  			name: "enum",
   289  			wire: `{"_typeName": "OptionValue","key": "option1",
   290  				"value": {"_typeName": "CustomizationNetBIOSMode","_value": "enableNetBIOS"}}`,
   291  			binding: OptionValue{Key: "option1", Value: CustomizationNetBIOSModeEnableNetBIOS},
   292  		},
   293  		// There is no ArrayOfCustomizationNetBIOSMode type emitted i.e. no enum
   294  		// array types are emitted in govmomi.
   295  		// We can process these in the serialization logic i.e. discover or
   296  		// prepend the "ArrayOf" prefix
   297  		// {
   298  		// 	name: "array of enum",
   299  		// 	wire: `{
   300  		//		"_typeName": "OptionValue",
   301  		//		"key": "option1",
   302  		// 		"value": {"_typeName": "ArrayOfCustomizationNetBIOSMode",
   303  		//                "_value": ["enableNetBIOS"]}}`,
   304  		// 	binding: OptionValue{Key: "option1",
   305  		//		Value: []CustomizationNetBIOSMode{
   306  		//			CustomizationNetBIOSModeEnableNetBIOS
   307  		//		}},
   308  		// },
   309  
   310  		// array of struct is weird. Do we want to unmarshal this as
   311  		// []ClusterHostRecommendation directly? Why do we want to use
   312  		// ArrayOfClusterHostRecommendation wrapper?
   313  		// if SOAP does it then I guess back compat is a big reason.
   314  		{
   315  			name: "array of struct",
   316  			wire: `{"_typeName": "OptionValue","key": "option1",
   317  				"value": {"_typeName": "ArrayOfClusterHostRecommendation","_value": [
   318  					{
   319  						"_typeName":"ClusterHostRecommendation",
   320  						"host": {
   321  							"_typeName": "ManagedObjectReference",
   322  							"type": "HostSystem",
   323  							"value": "host-42"
   324  						},
   325  						"rating":42
   326  					}]}}`,
   327  			binding: OptionValue{
   328  				Key: "option1",
   329  				Value: ArrayOfClusterHostRecommendation{
   330  					ClusterHostRecommendation: []ClusterHostRecommendation{
   331  						{
   332  							Host: ManagedObjectReference{
   333  								Type:  "HostSystem",
   334  								Value: "host-42",
   335  							},
   336  							Rating: 42,
   337  						},
   338  					},
   339  				},
   340  			},
   341  		},
   342  	}
   343  
   344  	for _, opt := range options {
   345  		t.Run("Serialize "+opt.name, func(t *testing.T) {
   346  			r := strings.NewReader(opt.wire)
   347  			dec := NewJSONDecoder(r)
   348  			v := OptionValue{}
   349  			e := dec.Decode(&v)
   350  			if e != nil {
   351  				assert.Fail(t, "Cannot read json", "json %v err %v", opt.wire, e)
   352  				return
   353  			}
   354  			assert.Equal(t, opt.binding, v)
   355  		})
   356  
   357  		t.Run("De-serialize "+opt.name, func(t *testing.T) {
   358  			var w bytes.Buffer
   359  			enc := NewJSONEncoder(&w)
   360  			enc.Encode(opt.binding)
   361  			s := w.String()
   362  			assert.JSONEq(t, opt.wire, s)
   363  		})
   364  	}
   365  }
   366  
   367  var vmInfoObjForTests = VirtualMachineConfigInfo{
   368  	ChangeVersion:         "2022-12-12T11:48:35.473645Z",
   369  	Modified:              mustParseTime(time.RFC3339, "1970-01-01T00:00:00Z"),
   370  	Name:                  "test",
   371  	GuestFullName:         "VMware Photon OS (64-bit)",
   372  	Version:               "vmx-20",
   373  	Uuid:                  "422ca90b-853b-1101-3350-759f747730cc",
   374  	CreateDate:            addrOfMustParseTime(time.RFC3339, "2022-12-12T11:47:24.685785Z"),
   375  	InstanceUuid:          "502cc2a5-1f06-2890-6d70-ba2c55c5c2b7",
   376  	NpivTemporaryDisabled: NewBool(true),
   377  	LocationId:            "Earth",
   378  	Template:              false,
   379  	GuestId:               "vmwarePhoton64Guest",
   380  	AlternateGuestName:    "",
   381  	Annotation:            "Hello, world.",
   382  	Files: VirtualMachineFileInfo{
   383  		VmPathName:        "[datastore1] test/test.vmx",
   384  		SnapshotDirectory: "[datastore1] test/",
   385  		SuspendDirectory:  "[datastore1] test/",
   386  		LogDirectory:      "[datastore1] test/",
   387  	},
   388  	Tools: &ToolsConfigInfo{
   389  		ToolsVersion:            1,
   390  		AfterPowerOn:            NewBool(true),
   391  		AfterResume:             NewBool(true),
   392  		BeforeGuestStandby:      NewBool(true),
   393  		BeforeGuestShutdown:     NewBool(true),
   394  		BeforeGuestReboot:       nil,
   395  		ToolsUpgradePolicy:      "manual",
   396  		SyncTimeWithHostAllowed: NewBool(true),
   397  		SyncTimeWithHost:        NewBool(false),
   398  		LastInstallInfo: &ToolsConfigInfoToolsLastInstallInfo{
   399  			Counter: 0,
   400  		},
   401  	},
   402  	Flags: VirtualMachineFlagInfo{
   403  		EnableLogging:            NewBool(true),
   404  		UseToe:                   NewBool(false),
   405  		RunWithDebugInfo:         NewBool(false),
   406  		MonitorType:              "release",
   407  		HtSharing:                "any",
   408  		SnapshotDisabled:         NewBool(false),
   409  		SnapshotLocked:           NewBool(false),
   410  		DiskUuidEnabled:          NewBool(false),
   411  		SnapshotPowerOffBehavior: "powerOff",
   412  		RecordReplayEnabled:      NewBool(false),
   413  		FaultToleranceType:       "unset",
   414  		CbrcCacheEnabled:         NewBool(false),
   415  		VvtdEnabled:              NewBool(false),
   416  		VbsEnabled:               NewBool(false),
   417  	},
   418  	DefaultPowerOps: VirtualMachineDefaultPowerOpInfo{
   419  		PowerOffType:        "soft",
   420  		SuspendType:         "hard",
   421  		ResetType:           "soft",
   422  		DefaultPowerOffType: "soft",
   423  		DefaultSuspendType:  "hard",
   424  		DefaultResetType:    "soft",
   425  		StandbyAction:       "checkpoint",
   426  	},
   427  	RebootPowerOff: NewBool(false),
   428  	Hardware: VirtualHardware{
   429  		NumCPU:              1,
   430  		NumCoresPerSocket:   1,
   431  		AutoCoresPerSocket:  NewBool(true),
   432  		MemoryMB:            2048,
   433  		VirtualICH7MPresent: NewBool(false),
   434  		VirtualSMCPresent:   NewBool(false),
   435  		Device: []BaseVirtualDevice{
   436  			&VirtualIDEController{
   437  				VirtualController: VirtualController{
   438  					VirtualDevice: VirtualDevice{
   439  						Key: 200,
   440  						DeviceInfo: &Description{
   441  							Label:   "IDE 0",
   442  							Summary: "IDE 0",
   443  						},
   444  					},
   445  					BusNumber: 0,
   446  				},
   447  			},
   448  			&VirtualIDEController{
   449  				VirtualController: VirtualController{
   450  					VirtualDevice: VirtualDevice{
   451  						Key: 201,
   452  						DeviceInfo: &Description{
   453  							Label:   "IDE 1",
   454  							Summary: "IDE 1",
   455  						},
   456  					},
   457  					BusNumber: 1,
   458  				},
   459  			},
   460  			&VirtualPS2Controller{
   461  				VirtualController: VirtualController{
   462  					VirtualDevice: VirtualDevice{
   463  						Key: 300,
   464  						DeviceInfo: &Description{
   465  							Label:   "PS2 controller 0",
   466  							Summary: "PS2 controller 0",
   467  						},
   468  					},
   469  					BusNumber: 0,
   470  					Device:    []int32{600, 700},
   471  				},
   472  			},
   473  			&VirtualPCIController{
   474  				VirtualController: VirtualController{
   475  					VirtualDevice: VirtualDevice{
   476  						Key: 100,
   477  						DeviceInfo: &Description{
   478  							Label:   "PCI controller 0",
   479  							Summary: "PCI controller 0",
   480  						},
   481  					},
   482  					BusNumber: 0,
   483  					Device:    []int32{500, 12000, 14000, 1000, 15000, 4000},
   484  				},
   485  			},
   486  			&VirtualSIOController{
   487  				VirtualController: VirtualController{
   488  					VirtualDevice: VirtualDevice{
   489  						Key: 400,
   490  						DeviceInfo: &Description{
   491  							Label:   "SIO controller 0",
   492  							Summary: "SIO controller 0",
   493  						},
   494  					},
   495  					BusNumber: 0,
   496  				},
   497  			},
   498  			&VirtualKeyboard{
   499  				VirtualDevice: VirtualDevice{
   500  					Key: 600,
   501  					DeviceInfo: &Description{
   502  						Label:   "Keyboard",
   503  						Summary: "Keyboard",
   504  					},
   505  					ControllerKey: 300,
   506  					UnitNumber:    NewInt32(0),
   507  				},
   508  			},
   509  			&VirtualPointingDevice{
   510  				VirtualDevice: VirtualDevice{
   511  					Key:        700,
   512  					DeviceInfo: &Description{Label: "Pointing device", Summary: "Pointing device; Device"},
   513  					Backing: &VirtualPointingDeviceDeviceBackingInfo{
   514  						VirtualDeviceDeviceBackingInfo: VirtualDeviceDeviceBackingInfo{
   515  							UseAutoDetect: NewBool(false),
   516  						},
   517  						HostPointingDevice: "autodetect",
   518  					},
   519  					ControllerKey: 300,
   520  					UnitNumber:    NewInt32(1),
   521  				},
   522  			},
   523  			&VirtualMachineVideoCard{
   524  				VirtualDevice: VirtualDevice{
   525  					Key:           500,
   526  					DeviceInfo:    &Description{Label: "Video card ", Summary: "Video card"},
   527  					ControllerKey: 100,
   528  					UnitNumber:    NewInt32(0),
   529  				},
   530  				VideoRamSizeInKB:       4096,
   531  				NumDisplays:            1,
   532  				UseAutoDetect:          NewBool(false),
   533  				Enable3DSupport:        NewBool(false),
   534  				Use3dRenderer:          "automatic",
   535  				GraphicsMemorySizeInKB: 262144,
   536  			},
   537  			&VirtualMachineVMCIDevice{
   538  				VirtualDevice: VirtualDevice{
   539  					Key: 12000,
   540  					DeviceInfo: &Description{
   541  						Label: "VMCI device",
   542  						Summary: "Device on the virtual machine PCI " +
   543  							"bus that provides support for the " +
   544  							"virtual machine communication interface",
   545  					},
   546  					ControllerKey: 100,
   547  					UnitNumber:    NewInt32(17),
   548  				},
   549  				Id:                             -1,
   550  				AllowUnrestrictedCommunication: NewBool(false),
   551  				FilterEnable:                   NewBool(true),
   552  			},
   553  			&ParaVirtualSCSIController{
   554  				VirtualSCSIController: VirtualSCSIController{
   555  					VirtualController: VirtualController{
   556  						VirtualDevice: VirtualDevice{
   557  							Key: 1000,
   558  							DeviceInfo: &Description{
   559  								Label:   "SCSI controller 0",
   560  								Summary: "VMware paravirtual SCSI",
   561  							},
   562  							ControllerKey: 100,
   563  							UnitNumber:    NewInt32(3),
   564  						},
   565  						Device: []int32{2000},
   566  					},
   567  					HotAddRemove:       NewBool(true),
   568  					SharedBus:          "noSharing",
   569  					ScsiCtlrUnitNumber: 7,
   570  				},
   571  			},
   572  			&VirtualAHCIController{
   573  				VirtualSATAController: VirtualSATAController{
   574  					VirtualController: VirtualController{
   575  						VirtualDevice: VirtualDevice{
   576  							Key: 15000,
   577  							DeviceInfo: &Description{
   578  								Label:   "SATA controller 0",
   579  								Summary: "AHCI",
   580  							},
   581  							ControllerKey: 100,
   582  							UnitNumber:    NewInt32(24),
   583  						},
   584  						Device: []int32{16000},
   585  					},
   586  				},
   587  			},
   588  			&VirtualCdrom{
   589  				VirtualDevice: VirtualDevice{
   590  					Key: 16000,
   591  					DeviceInfo: &Description{
   592  						Label:   "CD/DVD drive 1",
   593  						Summary: "Remote device",
   594  					},
   595  					Backing: &VirtualCdromRemotePassthroughBackingInfo{
   596  						VirtualDeviceRemoteDeviceBackingInfo: VirtualDeviceRemoteDeviceBackingInfo{
   597  							UseAutoDetect: NewBool(false),
   598  						},
   599  					},
   600  					Connectable:   &VirtualDeviceConnectInfo{AllowGuestControl: true, Status: "untried"},
   601  					ControllerKey: 15000,
   602  					UnitNumber:    NewInt32(0),
   603  				},
   604  			},
   605  			&VirtualDisk{
   606  				VirtualDevice: VirtualDevice{
   607  					Key: 2000,
   608  					DeviceInfo: &Description{
   609  						Label:   "Hard disk 1",
   610  						Summary: "4,194,304 KB",
   611  					},
   612  					Backing: &VirtualDiskFlatVer2BackingInfo{
   613  						VirtualDeviceFileBackingInfo: VirtualDeviceFileBackingInfo{
   614  							BackingObjectId: "1",
   615  							FileName:        "[datastore1] test/test.vmdk",
   616  							Datastore: &ManagedObjectReference{
   617  								Type:  "Datastore",
   618  								Value: "datastore-21",
   619  							},
   620  						},
   621  						DiskMode:               "persistent",
   622  						Split:                  NewBool(false),
   623  						WriteThrough:           NewBool(false),
   624  						ThinProvisioned:        NewBool(false),
   625  						EagerlyScrub:           NewBool(false),
   626  						Uuid:                   "6000C298-df15-fe89-ddcb-8ea33329595d",
   627  						ContentId:              "e4e1a794c6307ce7906a3973fffffffe",
   628  						ChangeId:               "",
   629  						Parent:                 nil,
   630  						DeltaDiskFormat:        "",
   631  						DigestEnabled:          NewBool(false),
   632  						DeltaGrainSize:         0,
   633  						DeltaDiskFormatVariant: "",
   634  						Sharing:                "sharingNone",
   635  						KeyId:                  nil,
   636  					},
   637  					ControllerKey: 1000,
   638  					UnitNumber:    NewInt32(0),
   639  				},
   640  				CapacityInKB:    4194304,
   641  				CapacityInBytes: 4294967296,
   642  				Shares:          &SharesInfo{Shares: 1000, Level: "normal"},
   643  				StorageIOAllocation: &StorageIOAllocationInfo{
   644  					Limit:       NewInt64(-1),
   645  					Shares:      &SharesInfo{Shares: 1000, Level: "normal"},
   646  					Reservation: NewInt32(0),
   647  				},
   648  				DiskObjectId:               "1-2000",
   649  				NativeUnmanagedLinkedClone: NewBool(false),
   650  			},
   651  			&VirtualVmxnet3{
   652  				VirtualVmxnet: VirtualVmxnet{
   653  					VirtualEthernetCard: VirtualEthernetCard{
   654  						VirtualDevice: VirtualDevice{
   655  							Key: 4000,
   656  							DeviceInfo: &Description{
   657  								Label:   "Network adapter 1",
   658  								Summary: "VM Network",
   659  							},
   660  							Backing: &VirtualEthernetCardNetworkBackingInfo{
   661  								VirtualDeviceDeviceBackingInfo: VirtualDeviceDeviceBackingInfo{
   662  									DeviceName:    "VM Network",
   663  									UseAutoDetect: NewBool(false),
   664  								},
   665  								Network: &ManagedObjectReference{
   666  									Type:  "Network",
   667  									Value: "network-27",
   668  								},
   669  							},
   670  							Connectable: &VirtualDeviceConnectInfo{
   671  								MigrateConnect: "unset",
   672  								StartConnected: true,
   673  								Status:         "untried",
   674  							},
   675  							ControllerKey: 100,
   676  							UnitNumber:    NewInt32(7),
   677  						},
   678  						AddressType:      "assigned",
   679  						MacAddress:       "00:50:56:ac:4d:ed",
   680  						WakeOnLanEnabled: NewBool(true),
   681  						ResourceAllocation: &VirtualEthernetCardResourceAllocation{
   682  							Reservation: NewInt64(0),
   683  							Share: SharesInfo{
   684  								Shares: 50,
   685  								Level:  "normal",
   686  							},
   687  							Limit: NewInt64(-1),
   688  						},
   689  						UptCompatibilityEnabled: NewBool(true),
   690  					},
   691  				},
   692  				Uptv2Enabled: NewBool(false),
   693  			},
   694  			&VirtualUSBXHCIController{
   695  				VirtualController: VirtualController{
   696  					VirtualDevice: VirtualDevice{
   697  						Key: 14000,
   698  						DeviceInfo: &Description{
   699  							Label:   "USB xHCI controller ",
   700  							Summary: "USB xHCI controller",
   701  						},
   702  						SlotInfo: &VirtualDevicePciBusSlotInfo{
   703  							PciSlotNumber: -1,
   704  						},
   705  						ControllerKey: 100,
   706  						UnitNumber:    NewInt32(23),
   707  					},
   708  				},
   709  
   710  				AutoConnectDevices: NewBool(false),
   711  			},
   712  		},
   713  		MotherboardLayout:   "i440bxHostBridge",
   714  		SimultaneousThreads: 1,
   715  	},
   716  	CpuAllocation: &ResourceAllocationInfo{
   717  		Reservation:           NewInt64(0),
   718  		ExpandableReservation: NewBool(false),
   719  		Limit:                 NewInt64(-1),
   720  		Shares: &SharesInfo{
   721  			Shares: 1000,
   722  			Level:  SharesLevelNormal,
   723  		},
   724  	},
   725  	MemoryAllocation: &ResourceAllocationInfo{
   726  		Reservation:           NewInt64(0),
   727  		ExpandableReservation: NewBool(false),
   728  		Limit:                 NewInt64(-1),
   729  		Shares: &SharesInfo{
   730  			Shares: 20480,
   731  			Level:  SharesLevelNormal,
   732  		},
   733  	},
   734  	LatencySensitivity: &LatencySensitivity{
   735  		Level: LatencySensitivitySensitivityLevelNormal,
   736  	},
   737  	MemoryHotAddEnabled: NewBool(false),
   738  	CpuHotAddEnabled:    NewBool(false),
   739  	CpuHotRemoveEnabled: NewBool(false),
   740  	ExtraConfig: []BaseOptionValue{
   741  		&OptionValue{Key: "nvram", Value: "test.nvram"},
   742  		&OptionValue{Key: "svga.present", Value: "TRUE"},
   743  		&OptionValue{Key: "pciBridge0.present", Value: "TRUE"},
   744  		&OptionValue{Key: "pciBridge4.present", Value: "TRUE"},
   745  		&OptionValue{Key: "pciBridge4.virtualDev", Value: "pcieRootPort"},
   746  		&OptionValue{Key: "pciBridge4.functions", Value: "8"},
   747  		&OptionValue{Key: "pciBridge5.present", Value: "TRUE"},
   748  		&OptionValue{Key: "pciBridge5.virtualDev", Value: "pcieRootPort"},
   749  		&OptionValue{Key: "pciBridge5.functions", Value: "8"},
   750  		&OptionValue{Key: "pciBridge6.present", Value: "TRUE"},
   751  		&OptionValue{Key: "pciBridge6.virtualDev", Value: "pcieRootPort"},
   752  		&OptionValue{Key: "pciBridge6.functions", Value: "8"},
   753  		&OptionValue{Key: "pciBridge7.present", Value: "TRUE"},
   754  		&OptionValue{Key: "pciBridge7.virtualDev", Value: "pcieRootPort"},
   755  		&OptionValue{Key: "pciBridge7.functions", Value: "8"},
   756  		&OptionValue{Key: "hpet0.present", Value: "TRUE"},
   757  		&OptionValue{Key: "RemoteDisplay.maxConnections", Value: "-1"},
   758  		&OptionValue{Key: "sched.cpu.latencySensitivity", Value: "normal"},
   759  		&OptionValue{Key: "vmware.tools.internalversion", Value: "0"},
   760  		&OptionValue{Key: "vmware.tools.requiredversion", Value: "12352"},
   761  		&OptionValue{Key: "migrate.hostLogState", Value: "none"},
   762  		&OptionValue{Key: "migrate.migrationId", Value: "0"},
   763  		&OptionValue{Key: "migrate.hostLog", Value: "test-36f94569.hlog"},
   764  		&OptionValue{
   765  			Key:   "viv.moid",
   766  			Value: "c5b34aa9-d962-4a74-b7d2-b83ec683ba1b:vm-28:lIgQ2t7v24n2nl3N7K3m6IHW2OoPF4CFrJd5N+Tdfio=",
   767  		},
   768  	},
   769  	DatastoreUrl: []VirtualMachineConfigInfoDatastoreUrlPair{
   770  		{
   771  			Name: "datastore1",
   772  			Url:  "/vmfs/volumes/63970ed8-4abddd2a-62d7-02003f49c37d",
   773  		},
   774  	},
   775  	SwapPlacement: "inherit",
   776  	BootOptions: &VirtualMachineBootOptions{
   777  		EnterBIOSSetup:       NewBool(false),
   778  		EfiSecureBootEnabled: NewBool(false),
   779  		BootDelay:            1,
   780  		BootRetryEnabled:     NewBool(false),
   781  		BootRetryDelay:       10000,
   782  		NetworkBootProtocol:  "ipv4",
   783  	},
   784  	FtInfo:                       nil,
   785  	RepConfig:                    nil,
   786  	VAppConfig:                   nil,
   787  	VAssertsEnabled:              NewBool(false),
   788  	ChangeTrackingEnabled:        NewBool(false),
   789  	Firmware:                     "bios",
   790  	MaxMksConnections:            -1,
   791  	GuestAutoLockEnabled:         NewBool(true),
   792  	ManagedBy:                    nil,
   793  	MemoryReservationLockedToMax: NewBool(false),
   794  	InitialOverhead: &VirtualMachineConfigInfoOverheadInfo{
   795  		InitialMemoryReservation: 214446080,
   796  		InitialSwapReservation:   2541883392,
   797  	},
   798  	NestedHVEnabled: NewBool(false),
   799  	VPMCEnabled:     NewBool(false),
   800  	ScheduledHardwareUpgradeInfo: &ScheduledHardwareUpgradeInfo{
   801  		UpgradePolicy:                  "never",
   802  		ScheduledHardwareUpgradeStatus: "none",
   803  	},
   804  	ForkConfigInfo:         nil,
   805  	VFlashCacheReservation: 0,
   806  	VmxConfigChecksum: []uint8{
   807  		0x69, 0xf7, 0xa7, 0x9e,
   808  		0xd1, 0xc2, 0x21, 0x4b,
   809  		0x6c, 0x20, 0x77, 0x0a,
   810  		0x94, 0x94, 0x99, 0xee,
   811  		0x17, 0x5d, 0xdd, 0xa3,
   812  	},
   813  	MessageBusTunnelEnabled: NewBool(false),
   814  	GuestIntegrityInfo: &VirtualMachineGuestIntegrityInfo{
   815  		Enabled: NewBool(false),
   816  	},
   817  	MigrateEncryption: "opportunistic",
   818  	SgxInfo: &VirtualMachineSgxInfo{
   819  		FlcMode:            "unlocked",
   820  		RequireAttestation: NewBool(false),
   821  	},
   822  	ContentLibItemInfo:      nil,
   823  	FtEncryptionMode:        "ftEncryptionOpportunistic",
   824  	GuestMonitoringModeInfo: &VirtualMachineGuestMonitoringModeInfo{},
   825  	SevEnabled:              NewBool(false),
   826  	NumaInfo: &VirtualMachineVirtualNumaInfo{
   827  		AutoCoresPerNumaNode:    NewBool(true),
   828  		VnumaOnCpuHotaddExposed: NewBool(false),
   829  	},
   830  	PmemFailoverEnabled:          NewBool(false),
   831  	VmxStatsCollectionEnabled:    NewBool(true),
   832  	VmOpNotificationToAppEnabled: NewBool(false),
   833  	VmOpNotificationTimeout:      -1,
   834  	DeviceSwap: &VirtualMachineVirtualDeviceSwap{
   835  		LsiToPvscsi: &VirtualMachineVirtualDeviceSwapDeviceSwapInfo{
   836  			Enabled:    NewBool(true),
   837  			Applicable: NewBool(false),
   838  			Status:     "none",
   839  		},
   840  	},
   841  	Pmem:         nil,
   842  	DeviceGroups: &VirtualMachineVirtualDeviceGroups{},
   843  }
   844  
   845  var retrieveResultForTests = RetrieveResult{
   846  	Token: "",
   847  	Objects: []ObjectContent{
   848  
   849  		{
   850  
   851  			DynamicData: DynamicData{},
   852  			Obj: ManagedObjectReference{
   853  
   854  				Type:  "Folder",
   855  				Value: "group-d1",
   856  			},
   857  			PropSet: []DynamicProperty{
   858  				{
   859  
   860  					Name: "alarmActionsEnabled",
   861  					Val:  true,
   862  				},
   863  				{
   864  
   865  					Name: "availableField",
   866  					Val: ArrayOfCustomFieldDef{
   867  
   868  						CustomFieldDef: []CustomFieldDef{},
   869  					},
   870  				},
   871  
   872  				{
   873  
   874  					Name: "childEntity",
   875  					Val: ArrayOfManagedObjectReference{
   876  						ManagedObjectReference: []ManagedObjectReference{},
   877  					},
   878  				},
   879  				{
   880  					Name: "childType",
   881  					Val: ArrayOfString{
   882  						String: []string{
   883  							"Folder",
   884  							"Datacenter"},
   885  					},
   886  				},
   887  				{
   888  					Name: "configIssue",
   889  					Val: ArrayOfEvent{
   890  						Event: []BaseEvent{},
   891  					},
   892  				},
   893  				{
   894  					Name: "configStatus",
   895  					Val:  ManagedEntityStatusGray},
   896  				{
   897  					Name: "customValue",
   898  					Val: ArrayOfCustomFieldValue{
   899  						CustomFieldValue: []BaseCustomFieldValue{},
   900  					},
   901  				},
   902  				{
   903  					Name: "declaredAlarmState",
   904  					Val: ArrayOfAlarmState{
   905  						AlarmState: []AlarmState{
   906  							{
   907  								Key: "alarm-328.group-d1",
   908  								Entity: ManagedObjectReference{
   909  									Type:  "Folder",
   910  									Value: "group-d1"},
   911  								Alarm: ManagedObjectReference{
   912  									Type:  "Alarm",
   913  									Value: "alarm-328"},
   914  								OverallStatus: "gray",
   915  								Time:          time.Date(2023, time.January, 14, 8, 57, 35, 279575000, time.UTC),
   916  								Acknowledged:  NewBool(false),
   917  							},
   918  							{
   919  								Key: "alarm-327.group-d1",
   920  								Entity: ManagedObjectReference{
   921  									Type:  "Folder",
   922  									Value: "group-d1"},
   923  								Alarm: ManagedObjectReference{
   924  									Type:  "Alarm",
   925  									Value: "alarm-327"},
   926  								OverallStatus: "green",
   927  								Time:          time.Date(2023, time.January, 14, 8, 56, 40, 83607000, time.UTC),
   928  								Acknowledged:  NewBool(false),
   929  								EventKey:      756,
   930  							},
   931  							{
   932  								DynamicData: DynamicData{},
   933  								Key:         "alarm-326.group-d1",
   934  								Entity: ManagedObjectReference{
   935  									Type:  "Folder",
   936  									Value: "group-d1"},
   937  								Alarm: ManagedObjectReference{
   938  									Type:  "Alarm",
   939  									Value: "alarm-326"},
   940  								OverallStatus: "green",
   941  								Time: time.Date(2023,
   942  									time.January,
   943  									14,
   944  									8,
   945  									56,
   946  									35,
   947  									82616000,
   948  									time.UTC),
   949  								Acknowledged: NewBool(false),
   950  								EventKey:     751,
   951  							},
   952  						},
   953  					},
   954  				},
   955  				{
   956  					Name: "disabledMethod",
   957  					Val: ArrayOfString{
   958  						String: []string{},
   959  					},
   960  				},
   961  				{
   962  					Name: "effectiveRole",
   963  					Val: ArrayOfInt{
   964  						Int: []int32{-1},
   965  					},
   966  				},
   967  				{
   968  					Name: "name",
   969  					Val:  "Datacenters"},
   970  				{
   971  					Name: "overallStatus",
   972  					Val:  ManagedEntityStatusGray},
   973  				{
   974  					Name: "permission",
   975  					Val: ArrayOfPermission{
   976  						Permission: []Permission{
   977  							{
   978  								Entity: &ManagedObjectReference{
   979  									Value: "group-d1",
   980  									Type:  "Folder",
   981  								},
   982  								Principal: "VSPHERE.LOCAL\\vmware-vsm-2bd917c6-e084-4d1f-988d-a68f7525cc94",
   983  								Group:     false,
   984  								RoleId:    1034,
   985  								Propagate: true},
   986  							{
   987  								Entity: &ManagedObjectReference{
   988  									Value: "group-d1",
   989  									Type:  "Folder",
   990  								},
   991  								Principal: "VSPHERE.LOCAL\\topologysvc-2bd917c6-e084-4d1f-988d-a68f7525cc94",
   992  								Group:     false,
   993  								RoleId:    1024,
   994  								Propagate: true},
   995  							{
   996  								Entity: &ManagedObjectReference{
   997  									Value: "group-d1",
   998  									Type:  "Folder",
   999  								},
  1000  								Principal: "VSPHERE.LOCAL\\vpxd-extension-2bd917c6-e084-4d1f-988d-a68f7525cc94",
  1001  								Group:     false,
  1002  								RoleId:    -1,
  1003  								Propagate: true},
  1004  						},
  1005  					},
  1006  				},
  1007  				{
  1008  					Name: "recentTask",
  1009  					Val: ArrayOfManagedObjectReference{
  1010  						ManagedObjectReference: []ManagedObjectReference{
  1011  							{
  1012  								Type:  "Task",
  1013  								Value: "task-186"},
  1014  							{
  1015  								Type:  "Task",
  1016  								Value: "task-187"},
  1017  							{
  1018  								Type:  "Task",
  1019  								Value: "task-188"},
  1020  						},
  1021  					},
  1022  				},
  1023  				{
  1024  					Name: "tag",
  1025  					Val: ArrayOfTag{
  1026  						Tag: []Tag{},
  1027  					},
  1028  				},
  1029  				{
  1030  					Name: "triggeredAlarmState",
  1031  					Val: ArrayOfAlarmState{
  1032  						AlarmState: []AlarmState{},
  1033  					},
  1034  				},
  1035  				{
  1036  					Name: "value",
  1037  					Val: ArrayOfCustomFieldValue{
  1038  						CustomFieldValue: []BaseCustomFieldValue{},
  1039  					},
  1040  				},
  1041  			},
  1042  			MissingSet: nil,
  1043  		},
  1044  	},
  1045  }
  1046  
  1047  func mustParseTime(layout, value string) time.Time {
  1048  	t, err := time.Parse(layout, value)
  1049  	if err != nil {
  1050  		panic(err)
  1051  	}
  1052  	return t
  1053  }
  1054  
  1055  func addrOfMustParseTime(layout, value string) *time.Time {
  1056  	t := mustParseTime(layout, value)
  1057  	return &t
  1058  }