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

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