summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/testcase
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/testcase')
-rw-r--r--src/VBox/Main/testcase/Makefile.kmk351
-rw-r--r--src/VBox/Main/testcase/VBoxVBTest/TestForm.frm137
-rw-r--r--src/VBox/Main/testcase/VBoxVBTest/TestForm.frxbin0 -> 155 bytes
-rw-r--r--src/VBox/Main/testcase/VBoxVBTest/VBoxVBTest.vbp34
-rw-r--r--src/VBox/Main/testcase/makefile.tstVBoxAPIWin100
-rw-r--r--src/VBox/Main/testcase/makefile.tstVBoxAPIXPCOM65
-rw-r--r--src/VBox/Main/testcase/msiDarwinDescriptorDecoder.cpp81
-rw-r--r--src/VBox/Main/testcase/ovf-joomla-0.9/joomla-1.1.4-ovf.ovf83
-rw-r--r--src/VBox/Main/testcase/ovf-winhost-audio-nodisks/WinXP.ovf267
-rw-r--r--src/VBox/Main/testcase/ovf-winxp-vbox-sharedfolders/winxp.ovf315
-rw-r--r--src/VBox/Main/testcase/tstAPI.cpp1689
-rw-r--r--src/VBox/Main/testcase/tstBstr.cpp295
-rw-r--r--src/VBox/Main/testcase/tstCollector.cpp588
-rw-r--r--src/VBox/Main/testcase/tstGuestCtrlContextID.cpp125
-rw-r--r--src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp409
-rw-r--r--src/VBox/Main/testcase/tstGuestCtrlPaths.cpp167
-rw-r--r--src/VBox/Main/testcase/tstGuid.cpp110
-rw-r--r--src/VBox/Main/testcase/tstMediumLock.cpp309
-rw-r--r--src/VBox/Main/testcase/tstOVF.cpp435
-rw-r--r--src/VBox/Main/testcase/tstSafeArray.cpp106
-rw-r--r--src/VBox/Main/testcase/tstUSBLinux.h79
-rw-r--r--src/VBox/Main/testcase/tstUSBProxyLinux.cpp195
-rw-r--r--src/VBox/Main/testcase/tstUnattendedScript-1.expected384
-rw-r--r--src/VBox/Main/testcase/tstUnattendedScript-1.template384
-rw-r--r--src/VBox/Main/testcase/tstUnattendedScript.cpp731
-rw-r--r--src/VBox/Main/testcase/tstVBoxAPI.cpp417
-rw-r--r--src/VBox/Main/testcase/tstVBoxAPIPerf.cpp257
-rw-r--r--src/VBox/Main/testcase/tstVBoxAPIWin.cpp305
-rw-r--r--src/VBox/Main/testcase/tstVBoxAPIXPCOM.cpp684
-rw-r--r--src/VBox/Main/testcase/tstVBoxCrypto.cpp429
-rw-r--r--src/VBox/Main/testcase/tstVBoxMultipleVM.cpp617
31 files changed, 10148 insertions, 0 deletions
diff --git a/src/VBox/Main/testcase/Makefile.kmk b/src/VBox/Main/testcase/Makefile.kmk
new file mode 100644
index 00000000..49d8ae5a
--- /dev/null
+++ b/src/VBox/Main/testcase/Makefile.kmk
@@ -0,0 +1,351 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the VBox API testcases.
+#
+
+#
+# Copyright (C) 2004-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+#
+# Target and globals (small mess)
+#
+ifndef VBOX_ONLY_SDK
+ if defined(VBOX_WITH_TESTCASES)
+ PROGRAMS += \
+ tstAPI \
+ tstVBoxAPI \
+ tstVBoxAPIPerf \
+ tstVBoxMultipleVM \
+ $(if $(VBOX_OSE),,tstOVF) \
+ $(if $(VBOX_WITH_XPCOM),tstVBoxAPIXPCOM,tstVBoxAPIWin msiDarwinDescriptorDecoder) \
+ $(if $(VBOX_WITH_RESOURCE_USAGE_API),tstCollector,) \
+ $(if $(VBOX_WITH_GUEST_CONTROL),tstGuestCtrlContextID,) \
+ $(if $(VBOX_WITH_GUEST_CONTROL),tstGuestCtrlParseBuffer,) \
+ $(if $(VBOX_WITH_GUEST_CONTROL),tstGuestCtrlPaths,) \
+ tstMediumLock \
+ tstSafeArray \
+ tstBstr \
+ tstGuid \
+ tstUnattendedScript \
+ tstVBoxCrypto
+ PROGRAMS.linux += \
+ $(if $(VBOX_WITH_USB),tstUSBProxyLinux,)
+ endif # !VBOX_WITH_TESTCASES
+endif # !VBOX_ONLY_SDK
+if defined(VBOX_ONLY_SDK) || !defined(VBOX_WITH_XPCOM)
+ INSTALLS += samplesMSCOM
+endif
+if defined(VBOX_ONLY_SDK) || defined(VBOX_WITH_XPCOM)
+ INSTALLS += samplesXPCOM
+endif
+
+
+#
+# The samples
+#
+samplesMSCOM_MODE = a+r,u+w
+samplesMSCOM_INST = $(INST_SDK)bindings/mscom/samples/
+samplesMSCOM_SOURCES = tstVBoxAPIWin.cpp makefile.tstVBoxAPIWin=>Makefile
+
+samplesXPCOM_MODE = a+r,u+w
+samplesXPCOM_INST = $(INST_SDK)bindings/xpcom/samples/
+samplesXPCOM_SOURCES = tstVBoxAPIXPCOM.cpp makefile.tstVBoxAPIXPCOM=>Makefile
+
+#
+# tstVBoxMultipleVM
+#
+tstVBoxMultipleVM_TEMPLATE = VBoxMainClientTstExe
+tstVBoxMultipleVM_SOURCES = tstVBoxMultipleVM.cpp
+
+#
+# tstAPI
+#
+tstAPI_TEMPLATE = VBoxMainClientTstExe
+#tstAPI_INST = $(INST_SDK)bindings/gluecom/samples/
+tstAPI_SOURCES = tstAPI.cpp
+
+#
+# tstVBoxAPI
+#
+tstVBoxAPI_TEMPLATE = VBoxMainClientTstExe
+tstVBoxAPI_SOURCES = \
+ tstVBoxAPI.cpp
+
+#
+# tstVBoxAPIPerf
+#
+tstVBoxAPIPerf_TEMPLATE = VBoxMainClientTstExe
+tstVBoxAPIPerf_SOURCES = \
+ tstVBoxAPIPerf.cpp
+
+#
+# tstOVF
+#
+tstOVF_TEMPLATE = VBoxMainClientTstExe
+#tstOVF_INST = $(INST_SDK)bindings/gluecom/samples/
+tstOVF_SOURCES = tstOVF.cpp
+
+ifndef VBOX_OSE
+ #
+ # OVF test data.
+ #
+ INSTALLS += ovf-testcases
+ ovf-testcases_MODE = a+r,u+w
+ ovf-testcases_INST = $(INST_TESTCASE)ovf-testcases/
+ ovf-testcases_SOURCES = \
+ ovf-dummy.vmdk \
+ ovf-joomla-0.9/joomla-1.1.4-ovf.ovf=>ovf-joomla-0.9/joomla-1.1.4-ovf.ovf \
+ ovf-winhost-audio-nodisks/WinXP.ovf=>ovf-winhost-audio-nodisks/WinXP.ovf \
+ ovf-winxp-vbox-sharedfolders/winxp.ovf=>ovf-winxp-vbox-sharedfolders/winxp.ovf
+endif
+
+
+#
+# tstVBoxAPIXPCOM
+#
+# We only build the testcase here to make sure it builds.
+# It comes with a custom makefile which should be tested as well!
+#
+# Use very generic template to make the build environment similar
+# to the standalone case, to detect if IPRT or glue use sneaks in.
+#
+tstVBoxAPIXPCOM_TEMPLATE = VBoxR3Exe
+tstVBoxAPIXPCOM_INST = $(INST_TESTCASE)
+tstVBoxAPIXPCOM_SOURCES = tstVBoxAPIXPCOM.cpp
+tstVBoxAPIXPCOM_INCS = \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include/nsprpub \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include/string \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include/xpcom \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include/ipcd
+tstVBoxAPIXPCOM_LIBS = \
+ $(LIB_XPCOM) \
+ $(LIB_RUNTIME)
+tstVBoxAPIXPCOM_CXXFLAGS = -fshort-wchar
+ifdef VBOX_WITH_RUNPATH
+ tstVBoxAPIXPCOM_LDFLAGS = '$(VBOX_GCC_RPATH_OPT)$(VBOX_WITH_RUNPATH)' $(TEMPLATE_VBoxBldProg_LDFLAGS)
+else ifdef VBOX_WITH_RELATIVE_RUNPATH
+ tstVBoxAPIXPCOM_LDFLAGS = '$(VBOX_GCC_RPATH_OPT)$(VBOX_WITH_RELATIVE_RUNPATH)/..' $(TEMPLATE_VBoxBldProg_LDFLAGS)
+endif
+tstVBoxAPIXPCOM_INTERMEDIATES = \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include/VirtualBox_XPCOM.h
+ifdef VBOX_WITH_XPCOM_NAMESPACE_CLEANUP
+ tstVBoxAPIXPCOM_DEFS += VBOX_WITH_XPCOM_NAMESPACE_CLEANUP
+endif
+
+
+#
+# tstVBoxAPIWin
+#
+# Use very generic template to make the build environment similar
+# to the standalone case, to detect if IPRT or glue use sneaks in.
+#
+tstVBoxAPIWin_TEMPLATE = VBoxR3Exe
+tstVBoxAPIWin_INST = $(INST_TESTCASE)
+tstVBoxAPIWin_SOURCES = \
+ tstVBoxAPIWin.cpp \
+ $(VBOX_PATH_SDK)/bindings/mscom/lib/VirtualBox_i.c
+tstVBoxAPIWin_INCS = \
+ $(VBOX_PATH_SDK)/bindings/mscom/include
+tstVBoxAPIWin_INTERMEDIATES = \
+ $(VBOX_PATH_SDK)/bindings/mscom/include/VirtualBox.h
+
+
+#
+# msiDarwinDescriptorDecoder
+#
+# Use very generic template to make the build environment similar
+# to the standalone case, to detect if IPRT or glue use sneaks in.
+#
+msiDarwinDescriptorDecoder_TEMPLATE = VBoxR3Exe
+msiDarwinDescriptorDecoder_INST = $(VBOX_INST_TOOLS)
+msiDarwinDescriptorDecoder_SOURCES = \
+ msiDarwinDescriptorDecoder.cpp
+
+
+#
+# tstCollector
+#
+# Note! VBOX_MAIN_APIWRAPPER_GEN_HDRS is only defined if kmk is executed a
+# parent directory. Since the rules for generating the files listed by
+# the variable lives in the parent makefile, this is not a problem.
+#
+tstCollector_TEMPLATE = VBoxMainClientTstExe
+tstCollector_SOURCES = \
+ tstCollector.cpp \
+ ../src-server/Performance.cpp
+tstCollector_INCS = \
+ ../include \
+ $(VBOX_MAIN_APIWRAPPER_INCS)
+tstCollector_INTERMEDIATES = $(VBOX_MAIN_APIWRAPPER_GEN_HDRS)
+tstCollector_DEFS = VBOX_COLLECTOR_TEST_CASE
+tstCollector_LDFLAGS.darwin = -lproc
+tstCollector_LDFLAGS.solaris = -lkstat -lnvpair
+tstCollector_LDFLAGS.win = psapi.lib powrprof.lib
+
+
+#
+# tstGuestCtrlContextID
+#
+tstGuestCtrlContextID_TEMPLATE = VBoxMainClientTstExe
+tstGuestCtrlContextID_INTERMEDIATES = $(VBOX_MAIN_APIWRAPPER_GEN_HDRS)
+tstGuestCtrlContextID_DEFS += VBOX_WITH_HGCM VBOX_WITH_GUEST_CONTROL VBOX_GUESTCTRL_TEST_CASE
+tstGuestCtrlContextID_SOURCES = \
+ tstGuestCtrlContextID.cpp \
+ ../src-client/GuestCtrlPrivate.cpp
+tstGuestCtrlContextID_INCS = ../include \
+ $(VBOX_MAIN_APIWRAPPER_INCS)
+
+
+#
+# tstGuestCtrlParseBuffer
+#
+tstGuestCtrlParseBuffer_TEMPLATE = VBoxMainClientTstExe
+tstGuestCtrlParseBuffer_INTERMEDIATES = $(VBOX_MAIN_APIWRAPPER_GEN_HDRS)
+tstGuestCtrlParseBuffer_DEFS += VBOX_WITH_HGCM VBOX_WITH_GUEST_CONTROL VBOX_GUESTCTRL_TEST_CASE
+tstGuestCtrlParseBuffer_SOURCES = \
+ tstGuestCtrlParseBuffer.cpp \
+ ../src-client/GuestCtrlPrivate.cpp
+tstGuestCtrlParseBuffer_INCS = ../include \
+ $(VBOX_MAIN_APIWRAPPER_INCS)
+
+
+#
+# tstGuestCtrlPaths
+#
+tstGuestCtrlPaths_TEMPLATE = VBoxMainClientTstExe
+tstGuestCtrlPaths_INTERMEDIATES = $(VBOX_MAIN_APIWRAPPER_GEN_HDRS)
+tstGuestCtrlPaths_DEFS += VBOX_WITH_HGCM VBOX_WITH_GUEST_CONTROL VBOX_GUESTCTRL_TEST_CASE
+tstGuestCtrlPaths_SOURCES = \
+ tstGuestCtrlPaths.cpp \
+ ../src-client/GuestCtrlPrivate.cpp
+tstGuestCtrlPaths_INCS = ../include \
+ $(VBOX_MAIN_APIWRAPPER_INCS)
+
+if 0 # Enable this if you want automatic runs after compilation.
+ $$(tstGuestCtrlPaths_0_OUTDIR)/tstGuestCtrlPaths.run: $$(tstGuestCtrlPaths_1_STAGE_TARGET)
+ export VBOX_LOG_DEST=nofile; $(tstGuestCtrlPaths_1_STAGE_TARGET) quiet
+ $(QUIET)$(APPEND) -t "$@" "done"
+ OTHERS += $(tstGuestCtrlPaths_0_OUTDIR)/tstGuestCtrlPaths.run
+endif
+
+
+#
+# tstUSBProxyLinux
+#
+tstUSBProxyLinux_TEMPLATE = VBoxMainClientTstExe
+tstUSBProxyLinux_INTERMEDIATES = $(VBOX_MAIN_APIWRAPPER_GEN_HDRS)
+tstUSBProxyLinux_SOURCES = \
+ tstUSBProxyLinux.cpp \
+ ../src-server/linux/USBGetDevices.cpp
+tstUSBProxyLinux_INCS = \
+ . \
+ ../include \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include/nsprpub \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include/xpcom \
+ $(VBOX_MAIN_APIWRAPPER_INCS)
+tstUSBProxyLinux_DEFS = \
+ UNIT_TEST \
+ VBOX_WITH_USB \
+ VBOX_USB_WITH_SYSFS \
+ VBOX_WITH_XPCOM
+tstUSBProxyLinux_DEPS = \
+ $(VBOX_PATH_SDK)/bindings/xpcom/include/VirtualBox_XPCOM.h
+tstUSBProxyLinux_LIBS += \
+ $(PATH_OUT)/lib/USBLib.a \
+ $(PATH_OUT)/lib/VBoxCOM.a
+
+
+#
+# tstMediumLock
+#
+tstMediumLock_TEMPLATE = VBoxMainClientTstExe
+tstMediumLock_SOURCES = tstMediumLock.cpp
+
+
+#
+# tstSafeArray
+#
+tstSafeArray_TEMPLATE = VBoxMainClientTstExe
+tstSafeArray_SOURCES = tstSafeArray.cpp
+
+
+#
+# tstBstr
+#
+tstBstr_TEMPLATE = VBoxMainClientTstExe
+tstBstr_SOURCES = tstBstr.cpp
+
+
+#
+# tstGuid
+#
+tstGuid_TEMPLATE = VBoxMainClientTstExe
+tstGuid_SOURCES = tstGuid.cpp
+
+
+#
+# tstUnattendedScript
+#
+tstUnattendedScript_TEMPLATE = VBoxMainClientTstExe
+tstUnattendedScript_DEFS = VBOX_WITH_UNATTENDED IN_VBOXSVC IN_TST_UNATTENDED_SCRIPT
+tstUnattendedScript_INTERMEDIATES = \
+ $(VBOX_MAIN_APIWRAPPER_GEN_HDRS) \
+ $(VBOX_XML_SCHEMADEFS_H)
+tstUnattendedScript_INCS = \
+ ../include \
+ $(VBOX_MAIN_APIWRAPPER_INCS) \
+ $(dir $(VBOX_XML_SCHEMADEFS_H))
+tstUnattendedScript_SOURCES = \
+ tstUnattendedScript.cpp \
+ ../src-server/UnattendedScript.cpp \
+ ../src-all/TextScript.cpp \
+ ../src-all/VirtualBoxBase.cpp \
+ ../src-all/VirtualBoxErrorInfoImpl.cpp \
+ ../src-all/AutoCaller.cpp \
+ ../src-all/GlobalStatusConversion.cpp
+tstUnattendedScript_LIBS = \
+ $(PATH_STAGE_LIB)/VBoxAPIWrap$(VBOX_SUFF_LIB)
+
+INSTALLS += tstUnattendedScriptFiles
+tstUnattendedScriptFiles_TEMPLATE = VBoxMainTstExe
+tstUnattendedScriptFiles_SOURCES = \
+ tstUnattendedScript-1.template \
+ tstUnattendedScript-1.expected
+
+
+#
+# tstVBoxCrypto
+#
+tstVBoxCrypto_TEMPLATE = VBoxMainClientTstExe
+tstVBoxCrypto_SOURCES = tstVBoxCrypto.cpp
+
+
+
+# generate rules.
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Main/testcase/VBoxVBTest/TestForm.frm b/src/VBox/Main/testcase/VBoxVBTest/TestForm.frm
new file mode 100644
index 00000000..3ba63c8f
--- /dev/null
+++ b/src/VBox/Main/testcase/VBoxVBTest/TestForm.frm
@@ -0,0 +1,137 @@
+VERSION 5.00
+Begin VB.Form TestForm
+ Caption = "VirtualBox Test"
+ ClientHeight = 4692
+ ClientLeft = 60
+ ClientTop = 348
+ ClientWidth = 6972
+ LinkTopic = "TestForm"
+ ScaleHeight = 4692
+ ScaleWidth = 6972
+ StartUpPosition = 3 'Windows Default
+ Begin VB.ListBox machineList
+ Height = 2352
+ ItemData = "TestForm.frx":0000
+ Left = 240
+ List = "TestForm.frx":0007
+ TabIndex = 4
+ Top = 2040
+ Width = 6492
+ End
+ Begin VB.CommandButton getMachieListCmd
+ Caption = "Get Machine List"
+ Height = 372
+ Left = 2640
+ TabIndex = 0
+ Top = 720
+ Width = 1692
+ End
+ Begin VB.Label Label3
+ AutoSize = -1 'True
+ Caption = "Registered Machines:"
+ Height = 192
+ Left = 240
+ TabIndex = 5
+ Top = 1680
+ Width = 1572
+ End
+ Begin VB.Label versionLabel
+ AutoSize = -1 'True
+ Caption = "<none>"
+ Height = 192
+ Left = 1680
+ TabIndex = 3
+ Top = 1320
+ Width = 528
+ End
+ Begin VB.Label Label2
+ AutoSize = -1 'True
+ Caption = "VirtualBox Version:"
+ Height = 252
+ Left = 240
+ TabIndex = 2
+ Top = 1320
+ Width = 1344
+ End
+ Begin VB.Label Label1
+ Alignment = 2 'Center
+ Caption = $"TestForm.frx":0013
+ Height = 372
+ Left = 240
+ TabIndex = 1
+ Top = 120
+ Width = 6492
+ WordWrap = -1 'True
+ End
+End
+Attribute VB_Name = "TestForm"
+Attribute VB_GlobalNameSpace = False
+Attribute VB_Creatable = False
+Attribute VB_PredeclaredId = True
+Attribute VB_Exposed = False
+
+Private Declare Function SetEnvironmentVariable Lib "kernel32" _
+ Alias "SetEnvironmentVariableA" (ByVal lpName As String, ByVal lpValue As String) As Long
+Private Declare Function GetEnvironmentVariable Lib "kernel32" _
+ Alias "GetEnvironmentVariableA" (ByVal lpName As String, ByVal lpValue As String, ByVal nSize As Long) As Long
+
+Private Sub Form_Load()
+
+ ' Set where to take VirtualBox configuration from
+
+ 'SetEnvironmentVariable "VBOX_USER_HOME", "E:\VirtualBoxHome\win"
+
+ ' Setup debug logging (available only in debug builds)
+
+ 'PATH_OUT_BASE = "D:/Coding/innotek/vbox/out"
+
+ 'SetEnvironmentVariable "VBOX_LOG", "main.e.l.f + gui.e.l.f"
+ 'SetEnvironmentVariable "VBOX_LOG_FLAGS", "time tid thread"
+ 'SetEnvironmentVariable "VBOX_LOG_DEST", "dir:" + PATH_OUT_BASE + "/logs"
+
+End Sub
+
+Private Sub getMachieListCmd_Click()
+
+ ' Clear the old list contents
+
+ machineList.Clear
+ machineList.Refresh
+
+ versionLabel.Caption = "<none>"
+
+ ' Disable the button and the list for the duration of the call
+
+ getMachieListCmd.Enabled = False
+ machineList.Enabled = False
+
+ ' Obtain the global VirtualBox object (this will start
+ ' the VirtualBox server if it is not already started)
+
+ Dim vbox As VirtualBox.VirtualBox
+ Set vbox = New VirtualBox.VirtualBox
+
+ ' Get the VirtualBox server version
+
+ versionLabel.Caption = vbox.Version
+
+ ' Obtain a list of registered machines
+
+ Dim machines() As VirtualBox.IMachine
+ machines = vbox.Machines2
+
+ If UBound(machines) < 0 Then
+ machineList.AddItem ("<none>")
+ Else
+ For i = 0 To UBound(machines)
+ Item = machines(i).Name + " (" + machines(i).OSTypeId() + ")"
+ machineList.AddItem (Item)
+ Next i
+ End If
+
+ ' Reenable the button and the list
+
+ getMachieListCmd.Enabled = True
+ machineList.Enabled = True
+
+End Sub
diff --git a/src/VBox/Main/testcase/VBoxVBTest/TestForm.frx b/src/VBox/Main/testcase/VBoxVBTest/TestForm.frx
new file mode 100644
index 00000000..bd27fb45
--- /dev/null
+++ b/src/VBox/Main/testcase/VBoxVBTest/TestForm.frx
Binary files differ
diff --git a/src/VBox/Main/testcase/VBoxVBTest/VBoxVBTest.vbp b/src/VBox/Main/testcase/VBoxVBTest/VBoxVBTest.vbp
new file mode 100644
index 00000000..cdc19c8a
--- /dev/null
+++ b/src/VBox/Main/testcase/VBoxVBTest/VBoxVBTest.vbp
@@ -0,0 +1,34 @@
+Type=Exe
+Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\system32\stdole2.tlb#OLE Automation
+Reference=*\G{D7569351-1750-46F0-936E-BD127D5BC264}#1.3#0#VBoxC.dll#InnoTek VirtualBox Machine Type Library
+Form=TestForm.frm
+Startup="TestForm"
+ExeName32="VBoxVBTest.exe"
+Command32=""
+Name="VBoxVBTest"
+HelpContextID="0"
+CompatibleMode="0"
+MajorVer=1
+MinorVer=0
+RevisionVer=0
+AutoIncrementVer=0
+ServerSupportFiles=0
+VersionCompanyName="Something"
+CompilationType=0
+OptimizationType=0
+FavorPentiumPro(tm)=0
+CodeViewDebugInfo=0
+NoAliasing=0
+BoundsCheck=0
+OverflowCheck=0
+FlPointCheck=0
+FDIVCheck=0
+UnroundedFP=0
+StartMode=0
+Unattended=0
+Retained=0
+ThreadPerObject=0
+MaxNumberOfThreads=1
+
+[MS Transaction Server]
+AutoRefresh=1
diff --git a/src/VBox/Main/testcase/makefile.tstVBoxAPIWin b/src/VBox/Main/testcase/makefile.tstVBoxAPIWin
new file mode 100644
index 00000000..1229c981
--- /dev/null
+++ b/src/VBox/Main/testcase/makefile.tstVBoxAPIWin
@@ -0,0 +1,100 @@
+# $Id: makefile.tstVBoxAPIWin $
+## @file
+# tstVBoxAPILinux makefile
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+#
+# Several assumptions and propositions:
+# - Visual Studio has already installed on machine or you already have nmake.exe, cl.exe, link.exe
+# - Windows SDK has already installed on machine or you already have Uuid.Lib Ole32.Lib OleAut32.Lib OleDlg.Lib
+# - VirtualBox SDK was downloaded and was placed into folder where VirtualBox had been installed.
+# - nmake is a default tool that builds projects based on commands contained in this description file
+# - cl is cl.exe - Windows compiler
+# - link is link.exe - Windows linker
+# - all needed paths have been set in working environment. It means that when you type "cl" from the console,
+# Windows shall find cl.exe by using enviroment variable PATH or something similar.
+#
+# The best way to accomplish it is to run a script vcvars32.bat located in the Visual studio "bin" directory.
+# This script installs needed paths in your working environment.
+# Important!!!
+# Script vcvars32.bat sets up needed paths only for local console session.
+# For permanent using, needed paths must be added globally.
+#
+# Several possible examples of paths:
+# VS_INSTALL_PATH = "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\"
+# VS_INCLUDE_PATH = "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include"
+# WIN_SDK_INCLUDE_PATH = "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include"
+# WIN_SDK_LIB_PATH = "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Lib\x64\"
+# VB_INSTALL_PATH = "C:\Program Files\Oracle\VirtualBox"
+#
+
+
+CXX = cl
+LINK = link
+PATH_MSCOM = ../../../bindings/mscom
+INCS_MSCOM = $(PATH_MSCOM)/include
+LIBS_MSCOM = $(PATH_MSCOM)/lib
+
+LIBS_DEPS = "Uuid.Lib" "Ole32.Lib" "OleAut32.Lib" "OleDlg.Lib"
+
+tstVBoxAPIWin_SOURCES = $(LIBS_MSCOM)/VirtualBox_i.c
+tstVBoxAPIWin_DEPS = $(INCS_MSCOM)
+
+COMPILER_CMDLINE = /Zi /nologo /W3 /WX- /Od /Oy- /Gm /EHsc /RTC1 /GS /fp:precise /Gd /analyze- /errorReport:queue
+
+LINKER_CMDLINE = /INCREMENTAL /DEBUG /SUBSYSTEM:CONSOLE
+
+# default linking
+tstVBoxAPIWin.exe: tstVBoxAPIWin.obj VirtualBox_i.obj
+ $(LINK) /out:tstVBoxAPIWin.exe $** $(LIBS_DEPS)
+
+# default compilation
+tstVBoxAPIWin.obj:
+ $(CXX) /c /I$(INCS_MSCOM) tstVBoxAPIWin.cpp
+
+# default compilation
+VirtualBox_i.obj:
+ $(CXX) /c /I$(INCS_MSCOM) $(tstVBoxAPIWin_SOURCES)
+
+# linking with defined linker's options
+#tstVBoxAPIWin.exe: tstVBoxAPIWin.obj VirtualBox_i.obj
+# $(LINK) $(LINKER_CMDLINE) /out:tstVBoxAPIWin.exe $** $(LIBS_DEPS)
+
+# compile with pre-defined compiler's options and locally defined paths
+#tstVBoxAPIWin.obj:
+# $(CXX) /c $(COMPILER_CMDLINE) /I$(INCS_MSCOM) /I$(WIN_SDK_INCLUDE_PATH) /I$(VS_INCLUDE_PATH) tstVBoxAPIWin.cpp
+
+# compile with locally defined paths
+#tstVBoxAPIWin.obj:
+# $(CXX) /c /I$(INCS_MSCOM) /I$(WIN_SDK_INCLUDE_PATH) /I$(VS_INCLUDE_PATH) tstVBoxAPIWin.cpp
+
+# compile with pre-defined compiler's options and locally defined paths
+#VirtualBox_i.obj:
+# $(CXX) /c $(COMPILER_CMDLINE) /I$(INCS_MSCOM) /I$(WIN_SDK_INCLUDE_PATH) /I$(VS_INCLUDE_PATH) $(tstVBoxAPIWin_SOURCES)
+
+# compile with locally defined paths
+#VirtualBox_i.obj:
+# $(CXX) /c /I$(INCS_MSCOM) /I$(WIN_SDK_INCLUDE_PATH) /I$(VS_INCLUDE_PATH) $(tstVBoxAPIWin_SOURCES)
+
diff --git a/src/VBox/Main/testcase/makefile.tstVBoxAPIXPCOM b/src/VBox/Main/testcase/makefile.tstVBoxAPIXPCOM
new file mode 100644
index 00000000..f4752a04
--- /dev/null
+++ b/src/VBox/Main/testcase/makefile.tstVBoxAPIXPCOM
@@ -0,0 +1,65 @@
+# $Id: makefile.tstVBoxAPIXPCOM $
+## @file
+# tstVBoxAPIXPCOM makefile
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+PATH_XPCOM = ..
+PATH_BIN = ../../../../
+
+# This setting must be the same as used when building VBoxXPCOM.so.
+# If you get a lot of unresolved symbols, try commenting it out.
+VBOX_WITH_XPCOM_NAMESPACE_CLEANUP=1
+
+PATH_XPCOM_IDL = $(PATH_XPCOM)/idl
+INCS_XPCOM = $(PATH_XPCOM)/include \
+ $(PATH_XPCOM)/include/nsprpub \
+ $(PATH_XPCOM)/include/string \
+ $(PATH_XPCOM)/include/xpcom \
+ $(PATH_XPCOM)/include/ipcd
+
+ifdef VBOX_WITH_XPCOM_NAMESPACE_CLEANUP
+ DEFS_XPCOM += VBOX_WITH_XPCOM_NAMESPACE_CLEANUP
+endif
+
+# Adjust this to match your platform, pick from RT_OS_LINUX, RT_OS_WINDOWS,
+# RT_OS_DARWIN, RT_OS_SOLARIS...
+DEFS_XPCOM += RT_OS_LINUX
+
+
+#
+# Link with the public XPCOM libraries
+#
+tstVBoxAPIXPCOM: tstVBoxAPIXPCOM.o
+ g++ -g -o $@ $^ \
+ $(PATH_BIN)/VBoxXPCOM.so \
+ -Wl,-rpath $(PATH_BIN)/ \
+ -ldl -lpthread
+
+tstVBoxAPIXPCOM.o: tstVBoxAPIXPCOM.cpp
+ g++ -c -g -fshort-wchar $(addprefix -I, $(INCS_XPCOM)) $(addprefix -D, $(DEFS_XPCOM)) -o $@ tstVBoxAPIXPCOM.cpp
+
+clean:
+ rm -f tstVBoxAPIXPCOM tstVBoxAPIXPCOM.o
+
diff --git a/src/VBox/Main/testcase/msiDarwinDescriptorDecoder.cpp b/src/VBox/Main/testcase/msiDarwinDescriptorDecoder.cpp
new file mode 100644
index 00000000..8b37043f
--- /dev/null
+++ b/src/VBox/Main/testcase/msiDarwinDescriptorDecoder.cpp
@@ -0,0 +1,81 @@
+/* $Id: msiDarwinDescriptorDecoder.cpp $ */
+/** @file
+ * msiDarwinDescriptorDecoder
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+#include <stdio.h>
+#include <iprt/win/windows.h> /* Avoid -Wall warnings. */
+
+
+
+typedef DWORD (WINAPI *PFNMSIDECOMPOSEDESCRIPTORW)(PCWSTR pwszDescriptor,
+ LPWSTR pwszProductCode /*[40]*/,
+ LPWSTR pwszFeatureId /*[40]*/,
+ LPWSTR pwszComponentCode /*[40]*/,
+ DWORD *poffArguments);
+
+int wmain(int cArgs, wchar_t **papwszArgs)
+{
+ HMODULE hmodMsi = LoadLibrary("msi.dll");
+ PFNMSIDECOMPOSEDESCRIPTORW pfnMsiDecomposeDescriptorW;
+ pfnMsiDecomposeDescriptorW = (PFNMSIDECOMPOSEDESCRIPTORW)GetProcAddress(hmodMsi, "MsiDecomposeDescriptorW");
+ if (!pfnMsiDecomposeDescriptorW)
+ {
+ fprintf(stderr, "Failed to load msi.dll or resolve 'MsiDecomposeDescriptorW'\n");
+ return 1;
+ }
+
+ int rcExit = 0;
+ for (int iArg = 1; iArg < cArgs; iArg++)
+ {
+ wchar_t wszProductCode[40] = { 0 };
+ wchar_t wszFeatureId[40] = { 0 };
+ wchar_t wszComponentCode[40] = { 0 };
+ DWORD offArguments = ~(DWORD)0;
+ DWORD dwErr = pfnMsiDecomposeDescriptorW(papwszArgs[iArg], wszProductCode, wszFeatureId, wszComponentCode, &offArguments);
+ if (dwErr == 0)
+ {
+ fprintf(stderr,
+ "#%u: '%ls'\n"
+ " -> Product=%ls\n"
+ " -> FeatureId=%ls\n"
+ " -> ComponentCode=%ls\n"
+ " -> offArguments=%#lx (%ld)\n"
+ , iArg, papwszArgs[iArg], wszProductCode, wszFeatureId, wszComponentCode, offArguments, offArguments);
+ }
+ else
+ {
+ fprintf(stderr,
+ "#%u: '%ls'\n"
+ " -> error %lu (%#lx)\n"
+ , iArg, papwszArgs[iArg], dwErr, dwErr);
+ rcExit = 1;
+ }
+ }
+
+ return rcExit;
+}
+
diff --git a/src/VBox/Main/testcase/ovf-joomla-0.9/joomla-1.1.4-ovf.ovf b/src/VBox/Main/testcase/ovf-joomla-0.9/joomla-1.1.4-ovf.ovf
new file mode 100644
index 00000000..924379a8
--- /dev/null
+++ b/src/VBox/Main/testcase/ovf-joomla-0.9/joomla-1.1.4-ovf.ovf
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<ovf:Envelope xmlns:ovf="http://www.vmware.com/schema/ovf/1/envelope" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ovf:version="0.9">
+<References>
+<File ovf:href="joomla-1.1.4-ovf-0.vmdk" ovf:id="file1" ovf:size="199414784"/>
+<File ovf:href="joomla-1.1.4-ovf-1.vmdk" ovf:id="file2" ovf:size="393216"/>
+</References>
+<Section xsi:type="ovf:NetworkSection_Type">
+<Info>List of networks</Info>
+<Network ovf:name="Network 1">
+<Description>The "Network 1" network</Description>
+</Network>
+</Section>
+<Section xsi:type="ovf:DiskSection_Type">
+<Info>List of Virtual Disks</Info>
+<Disk ovf:capacity="1610612736" ovf:diskId="disk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse" ovf:populatedSize="639434752"/>
+<Disk ovf:capacity="10737418240" ovf:diskId="disk2" ovf:fileRef="file2" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse" ovf:populatedSize="7536640"/>
+</Section>
+<Content ovf:id="joomla-1.1.4-ovf" xsi:type="ovf:VirtualSystem_Type">
+<Info>A virtual machine</Info>
+<Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
+<Info>An annotation</Info>
+<Annotation>This is a JumpBox for joomla. For more information, please visit http://www.jumpbox.com/</Annotation>
+<!-- <Annotation>This is a JumpBox for joomla. For more information, please visit http://www.jumpbox.com/</Annotation> -->
+</Section>
+<Section ovf:id="101" ovf:required="false" xsi:type="ovf:OperatingSystemSection_Type">
+<Info>Guest Operating System</Info>
+<Description>Ubuntu</Description>
+</Section>
+<Section xsi:type="ovf:VirtualHardwareSection_Type">
+<Info>1 CPU, 256 memory</Info>
+<System>
+<vssd:VirtualSystemType>vmx-04</vssd:VirtualSystemType>
+</System>
+<Item>
+<rasd:Caption>1 virtual CPUs</rasd:Caption>
+<rasd:Description>Number of virtual CPUs</rasd:Description>
+<rasd:InstanceId>1</rasd:InstanceId>
+<rasd:ResourceType>3</rasd:ResourceType>
+<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
+</Item>
+<Item>
+<rasd:Caption>256 MB of memory</rasd:Caption>
+<rasd:Description>Memory Size</rasd:Description>
+<rasd:InstanceId>2</rasd:InstanceId>
+<rasd:ResourceType>4</rasd:ResourceType>
+<rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
+<rasd:VirtualQuantity>256</rasd:VirtualQuantity>
+</Item>
+<Item>
+<rasd:Caption>Harddisk 0</rasd:Caption>
+<rasd:InstanceId>9</rasd:InstanceId>
+<rasd:ResourceType>17</rasd:ResourceType>
+<rasd:HostResource>/disk/disk1</rasd:HostResource>
+<rasd:Parent>8</rasd:Parent>
+<rasd:AddressOnParent>0</rasd:AddressOnParent>
+</Item>
+<Item>
+<rasd:Caption>Harddisk 1</rasd:Caption>
+<rasd:InstanceId>10</rasd:InstanceId>
+<rasd:ResourceType>17</rasd:ResourceType>
+<rasd:HostResource>/disk/disk2</rasd:HostResource>
+<rasd:Parent>8</rasd:Parent>
+<rasd:AddressOnParent>1</rasd:AddressOnParent>
+</Item>
+<Item>
+<rasd:Caption>SCSI Controller 0</rasd:Caption>
+<rasd:InstanceId>8</rasd:InstanceId>
+<rasd:ResourceType>6</rasd:ResourceType>
+<rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>
+<rasd:BusNumber>0</rasd:BusNumber>
+</Item>
+<Item>
+<rasd:Caption>Ethernet adapter on "Network 1"</rasd:Caption>
+<rasd:InstanceId>7</rasd:InstanceId>
+<rasd:ResourceType>10</rasd:ResourceType>
+<rasd:ResourceSubType>PCNet32</rasd:ResourceSubType>
+<rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+<rasd:Connection>Network 1</rasd:Connection>
+<rasd:AddressOnParent>1</rasd:AddressOnParent>
+</Item>
+</Section>
+</Content>
+</ovf:Envelope>
diff --git a/src/VBox/Main/testcase/ovf-winhost-audio-nodisks/WinXP.ovf b/src/VBox/Main/testcase/ovf-winhost-audio-nodisks/WinXP.ovf
new file mode 100644
index 00000000..ea46077e
--- /dev/null
+++ b/src/VBox/Main/testcase/ovf-winhost-audio-nodisks/WinXP.ovf
@@ -0,0 +1,267 @@
+<?xml version="1.0"?>
+<Envelope ovf:version="1.0" xml:lang="en-US" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:vbox="http://www.virtualbox.org/ovf/machine">
+ <References/>
+ <DiskSection>
+ <Info>List of the virtual disks used in the package</Info>
+ </DiskSection>
+ <NetworkSection>
+ <Info>Logical networks used in the package</Info>
+ <Network ovf:name="NAT">
+ <Description>Logical network used by this appliance.</Description>
+ </Network>
+ </NetworkSection>
+ <VirtualSystem ovf:id="WinXP">
+ <Info>A virtual machine</Info>
+ <OperatingSystemSection ovf:id="67">
+ <Info>The kind of installed guest operating system</Info>
+ <Description>WindowsXP</Description>
+ </OperatingSystemSection>
+ <VirtualHardwareSection>
+ <Info>Virtual hardware requirements for a virtual machine</Info>
+ <System>
+ <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
+ <vssd:InstanceID>0</vssd:InstanceID>
+ <vssd:VirtualSystemIdentifier>WinXP</vssd:VirtualSystemIdentifier>
+ <vssd:VirtualSystemType>virtualbox-2.2</vssd:VirtualSystemType>
+ </System>
+ <Item>
+ <rasd:Caption>1 virtual CPU</rasd:Caption>
+ <rasd:Description>Number of virtual CPUs</rasd:Description>
+ <rasd:ElementName>1 virtual CPU</rasd:ElementName>
+ <rasd:InstanceID>1</rasd:InstanceID>
+ <rasd:ResourceType>3</rasd:ResourceType>
+ <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
+ </Item>
+ <Item>
+ <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
+ <rasd:Caption>512 MB of memory</rasd:Caption>
+ <rasd:Description>Memory Size</rasd:Description>
+ <rasd:ElementName>512 MB of memory</rasd:ElementName>
+ <rasd:InstanceID>2</rasd:InstanceID>
+ <rasd:ResourceType>4</rasd:ResourceType>
+ <rasd:VirtualQuantity>512</rasd:VirtualQuantity>
+ </Item>
+ <Item>
+ <rasd:Address>0</rasd:Address>
+ <rasd:Caption>ideController0</rasd:Caption>
+ <rasd:Description>IDE Controller</rasd:Description>
+ <rasd:ElementName>ideController0</rasd:ElementName>
+ <rasd:InstanceID>3</rasd:InstanceID>
+ <rasd:ResourceSubType>PIIX3</rasd:ResourceSubType>
+ <rasd:ResourceType>5</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:Address>1</rasd:Address>
+ <rasd:Caption>ideController1</rasd:Caption>
+ <rasd:Description>IDE Controller</rasd:Description>
+ <rasd:ElementName>ideController1</rasd:ElementName>
+ <rasd:InstanceID>4</rasd:InstanceID>
+ <rasd:ResourceSubType>PIIX3</rasd:ResourceSubType>
+ <rasd:ResourceType>5</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+ <rasd:Caption>Ethernet adapter on 'NAT'</rasd:Caption>
+ <rasd:Connection>NAT</rasd:Connection>
+ <rasd:ElementName>Ethernet adapter on 'NAT'</rasd:ElementName>
+ <rasd:InstanceID>5</rasd:InstanceID>
+ <rasd:ResourceSubType>PCNet32</rasd:ResourceSubType>
+ <rasd:ResourceType>10</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:Address>0</rasd:Address>
+ <rasd:Caption>usb</rasd:Caption>
+ <rasd:Description>USB Controller</rasd:Description>
+ <rasd:ElementName>usb</rasd:ElementName>
+ <rasd:InstanceID>6</rasd:InstanceID>
+ <rasd:ResourceType>23</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:AddressOnParent>3</rasd:AddressOnParent>
+ <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+ <rasd:Caption>sound</rasd:Caption>
+ <rasd:Description>Sound Card</rasd:Description>
+ <rasd:ElementName>sound</rasd:ElementName>
+ <rasd:InstanceID>7</rasd:InstanceID>
+ <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
+ <rasd:ResourceType>35</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:AddressOnParent>0</rasd:AddressOnParent>
+ <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+ <rasd:Caption>cdrom1</rasd:Caption>
+ <rasd:Description>CD-ROM Drive</rasd:Description>
+ <rasd:ElementName>cdrom1</rasd:ElementName>
+ <rasd:InstanceID>8</rasd:InstanceID>
+ <rasd:Parent>4</rasd:Parent>
+ <rasd:ResourceType>15</rasd:ResourceType>
+ </Item>
+ </VirtualHardwareSection>
+ <vbox:Machine ovf:required="false" version="1.10-windows" uuid="{fc4b0fa2-61a7-4974-939a-d25bdab59971}" name="WinXP" OSType="WindowsXP" lastStateChange="2010-07-20T14:23:26Z">
+ <ovf:Info>Complete VirtualBox machine configuration in VirtualBox format</ovf:Info>
+ <ExtraData>
+ <ExtraDataItem name="GUI/LastCloseAction" value="powerOff"/>
+ <ExtraDataItem name="GUI/LastGuestSizeHint" value="640,480"/>
+ <ExtraDataItem name="GUI/LastNormalWindowPosition" value="520,243,640,523"/>
+ <ExtraDataItem name="GUI/LastScaleWindowPosition" value="520,265,640,480"/>
+ <ExtraDataItem name="GUI/MiniToolBarAlignment" value="bottom"/>
+ <ExtraDataItem name="GUI/SaveMountedAtRuntime" value="yes"/>
+ <ExtraDataItem name="GUI/ShowMiniToolBar" value="yes"/>
+ </ExtraData>
+ <Hardware version="2">
+ <CPU count="1" hotplug="false">
+ <HardwareVirtEx enabled="true" exclusive="false"/>
+ <HardwareVirtExNestedPaging enabled="true"/>
+ <HardwareVirtExVPID enabled="true"/>
+ <PAE enabled="false"/>
+ </CPU>
+ <Memory RAMSize="512" PageFusion="false"/>
+ <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/>
+ <HPET enabled="false"/>
+ <Boot>
+ <Order position="1" device="HardDisk"/>
+ <Order position="2" device="None"/>
+ <Order position="3" device="None"/>
+ <Order position="4" device="None"/>
+ </Boot>
+ <Display VRAMSize="20" monitorCount="1" accelerate3D="false" accelerate2DVideo="false"/>
+ <RemoteDisplay enabled="false" port="3389" authType="Null" authTimeout="5000">
+ <VideoChannel enabled="false" quality="75"/>
+ </RemoteDisplay>
+ <BIOS>
+ <ACPI enabled="true"/>
+ <IOAPIC enabled="false"/>
+ <Logo fadeIn="true" fadeOut="true" displayTime="0"/>
+ <BootMenu mode="MessageAndMenu"/>
+ <TimeOffset value="0"/>
+ <PXEDebug enabled="false"/>
+ </BIOS>
+ <USBController enabled="true" enabledEhci="true"/>
+ <Network>
+ <Adapter slot="0" enabled="true" MACAddress="080027FDEC62" cable="true" speed="0" type="Am79C973">
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </Adapter>
+ <Adapter slot="1" enabled="false" MACAddress="08002767B875" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="2" enabled="false" MACAddress="080027CE5CC2" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="3" enabled="false" MACAddress="0800277CCAFC" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="4" enabled="false" MACAddress="080027D1AE28" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="5" enabled="false" MACAddress="080027A2AA62" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="6" enabled="false" MACAddress="080027BFC29B" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="7" enabled="false" MACAddress="080027A2A78A" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ </Network>
+ <UART>
+ <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/>
+ <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/>
+ </UART>
+ <LPT>
+ <Port slot="0" enabled="false" IOBase="0x378" IRQ="4"/>
+ <Port slot="1" enabled="false" IOBase="0x378" IRQ="4"/>
+ </LPT>
+ <AudioAdapter controller="AC97" driver="DirectSound" enabled="true"/>
+ <RTC localOrUTC="local"/>
+ <SharedFolders/>
+ <Clipboard mode="Bidirectional"/>
+ <IO>
+ <IoCache enabled="true" size="5"/>
+ <IoBandwidth max="0"/>
+ </IO>
+ <Guest memoryBalloonSize="0"/>
+ <GuestProperties>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/Product" value="Windows XP Professional" timestamp="1279634733194581900" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/Release" value="5.1.2600" timestamp="1279634733206582500" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/Version" value="" timestamp="1279634733209582700" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/ServicePack" value="2" timestamp="1279634733215583100" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Version" value="3.1.51" timestamp="1279634733216583100" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Revision" value="60359" timestamp="1279634733217583200" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/InstallDir" value="C:/Program Files/Oracle/VirtualBox Guest Additions" timestamp="1279634733219583300" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxControl.exe" value="3.1.51r60359" timestamp="1279634733223583500" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxHook.dll" value="3.1.51r60359" timestamp="1279634733227583700" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxDisp.dll" value="3.1.51r60359" timestamp="1279634733230583900" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxMRXNP.dll" value="3.1.51r60359" timestamp="1279634733235584200" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxService.exe" value="3.1.51r60359" timestamp="1279634733236584300" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxTray.exe" value="3.1.51r60359" timestamp="1279634733243584700" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxGINA.dll" value="-" timestamp="1279634733274586400" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxCredProv.dll" value="-" timestamp="1279634733292587500" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLarrayspu.dll" value="3.1.51r60359" timestamp="1279634733294587600" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLcrutil.dll" value="3.1.51r60359" timestamp="1279634733296587700" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLerrorspu.dll" value="3.1.51r60359" timestamp="1279634733298587800" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLpackspu.dll" value="3.1.51r60359" timestamp="1279634733300587900" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLpassthroughspu.dll" value="3.1.51r60359" timestamp="1279634733303588100" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLfeedbackspu.dll" value="3.1.51r60359" timestamp="1279634733305588200" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGL.dll" value="3.1.51r60359" timestamp="1279634733310588500" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxGuest.sys" value="3.1.51r60359" timestamp="1279634733316588800" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxMouse.sys" value="3.1.51r60359" timestamp="1279634733321589100" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxSF.sys" value="3.1.51r60359" timestamp="1279634733325589400" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxVideo.sys" value="3.1.51r60359" timestamp="1279634733331589700" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/NoLoggedInUsers" value="false" timestamp="1279634743366163600" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/HostVerLastChecked" value="3.2.51" timestamp="1279634759329076700" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/LoggedInUsersList" value="Dsen" timestamp="1279634773413882300" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/LoggedInUsers" value="1" timestamp="1279634773414882300" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/Count" value="1" timestamp="1279634773416882400" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/0/V4/IP" value="10.0.2.15" timestamp="1279634773417882500" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/0/V4/Broadcast" value="255.255.255.255" timestamp="1279634773418882600" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/0/V4/Netmask" value="255.255.255.0" timestamp="1279634773418882601" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/0/Status" value="Up" timestamp="1279634773419882600" flags=""/>
+ <GuestProperty name="/VirtualBox/HostInfo/GUI/LanguageID" value="C" timestamp="1279636075749371600" flags=""/>
+ </GuestProperties>
+ </Hardware>
+ <StorageControllers>
+ <StorageController name="IDE Controller" type="PIIX3" PortCount="2" useHostIOCache="true">
+ <AttachedDevice passthrough="false" type="DVD" port="1" device="0"/>
+ </StorageController>
+ </StorageControllers>
+ </vbox:Machine>
+ </VirtualSystem>
+</Envelope>
diff --git a/src/VBox/Main/testcase/ovf-winxp-vbox-sharedfolders/winxp.ovf b/src/VBox/Main/testcase/ovf-winxp-vbox-sharedfolders/winxp.ovf
new file mode 100644
index 00000000..9d6ce61c
--- /dev/null
+++ b/src/VBox/Main/testcase/ovf-winxp-vbox-sharedfolders/winxp.ovf
@@ -0,0 +1,315 @@
+<?xml version="1.0"?>
+<Envelope ovf:version="1.0" xml:lang="en-US" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:vbox="http://www.virtualbox.org/ovf/machine">
+ <References>
+ <File ovf:href="Windows 5.1 XP 1 merged.vmdk" ovf:id="file1" ovf:size="4948965888"/>
+ <File ovf:href="smallvdi.vmdk" ovf:id="file2" ovf:size="1265152"/>
+ </References>
+ <DiskSection>
+ <Info>List of the virtual disks used in the package</Info>
+ <Disk ovf:capacity="10485760000" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" vbox:uuid="1fc3c37e-079b-477b-a6d7-84c0e8a717ac"/>
+ <Disk ovf:capacity="2147483648" ovf:diskId="vmdisk2" ovf:fileRef="file2" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" vbox:uuid="cf2b9350-4d5f-42a2-be70-b85a00c2ec9f"/>
+ </DiskSection>
+ <NetworkSection>
+ <Info>Logical networks used in the package</Info>
+ <Network ovf:name="NAT">
+ <Description>Logical network used by this appliance.</Description>
+ </Network>
+ </NetworkSection>
+ <VirtualSystem ovf:id="Windows 5.1 XP 1">
+ <Info>A virtual machine</Info>
+ <OperatingSystemSection ovf:id="67">
+ <Info>The kind of installed guest operating system</Info>
+ <Description>WindowsXP</Description>
+ </OperatingSystemSection>
+ <VirtualHardwareSection>
+ <Info>Virtual hardware requirements for a virtual machine</Info>
+ <System>
+ <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
+ <vssd:InstanceID>0</vssd:InstanceID>
+ <vssd:VirtualSystemIdentifier>Windows 5.1 XP 1</vssd:VirtualSystemIdentifier>
+ <vssd:VirtualSystemType>virtualbox-2.2</vssd:VirtualSystemType>
+ </System>
+ <Item>
+ <rasd:Caption>1 virtual CPU</rasd:Caption>
+ <rasd:Description>Number of virtual CPUs</rasd:Description>
+ <rasd:ElementName>1 virtual CPU</rasd:ElementName>
+ <rasd:InstanceID>1</rasd:InstanceID>
+ <rasd:ResourceType>3</rasd:ResourceType>
+ <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
+ </Item>
+ <Item>
+ <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
+ <rasd:Caption>895 MB of memory</rasd:Caption>
+ <rasd:Description>Memory Size</rasd:Description>
+ <rasd:ElementName>895 MB of memory</rasd:ElementName>
+ <rasd:InstanceID>2</rasd:InstanceID>
+ <rasd:ResourceType>4</rasd:ResourceType>
+ <rasd:VirtualQuantity>895</rasd:VirtualQuantity>
+ </Item>
+ <Item>
+ <rasd:Address>0</rasd:Address>
+ <rasd:Caption>ideController0</rasd:Caption>
+ <rasd:Description>IDE Controller</rasd:Description>
+ <rasd:ElementName>ideController0</rasd:ElementName>
+ <rasd:InstanceID>3</rasd:InstanceID>
+ <rasd:ResourceSubType>PIIX3</rasd:ResourceSubType>
+ <rasd:ResourceType>5</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:Address>1</rasd:Address>
+ <rasd:Caption>ideController1</rasd:Caption>
+ <rasd:Description>IDE Controller</rasd:Description>
+ <rasd:ElementName>ideController1</rasd:ElementName>
+ <rasd:InstanceID>4</rasd:InstanceID>
+ <rasd:ResourceSubType>PIIX3</rasd:ResourceSubType>
+ <rasd:ResourceType>5</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:AddressOnParent>0</rasd:AddressOnParent>
+ <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+ <rasd:Caption>floppy0</rasd:Caption>
+ <rasd:Description>Floppy Drive</rasd:Description>
+ <rasd:ElementName>floppy0</rasd:ElementName>
+ <rasd:InstanceID>5</rasd:InstanceID>
+ <rasd:ResourceType>14</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+ <rasd:Caption>Ethernet adapter on 'NAT'</rasd:Caption>
+ <rasd:Connection>NAT</rasd:Connection>
+ <rasd:ElementName>Ethernet adapter on 'NAT'</rasd:ElementName>
+ <rasd:InstanceID>6</rasd:InstanceID>
+ <rasd:ResourceSubType>PCNet32</rasd:ResourceSubType>
+ <rasd:ResourceType>10</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:Address>0</rasd:Address>
+ <rasd:Caption>usb</rasd:Caption>
+ <rasd:Description>USB Controller</rasd:Description>
+ <rasd:ElementName>usb</rasd:ElementName>
+ <rasd:InstanceID>7</rasd:InstanceID>
+ <rasd:ResourceType>23</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:AddressOnParent>3</rasd:AddressOnParent>
+ <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+ <rasd:Caption>sound</rasd:Caption>
+ <rasd:Description>Sound Card</rasd:Description>
+ <rasd:ElementName>sound</rasd:ElementName>
+ <rasd:InstanceID>8</rasd:InstanceID>
+ <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
+ <rasd:ResourceType>35</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:AddressOnParent>0</rasd:AddressOnParent>
+ <rasd:Caption>disk1</rasd:Caption>
+ <rasd:Description>Disk Image</rasd:Description>
+ <rasd:ElementName>disk1</rasd:ElementName>
+ <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
+ <rasd:InstanceID>9</rasd:InstanceID>
+ <rasd:Parent>3</rasd:Parent>
+ <rasd:ResourceType>17</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:AddressOnParent>1</rasd:AddressOnParent>
+ <rasd:Caption>disk2</rasd:Caption>
+ <rasd:Description>Disk Image</rasd:Description>
+ <rasd:ElementName>disk2</rasd:ElementName>
+ <rasd:HostResource>/disk/vmdisk2</rasd:HostResource>
+ <rasd:InstanceID>10</rasd:InstanceID>
+ <rasd:Parent>3</rasd:Parent>
+ <rasd:ResourceType>17</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:AddressOnParent>0</rasd:AddressOnParent>
+ <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+ <rasd:Caption>cdrom1</rasd:Caption>
+ <rasd:Description>CD-ROM Drive</rasd:Description>
+ <rasd:ElementName>cdrom1</rasd:ElementName>
+ <rasd:InstanceID>11</rasd:InstanceID>
+ <rasd:Parent>4</rasd:Parent>
+ <rasd:ResourceType>15</rasd:ResourceType>
+ </Item>
+ </VirtualHardwareSection>
+ <vbox:Machine ovf:required="false" version="1.10-linux" uuid="{5f102a55-a51b-48e3-b45a-b28d33469488}" name="Windows 5.1 XP 1" OSType="WindowsXP" snapshotFolder="Snapshots" lastStateChange="2010-06-15T15:33:58Z">
+ <ovf:Info>Complete VirtualBox machine configuration in VirtualBox format</ovf:Info>
+ <ExtraData>
+ <ExtraDataItem name="GUI/InfoDlgState" value="380,662,normal"/>
+ <ExtraDataItem name="GUI/LastCloseAction" value="save"/>
+ <ExtraDataItem name="GUI/LastGuestSizeHint" value="1136,933"/>
+ <ExtraDataItem name="GUI/LastWindowPostion" value="188,53,1136,977"/>
+ <ExtraDataItem name="GUI/MiniToolBarAlignment" value="bottom"/>
+ <ExtraDataItem name="GUI/SaveMountedAtRuntime" value="yes"/>
+ <ExtraDataItem name="GUI/ShowMiniToolBar" value="yes"/>
+ <ExtraDataItem name="VBoxInternal2/VRDPBindPort" value="3389"/>
+ </ExtraData>
+ <Hardware version="2">
+ <CPU count="1" hotplug="false">
+ <HardwareVirtEx enabled="true" exclusive="true"/>
+ <HardwareVirtExNestedPaging enabled="true"/>
+ <HardwareVirtExVPID enabled="false"/>
+ <PAE enabled="false"/>
+ </CPU>
+ <Memory RAMSize="895" PageFusion="false"/>
+ <HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/>
+ <HPET enabled="false"/>
+ <Boot>
+ <Order position="1" device="Floppy"/>
+ <Order position="2" device="DVD"/>
+ <Order position="3" device="HardDisk"/>
+ <Order position="4" device="None"/>
+ </Boot>
+ <Display VRAMSize="24" monitorCount="1" accelerate3D="true" accelerate2DVideo="false"/>
+ <RemoteDisplay enabled="true" port="3389" authType="Null" authTimeout="5000">
+ <VideoChannel enabled="false" quality="75"/>
+ </RemoteDisplay>
+ <BIOS>
+ <ACPI enabled="true"/>
+ <IOAPIC enabled="false"/>
+ <Logo fadeIn="true" fadeOut="false" displayTime="0"/>
+ <BootMenu mode="MessageAndMenu"/>
+ <TimeOffset value="0"/>
+ <PXEDebug enabled="false"/>
+ </BIOS>
+ <USBController enabled="true" enabledEhci="false">
+ <DeviceFilter name="SanDisk U3 Cruzer Micro [0200]" active="true" vendorId="0781" productId="5406" revision="0200" manufacturer="SanDisk" product="U3 Cruzer Micro" serialNumber="0877201B1A11B7A6" remote="no"/>
+ </USBController>
+ <Network>
+ <Adapter slot="0" enabled="true" MACAddress="080027F1086C" cable="true" speed="0" type="Am79C973">
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </Adapter>
+ <Adapter slot="1" enabled="false" MACAddress="0800273B18E3" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="2" enabled="false" MACAddress="080027C9B8C9" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="3" enabled="false" MACAddress="08002729FB57" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="4" enabled="false" MACAddress="080027C6BF50" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="5" enabled="false" MACAddress="080027DF7499" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="6" enabled="false" MACAddress="080027FB8C2B" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ <Adapter slot="7" enabled="false" MACAddress="080027614C1B" cable="true" speed="0" type="Am79C973">
+ <DisabledModes>
+ <NAT>
+ <DNS pass-domain="true" use-proxy="false" use-host-resolver="false"/>
+ <Alias logging="false" proxy-only="false" use-same-ports="false"/>
+ </NAT>
+ </DisabledModes>
+ </Adapter>
+ </Network>
+ <UART>
+ <Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/>
+ <Port slot="1" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/>
+ </UART>
+ <LPT>
+ <Port slot="0" enabled="false" IOBase="0x378" IRQ="4"/>
+ <Port slot="1" enabled="false" IOBase="0x378" IRQ="4"/>
+ </LPT>
+ <AudioAdapter controller="AC97" driver="ALSA" enabled="true"/>
+ <RTC localOrUTC="local"/>
+ <SharedFolders>
+ <SharedFolder name="t" hostPath="/path/does/not/exist" writable="true"/>
+ </SharedFolders>
+ <Clipboard mode="Bidirectional"/>
+ <IO>
+ <IoCache enabled="true" size="5"/>
+ <IoBandwidth max="0"/>
+ </IO>
+ <Guest memoryBalloonSize="0"/>
+ <GuestProperties>
+ <GuestProperty name="/VirtualBox/HostInfo/GUI/LanguageID" value="C" timestamp="1276615475026659000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/Product" value="Windows XP Professional" timestamp="1276615535415199000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/Release" value="5.1.2600" timestamp="1276615535423377000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/Version" value="" timestamp="1276615535424390000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/ServicePack" value="3" timestamp="1276615535425834000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Version" value="3.1.7" timestamp="1276615535426567000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Revision" value="60877" timestamp="1276615535428273000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/InstallDir" value="C:/Program Files/Sun/VirtualBox Guest Additions" timestamp="1276615535428823000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxControl.exe" value="3.1.7r60877" timestamp="1276615535452159000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxHook.dll" value="3.1.7r60877" timestamp="1276615535453735000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxDisp.dll" value="3.1.7r60877" timestamp="1276615535455440000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxMRXNP.dll" value="3.1.7r60877" timestamp="1276615535456268000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxService.exe" value="3.1.7r60877" timestamp="1276615535457312000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxTray.exe" value="3.1.7r60877" timestamp="1276615535458094000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxGINA.dll" value="3.0.0r49275" timestamp="1276615535458828000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxCredProv.dll" value="-" timestamp="1276615535467867000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLarrayspu.dll" value="3.1.7r60877" timestamp="1276615535468601000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLcrutil.dll" value="3.1.7r60877" timestamp="1276615535469299000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLerrorspu.dll" value="3.1.7r60877" timestamp="1276615535469853000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLpackspu.dll" value="3.1.7r60877" timestamp="1276615535491615000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLpassthroughspu.dll" value="3.1.7r60877" timestamp="1276615535492438000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGLfeedbackspu.dll" value="3.1.7r60877" timestamp="1276615535493364000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxOGL.dll" value="3.1.7r60877" timestamp="1276615535494186000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxGuest.sys" value="3.1.7r60877" timestamp="1276615535495047000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxMouse.sys" value="3.1.7r60877" timestamp="1276615535495946000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxSF.sys" value="3.1.7r60877" timestamp="1276615535496631000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/Components/VBoxVideo.sys" value="3.1.7r60877" timestamp="1276615535497318000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestAdd/HostVerLastChecked" value="3.2.51" timestamp="1276615561761565000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/LoggedInUsersList" value="" timestamp="1276616026260647000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/LoggedInUsers" value="0" timestamp="1276616026262352000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/OS/NoLoggedInUsers" value="true" timestamp="1276616026262920000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/0/V4/IP" value="" timestamp="1276616026263814000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/0/V4/Broadcast" value="" timestamp="1276616026264275000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/0/V4/Netmask" value="" timestamp="1276616026264727000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/0/Status" value="" timestamp="1276616026265170000" flags=""/>
+ <GuestProperty name="/VirtualBox/GuestInfo/Net/Count" value="0" timestamp="1276616026266071000" flags=""/>
+ </GuestProperties>
+ </Hardware>
+ <StorageControllers>
+ <StorageController name="IDE Controller" type="PIIX3" PortCount="2" useHostIOCache="true">
+ <AttachedDevice type="HardDisk" port="0" device="0">
+ <Image uuid="{1fc3c37e-079b-477b-a6d7-84c0e8a717ac}"/>
+ </AttachedDevice>
+ <AttachedDevice type="HardDisk" port="0" device="1">
+ <Image uuid="{cf2b9350-4d5f-42a2-be70-b85a00c2ec9f}"/>
+ </AttachedDevice>
+ <AttachedDevice passthrough="false" type="DVD" port="1" device="0"/>
+ </StorageController>
+ <StorageController name="Floppy Controller" type="I82078" PortCount="1" useHostIOCache="true">
+ <AttachedDevice type="Floppy" port="0" device="0"/>
+ </StorageController>
+ </StorageControllers>
+ </vbox:Machine>
+ </VirtualSystem>
+</Envelope>
diff --git a/src/VBox/Main/testcase/tstAPI.cpp b/src/VBox/Main/testcase/tstAPI.cpp
new file mode 100644
index 00000000..51ef54d8
--- /dev/null
+++ b/src/VBox/Main/testcase/tstAPI.cpp
@@ -0,0 +1,1689 @@
+/* $Id: tstAPI.cpp $ */
+/** @file
+ * tstAPI - test program for our COM/XPCOM interface
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+
+#include <VBox/com/VirtualBox.h>
+
+using namespace com;
+
+#define LOG_ENABLED
+#define LOG_GROUP LOG_GROUP_MAIN
+#include <VBox/log.h>
+
+#include <iprt/initterm.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/stream.h>
+#include <iprt/thread.h>
+
+
+// forward declarations
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef VBOX_WITH_RESOURCE_USAGE_API
+static Bstr getObjectName(ComPtr<IVirtualBox> aVirtualBox, ComPtr<IUnknown> aObject);
+static void queryMetrics(ComPtr<IVirtualBox> aVirtualBox,
+ ComPtr<IPerformanceCollector> collector,
+ ComSafeArrayIn(IUnknown *, objects));
+static void listAffectedMetrics(ComPtr<IVirtualBox> aVirtualBox,
+ ComSafeArrayIn(IPerformanceMetric*, aMetrics));
+#endif
+
+// funcs
+///////////////////////////////////////////////////////////////////////////////
+
+HRESULT readAndChangeMachineSettings(IMachine *machine, IMachine *readonlyMachine = 0)
+{
+ HRESULT hrc = S_OK;
+
+ Bstr name;
+ RTPrintf("Getting machine name...\n");
+ CHECK_ERROR_RET(machine, COMGETTER(Name)(name.asOutParam()), hrc);
+ RTPrintf("Name: {%ls}\n", name.raw());
+
+ RTPrintf("Getting machine GUID...\n");
+ Bstr guid;
+ CHECK_ERROR(machine, COMGETTER(Id)(guid.asOutParam()));
+ if (SUCCEEDED(hrc) && !guid.isEmpty()) {
+ RTPrintf("Guid::toString(): {%s}\n", Utf8Str(guid).c_str());
+ } else {
+ RTPrintf("WARNING: there's no GUID!");
+ }
+
+ ULONG memorySize;
+ RTPrintf("Getting memory size...\n");
+ CHECK_ERROR_RET(machine, COMGETTER(MemorySize)(&memorySize), hrc);
+ RTPrintf("Memory size: %d\n", memorySize);
+
+ MachineState_T machineState;
+ RTPrintf("Getting machine state...\n");
+ CHECK_ERROR_RET(machine, COMGETTER(State)(&machineState), hrc);
+ RTPrintf("Machine state: %d\n", machineState);
+
+ BOOL modified;
+ RTPrintf("Are any settings modified?...\n");
+ CHECK_ERROR(machine, COMGETTER(SettingsModified)(&modified));
+ if (SUCCEEDED(hrc))
+ RTPrintf("%s\n", modified ? "yes" : "no");
+
+ ULONG memorySizeBig = memorySize * 10;
+ RTPrintf("Changing memory size to %d...\n", memorySizeBig);
+ CHECK_ERROR(machine, COMSETTER(MemorySize)(memorySizeBig));
+
+ if (SUCCEEDED(hrc))
+ {
+ RTPrintf("Are any settings modified now?...\n");
+ CHECK_ERROR_RET(machine, COMGETTER(SettingsModified)(&modified), hrc);
+ RTPrintf("%s\n", modified ? "yes" : "no");
+ ASSERT_RET(modified, 0);
+
+ ULONG memorySizeGot;
+ RTPrintf("Getting memory size again...\n");
+ CHECK_ERROR_RET(machine, COMGETTER(MemorySize)(&memorySizeGot), hrc);
+ RTPrintf("Memory size: %d\n", memorySizeGot);
+ ASSERT_RET(memorySizeGot == memorySizeBig, 0);
+
+ if (readonlyMachine)
+ {
+ RTPrintf("Getting memory size of the counterpart readonly machine...\n");
+ ULONG memorySizeRO;
+ readonlyMachine->COMGETTER(MemorySize)(&memorySizeRO);
+ RTPrintf("Memory size: %d\n", memorySizeRO);
+ ASSERT_RET(memorySizeRO != memorySizeGot, 0);
+ }
+
+ RTPrintf("Discarding recent changes...\n");
+ CHECK_ERROR_RET(machine, DiscardSettings(), hrc);
+ RTPrintf("Are any settings modified after discarding?...\n");
+ CHECK_ERROR_RET(machine, COMGETTER(SettingsModified)(&modified), hrc);
+ RTPrintf("%s\n", modified ? "yes" : "no");
+ ASSERT_RET(!modified, 0);
+
+ RTPrintf("Getting memory size once more...\n");
+ CHECK_ERROR_RET(machine, COMGETTER(MemorySize)(&memorySizeGot), hrc);
+ RTPrintf("Memory size: %d\n", memorySizeGot);
+ ASSERT_RET(memorySizeGot == memorySize, 0);
+
+ memorySize = memorySize > 128 ? memorySize / 2 : memorySize * 2;
+ RTPrintf("Changing memory size to %d...\n", memorySize);
+ CHECK_ERROR_RET(machine, COMSETTER(MemorySize)(memorySize), hrc);
+ }
+
+ Bstr desc;
+ RTPrintf("Getting description...\n");
+ CHECK_ERROR_RET(machine, COMGETTER(Description)(desc.asOutParam()), hrc);
+ RTPrintf("Description is: \"%ls\"\n", desc.raw());
+
+ desc = L"This is an exemplary description (changed).";
+ RTPrintf("Setting description to \"%ls\"...\n", desc.raw());
+ CHECK_ERROR_RET(machine, COMSETTER(Description)(desc.raw()), hrc);
+
+ RTPrintf("Saving machine settings...\n");
+ CHECK_ERROR(machine, SaveSettings());
+ if (SUCCEEDED(hrc))
+ {
+ RTPrintf("Are any settings modified after saving?...\n");
+ CHECK_ERROR_RET(machine, COMGETTER(SettingsModified)(&modified), hrc);
+ RTPrintf("%s\n", modified ? "yes" : "no");
+ ASSERT_RET(!modified, 0);
+
+ if (readonlyMachine) {
+ RTPrintf("Getting memory size of the counterpart readonly machine...\n");
+ ULONG memorySizeRO;
+ readonlyMachine->COMGETTER(MemorySize)(&memorySizeRO);
+ RTPrintf("Memory size: %d\n", memorySizeRO);
+ ASSERT_RET(memorySizeRO == memorySize, 0);
+ }
+ }
+
+ Bstr extraDataKey = L"Blafasel";
+ Bstr extraData;
+ RTPrintf("Getting extra data key {%ls}...\n", extraDataKey.raw());
+ CHECK_ERROR_RET(machine, GetExtraData(extraDataKey.raw(), extraData.asOutParam()), hrc);
+ if (!extraData.isEmpty()) {
+ RTPrintf("Extra data value: {%ls}\n", extraData.raw());
+ } else {
+ RTPrintf("No extra data exists\n");
+ }
+
+ if (extraData.isEmpty())
+ extraData = L"Das ist die Berliner Luft, Luft, Luft...";
+ else
+ extraData.setNull();
+ RTPrintf("Setting extra data key {%ls} to {%ls}...\n",
+ extraDataKey.raw(), extraData.raw());
+ CHECK_ERROR(machine, SetExtraData(extraDataKey.raw(), extraData.raw()));
+
+ if (SUCCEEDED(hrc)) {
+ RTPrintf("Getting extra data key {%ls} again...\n", extraDataKey.raw());
+ CHECK_ERROR_RET(machine, GetExtraData(extraDataKey.raw(), extraData.asOutParam()), hrc);
+ if (!extraData.isEmpty()) {
+ RTPrintf("Extra data value: {%ls}\n", extraData.raw());
+ } else {
+ RTPrintf("No extra data exists\n");
+ }
+ }
+
+ return hrc;
+}
+
+// main
+///////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Initialize the VBox runtime without loading
+ * the support driver.
+ */
+ RTR3InitExe(argc, &argv, 0);
+
+ HRESULT hrc;
+
+ {
+ char homeDir[RTPATH_MAX];
+ GetVBoxUserHomeDirectory(homeDir, sizeof(homeDir));
+ RTPrintf("VirtualBox Home Directory = '%s'\n", homeDir);
+ }
+
+ RTPrintf("Initializing COM...\n");
+
+ hrc = com::Initialize();
+ if (FAILED(hrc))
+ {
+ RTPrintf("ERROR: failed to initialize COM!\n");
+ return hrc;
+ }
+
+ do
+ {
+ // scopes all the stuff till shutdown
+ ////////////////////////////////////////////////////////////////////////////
+
+ ComPtr<IVirtualBoxClient> virtualBoxClient;
+ ComPtr<IVirtualBox> virtualBox;
+ ComPtr<ISession> session;
+
+#if 0
+ // Utf8Str test
+ ////////////////////////////////////////////////////////////////////////////
+
+ Utf8Str nullUtf8Str;
+ RTPrintf("nullUtf8Str='%s'\n", nullUtf8Str.raw());
+
+ Utf8Str simpleUtf8Str = "simpleUtf8Str";
+ RTPrintf("simpleUtf8Str='%s'\n", simpleUtf8Str.raw());
+
+ Utf8Str utf8StrFmt = Utf8StrFmt("[0=%d]%s[1=%d]", 0, "utf8StrFmt", 1);
+ RTPrintf("utf8StrFmt='%s'\n", utf8StrFmt.raw());
+
+#endif
+
+ RTPrintf("Creating VirtualBox object...\n");
+ hrc = virtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
+ if (SUCCEEDED(hrc))
+ hrc = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
+ if (FAILED(hrc))
+ RTPrintf("ERROR: failed to create the VirtualBox object!\n");
+ else
+ {
+ hrc = session.createInprocObject(CLSID_Session);
+ if (FAILED(hrc))
+ RTPrintf("ERROR: failed to create a session object!\n");
+ }
+
+ if (FAILED(hrc))
+ {
+ com::ErrorInfo info;
+ if (!info.isFullAvailable() && !info.isBasicAvailable())
+ {
+ com::GluePrintRCMessage(hrc);
+ RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
+ }
+ else
+ com::GluePrintErrorInfo(info);
+ break;
+ }
+
+#if 0
+ // Testing VirtualBox::COMGETTER(ProgressOperations).
+ // This is designed to be tested while running
+ // "./VBoxManage clonehd src.vdi clone.vdi" in parallel.
+ // It will then display the progress every 2 seconds.
+ ////////////////////////////////////////////////////////////////////////////
+ {
+ RTPrintf("Testing VirtualBox::COMGETTER(ProgressOperations)...\n");
+
+ for (;;) {
+ com::SafeIfaceArray<IProgress> operations;
+
+ CHECK_ERROR_BREAK(virtualBox,
+ COMGETTER(ProgressOperations)(ComSafeArrayAsOutParam(operations)));
+
+ RTPrintf("operations: %d\n", operations.size());
+ if (operations.size() == 0)
+ break; // No more operations left.
+
+ for (size_t i = 0; i < operations.size(); ++i) {
+ PRInt32 percent;
+
+ operations[i]->COMGETTER(Percent)(&percent);
+ RTPrintf("operations[%u]: %ld\n", (unsigned)i, (long)percent);
+ }
+ RTThreadSleep(2000); // msec
+ }
+ }
+#endif
+
+#if 0
+ // IUnknown identity test
+ ////////////////////////////////////////////////////////////////////////////
+ {
+ {
+ ComPtr<IVirtualBox> virtualBox2;
+
+ RTPrintf("Creating one more VirtualBox object...\n");
+ CHECK_RC(virtualBoxClient->COMGETTER(virtualBox2.asOutParam()));
+ if (FAILED(rc))
+ {
+ CHECK_ERROR_NOCALL();
+ break;
+ }
+
+ RTPrintf("IVirtualBox(virtualBox)=%p IVirtualBox(virtualBox2)=%p\n",
+ (IVirtualBox *)virtualBox, (IVirtualBox *)virtualBox2);
+ Assert((IVirtualBox *)virtualBox == (IVirtualBox *)virtualBox2);
+
+ ComPtr<IUnknown> unk(virtualBox);
+ ComPtr<IUnknown> unk2;
+ unk2 = virtualBox2;
+
+ RTPrintf("IUnknown(virtualBox)=%p IUnknown(virtualBox2)=%p\n",
+ (IUnknown *)unk, (IUnknown *)unk2);
+ Assert((IUnknown *)unk == (IUnknown *)unk2);
+
+ ComPtr<IVirtualBox> vb = unk;
+ ComPtr<IVirtualBox> vb2 = unk;
+
+ RTPrintf("IVirtualBox(IUnknown(virtualBox))=%p IVirtualBox(IUnknown(virtualBox2))=%p\n",
+ (IVirtualBox *)vb, (IVirtualBox *)vb2);
+ Assert((IVirtualBox *)vb == (IVirtualBox *)vb2);
+ }
+
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR_BREAK(virtualBox, COMGETTER(Host)(host.asOutParam()));
+ RTPrintf(" IHost(host)=%p\n", (IHost *)host);
+ ComPtr<IUnknown> unk = host;
+ RTPrintf(" IUnknown(host)=%p\n", (IUnknown *)unk);
+ ComPtr<IHost> host_copy = unk;
+ RTPrintf(" IHost(host_copy)=%p\n", (IHost *)host_copy);
+ ComPtr<IUnknown> unk_copy = host_copy;
+ RTPrintf(" IUnknown(host_copy)=%p\n", (IUnknown *)unk_copy);
+ Assert((IUnknown *)unk == (IUnknown *)unk_copy);
+
+ /* query IUnknown on IUnknown */
+ ComPtr<IUnknown> unk_copy_copy;
+ unk_copy.queryInterfaceTo(unk_copy_copy.asOutParam());
+ RTPrintf(" IUnknown(unk_copy)=%p\n", (IUnknown *)unk_copy_copy);
+ Assert((IUnknown *)unk_copy == (IUnknown *)unk_copy_copy);
+ /* query IUnknown on IUnknown in the opposite direction */
+ unk_copy_copy.queryInterfaceTo(unk_copy.asOutParam());
+ RTPrintf(" IUnknown(unk_copy_copy)=%p\n", (IUnknown *)unk_copy);
+ Assert((IUnknown *)unk_copy == (IUnknown *)unk_copy_copy);
+
+ /* query IUnknown again after releasing all previous IUnknown instances
+ * but keeping IHost -- it should remain the same (Identity Rule) */
+ IUnknown *oldUnk = unk;
+ unk.setNull();
+ unk_copy.setNull();
+ unk_copy_copy.setNull();
+ unk = host;
+ RTPrintf(" IUnknown(host)=%p\n", (IUnknown *)unk);
+ Assert(oldUnk == (IUnknown *)unk);
+ }
+
+// RTPrintf("Will be now released (press Enter)...");
+// getchar();
+ }
+#endif
+
+#if 0
+ // the simplest COM API test
+ ////////////////////////////////////////////////////////////////////////////
+ {
+ Bstr version;
+ CHECK_ERROR_BREAK(virtualBox, COMGETTER(Version)(version.asOutParam()));
+ RTPrintf("VirtualBox version = %ls\n", version.raw());
+ }
+#endif
+
+#if 0
+ // Array test
+ ////////////////////////////////////////////////////////////////////////////
+ {
+ RTPrintf("Calling IVirtualBox::Machines...\n");
+
+ com::SafeIfaceArray<IMachine> machines;
+ CHECK_ERROR_BREAK(virtualBox,
+ COMGETTER(Machines)(ComSafeArrayAsOutParam(machines)));
+
+ RTPrintf("%u machines registered (machines.isNull()=%d).\n",
+ machines.size(), machines.isNull());
+
+ for (size_t i = 0; i < machines.size(); ++ i)
+ {
+ Bstr name;
+ CHECK_ERROR_BREAK(machines[i], COMGETTER(Name)(name.asOutParam()));
+ RTPrintf("machines[%u]='%s'\n", i, Utf8Str(name).raw());
+ }
+
+#if 0
+ {
+ RTPrintf("Testing [out] arrays...\n");
+ com::SafeGUIDArray uuids;
+ CHECK_ERROR_BREAK(virtualBox,
+ COMGETTER(Uuids)(ComSafeArrayAsOutParam(uuids)));
+
+ for (size_t i = 0; i < uuids.size(); ++ i)
+ RTPrintf("uuids[%u]=%RTuuid\n", i, &uuids[i]);
+ }
+
+ {
+ RTPrintf("Testing [in] arrays...\n");
+ com::SafeGUIDArray uuids(5);
+ for (size_t i = 0; i < uuids.size(); ++ i)
+ {
+ Guid id;
+ id.create();
+ uuids[i] = id;
+ RTPrintf("uuids[%u]=%RTuuid\n", i, &uuids[i]);
+ }
+
+ CHECK_ERROR_BREAK(virtualBox,
+ SetUuids(ComSafeArrayAsInParam(uuids)));
+ }
+#endif
+
+ }
+#endif
+
+#if 0
+ // some outdated stuff
+ ////////////////////////////////////////////////////////////////////////////
+
+ RTPrintf("Getting IHost interface...\n");
+ IHost *host;
+ rc = virtualBox->GetHost(&host);
+ if (SUCCEEDED(rc))
+ {
+ IHostDVDDriveCollection *dvdColl;
+ rc = host->GetHostDVDDrives(&dvdColl);
+ if (SUCCEEDED(rc))
+ {
+ IHostDVDDrive *dvdDrive = NULL;
+ dvdColl->GetNextHostDVDDrive(dvdDrive, &dvdDrive);
+ while (dvdDrive)
+ {
+ BSTR driveName;
+ char *driveNameUtf8;
+ dvdDrive->GetDriveName(&driveName);
+ RTUtf16ToUtf8((PCRTUTF16)driveName, &driveNameUtf8);
+ RTPrintf("Host DVD drive name: %s\n", driveNameUtf8);
+ RTStrFree(driveNameUtf8);
+ SysFreeString(driveName);
+ IHostDVDDrive *dvdDriveTemp = dvdDrive;
+ dvdColl->GetNextHostDVDDrive(dvdDriveTemp, &dvdDrive);
+ dvdDriveTemp->Release();
+ }
+ dvdColl->Release();
+ } else
+ {
+ RTPrintf("Could not get host DVD drive collection\n");
+ }
+
+ IHostFloppyDriveCollection *floppyColl;
+ rc = host->GetHostFloppyDrives(&floppyColl);
+ if (SUCCEEDED(rc))
+ {
+ IHostFloppyDrive *floppyDrive = NULL;
+ floppyColl->GetNextHostFloppyDrive(floppyDrive, &floppyDrive);
+ while (floppyDrive)
+ {
+ BSTR driveName;
+ char *driveNameUtf8;
+ floppyDrive->GetDriveName(&driveName);
+ RTUtf16ToUtf8((PCRTUTF16)driveName, &driveNameUtf8);
+ RTPrintf("Host floppy drive name: %s\n", driveNameUtf8);
+ RTStrFree(driveNameUtf8);
+ SysFreeString(driveName);
+ IHostFloppyDrive *floppyDriveTemp = floppyDrive;
+ floppyColl->GetNextHostFloppyDrive(floppyDriveTemp, &floppyDrive);
+ floppyDriveTemp->Release();
+ }
+ floppyColl->Release();
+ } else
+ {
+ RTPrintf("Could not get host floppy drive collection\n");
+ }
+ host->Release();
+ } else
+ {
+ RTPrintf("Call failed\n");
+ }
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // IVirtualBoxErrorInfo test
+ ////////////////////////////////////////////////////////////////////////////
+ {
+ // RPC calls
+
+ // call a method that will definitely fail
+ Guid uuid;
+ ComPtr<IHardDisk> hardDisk;
+ rc = virtualBox->GetHardDisk(uuid, hardDisk.asOutParam());
+ RTPrintf("virtualBox->GetHardDisk(null-uuid)=%08X\n", rc);
+
+// {
+// com::ErrorInfo info(virtualBox);
+// PRINT_ERROR_INFO(info);
+// }
+
+ // call a method that will definitely succeed
+ Bstr version;
+ rc = virtualBox->COMGETTER(Version)(version.asOutParam());
+ RTPrintf("virtualBox->COMGETTER(Version)=%08X\n", rc);
+
+ {
+ com::ErrorInfo info(virtualBox);
+ PRINT_ERROR_INFO(info);
+ }
+
+ // Local calls
+
+ // call a method that will definitely fail
+ ComPtr<IMachine> machine;
+ rc = session->COMGETTER(Machine)(machine.asOutParam());
+ RTPrintf("session->COMGETTER(Machine)=%08X\n", rc);
+
+// {
+// com::ErrorInfo info(virtualBox);
+// PRINT_ERROR_INFO(info);
+// }
+
+ // call a method that will definitely succeed
+ SessionState_T state;
+ rc = session->COMGETTER(State)(&state);
+ RTPrintf("session->COMGETTER(State)=%08X\n", rc);
+
+ {
+ com::ErrorInfo info(virtualBox);
+ PRINT_ERROR_INFO(info);
+ }
+ }
+#endif
+
+#if 0
+ // register the existing hard disk image
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ ComPtr<IHardDisk> hd;
+ Bstr src = L"E:\\develop\\innotek\\images\\NewHardDisk.vdi";
+ RTPrintf("Opening the existing hard disk '%ls'...\n", src.raw());
+ CHECK_ERROR_BREAK(virtualBox, OpenHardDisk(src, AccessMode_ReadWrite, hd.asOutParam()));
+ RTPrintf("Enter to continue...\n");
+ getchar();
+ RTPrintf("Registering the existing hard disk '%ls'...\n", src.raw());
+ CHECK_ERROR_BREAK(virtualBox, RegisterHardDisk(hd));
+ RTPrintf("Enter to continue...\n");
+ getchar();
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // find and unregister the existing hard disk image
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ ComPtr<IVirtualDiskImage> vdi;
+ Bstr src = L"CreatorTest.vdi";
+ RTPrintf("Unregistering the hard disk '%ls'...\n", src.raw());
+ CHECK_ERROR_BREAK(virtualBox, FindVirtualDiskImage(src, vdi.asOutParam()));
+ ComPtr<IHardDisk> hd = vdi;
+ Guid id;
+ CHECK_ERROR_BREAK(hd, COMGETTER(Id)(id.asOutParam()));
+ CHECK_ERROR_BREAK(virtualBox, UnregisterHardDisk(id, hd.asOutParam()));
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // clone the registered hard disk
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+#if defined RT_OS_LINUX
+ Bstr src = L"/mnt/hugaida/common/develop/innotek/images/freedos-linux.vdi";
+#else
+ Bstr src = L"E:/develop/innotek/images/freedos.vdi";
+#endif
+ Bstr dst = L"./clone.vdi";
+ RTPrintf("Cloning '%ls' to '%ls'...\n", src.raw(), dst.raw());
+ ComPtr<IVirtualDiskImage> vdi;
+ CHECK_ERROR_BREAK(virtualBox, FindVirtualDiskImage(src, vdi.asOutParam()));
+ ComPtr<IHardDisk> hd = vdi;
+ ComPtr<IProgress> progress;
+ CHECK_ERROR_BREAK(hd, CloneToImage(dst, vdi.asOutParam(), progress.asOutParam()));
+ RTPrintf("Waiting for completion...\n");
+ CHECK_ERROR_BREAK(progress, WaitForCompletion(-1));
+ ProgressErrorInfo ei(progress);
+ if (FAILED(ei.getResultCode()))
+ {
+ PRINT_ERROR_INFO(ei);
+ }
+ else
+ {
+ vdi->COMGETTER(FilePath)(dst.asOutParam());
+ RTPrintf("Actual clone path is '%ls'\n", dst.raw());
+ }
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // find a registered hard disk by location and get properties
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ ComPtr<IHardDisk> hd;
+ static const wchar_t *Names[] =
+ {
+#ifndef RT_OS_LINUX
+ L"freedos.vdi",
+ L"MS-DOS.vmdk",
+ L"iscsi",
+ L"some/path/and/disk.vdi",
+#else
+ L"xp.vdi",
+ L"Xp.vDI",
+#endif
+ };
+
+ RTPrintf("\n");
+
+ for (size_t i = 0; i < RT_ELEMENTS(Names); ++ i)
+ {
+ Bstr src = Names[i];
+ RTPrintf("Searching for hard disk '%ls'...\n", src.raw());
+ rc = virtualBox->FindHardDisk(src, hd.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ Guid id;
+ Bstr location;
+ CHECK_ERROR_BREAK(hd, COMGETTER(Id)(id.asOutParam()));
+ CHECK_ERROR_BREAK(hd, COMGETTER(Location)(location.asOutParam()));
+ RTPrintf("Found, UUID={%RTuuid}, location='%ls'.\n",
+ id.raw(), location.raw());
+
+ com::SafeArray<BSTR> names;
+ com::SafeArray<BSTR> values;
+
+ CHECK_ERROR_BREAK(hd, GetProperties(NULL,
+ ComSafeArrayAsOutParam(names),
+ ComSafeArrayAsOutParam(values)));
+
+ RTPrintf("Properties:\n");
+ for (size_t i = 0; i < names.size(); ++ i)
+ RTPrintf(" %ls = %ls\n", names[i], values[i]);
+
+ if (names.size() == 0)
+ RTPrintf(" <none>\n");
+
+#if 0
+ Bstr name("TargetAddress");
+ Bstr value = Utf8StrFmt("lalala (%llu)", RTTimeMilliTS());
+
+ RTPrintf("Settings property %ls to %ls...\n", name.raw(), value.raw());
+ CHECK_ERROR(hd, SetProperty(name, value));
+#endif
+ }
+ else
+ {
+ com::ErrorInfo info(virtualBox);
+ PRINT_ERROR_INFO(info);
+ }
+ RTPrintf("\n");
+ }
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // access the machine in read-only mode
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ ComPtr<IMachine> machine;
+ Bstr name = argc > 1 ? argv[1] : "dos";
+ RTPrintf("Getting a machine object named '%ls'...\n", name.raw());
+ CHECK_ERROR_BREAK(virtualBox, FindMachine(name, machine.asOutParam()));
+ RTPrintf("Accessing the machine in read-only mode:\n");
+ readAndChangeMachineSettings(machine);
+ }
+ while (0);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // create a new machine (w/o registering it)
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ ComPtr<IMachine> machine;
+#if defined(RT_OS_LINUX)
+ Bstr baseDir = L"/tmp/vbox";
+#else
+ Bstr baseDir = L"C:\\vbox";
+#endif
+ Bstr name = L"machina";
+
+ RTPrintf("Creating a new machine object(base dir '%ls', name '%ls')...\n",
+ baseDir.raw(), name.raw());
+ CHECK_ERROR_BREAK(virtualBox, CreateMachine(name, L"", baseDir, L"",
+ false,
+ machine.asOutParam()));
+
+ RTPrintf("Getting name...\n");
+ CHECK_ERROR_BREAK(machine, COMGETTER(Name)(name.asOutParam()));
+ RTPrintf("Name: {%ls}\n", name.raw());
+
+ BOOL modified = FALSE;
+ RTPrintf("Are any settings modified?...\n");
+ CHECK_ERROR_BREAK(machine, COMGETTER(SettingsModified)(&modified));
+ RTPrintf("%s\n", modified ? "yes" : "no");
+
+ ASSERT_BREAK(modified == TRUE);
+
+ name = L"Kakaya prekrasnaya virtual'naya mashina!";
+ RTPrintf("Setting new name ({%ls})...\n", name.raw());
+ CHECK_ERROR_BREAK(machine, COMSETTER(Name)(name));
+
+ RTPrintf("Setting memory size to 111...\n");
+ CHECK_ERROR_BREAK(machine, COMSETTER(MemorySize)(111));
+
+ Bstr desc = L"This is an exemplary description.";
+ RTPrintf("Setting description to \"%ls\"...\n", desc.raw());
+ CHECK_ERROR_BREAK(machine, COMSETTER(Description)(desc));
+
+ ComPtr<IGuestOSType> guestOSType;
+ Bstr type = L"os2warp45";
+ CHECK_ERROR_BREAK(virtualBox, GetGuestOSType(type, guestOSType.asOutParam()));
+
+ RTPrintf("Saving new machine settings...\n");
+ CHECK_ERROR_BREAK(machine, SaveSettings());
+
+ RTPrintf("Accessing the newly created machine:\n");
+ readAndChangeMachineSettings(machine);
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // enumerate host DVD drives
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ ComPtr<IHost> host;
+ CHECK_RC_BREAK(virtualBox->COMGETTER(Host)(host.asOutParam()));
+
+ {
+ ComPtr<IHostDVDDriveCollection> coll;
+ CHECK_RC_BREAK(host->COMGETTER(DVDDrives)(coll.asOutParam()));
+ ComPtr<IHostDVDDriveEnumerator> enumerator;
+ CHECK_RC_BREAK(coll->Enumerate(enumerator.asOutParam()));
+ BOOL hasmore;
+ while (SUCCEEDED(enumerator->HasMore(&hasmore)) && hasmore)
+ {
+ ComPtr<IHostDVDDrive> drive;
+ CHECK_RC_BREAK(enumerator->GetNext(drive.asOutParam()));
+ Bstr name;
+ CHECK_RC_BREAK(drive->COMGETTER(Name)(name.asOutParam()));
+ RTPrintf("Host DVD drive: name={%ls}\n", name.raw());
+ }
+ CHECK_RC_BREAK(rc);
+
+ ComPtr<IHostDVDDrive> drive;
+ CHECK_ERROR(enumerator, GetNext(drive.asOutParam()));
+ CHECK_ERROR(coll, GetItemAt(1000, drive.asOutParam()));
+ CHECK_ERROR(coll, FindByName(Bstr("R:"), drive.asOutParam()));
+ if (SUCCEEDED(rc))
+ {
+ Bstr name;
+ CHECK_RC_BREAK(drive->COMGETTER(Name)(name.asOutParam()));
+ RTPrintf("Found by name: name={%ls}\n", name.raw());
+ }
+ }
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // check for available hd backends
+ ///////////////////////////////////////////////////////////////////////////
+ {
+ RTPrintf("Supported hard disk backends: --------------------------\n");
+ ComPtr<ISystemProperties> systemProperties;
+ CHECK_ERROR_BREAK(virtualBox,
+ COMGETTER(SystemProperties)(systemProperties.asOutParam()));
+ com::SafeIfaceArray<IHardDiskFormat> hardDiskFormats;
+ CHECK_ERROR_BREAK(systemProperties,
+ COMGETTER(HardDiskFormats)(ComSafeArrayAsOutParam(hardDiskFormats)));
+
+ for (size_t i = 0; i < hardDiskFormats.size(); ++ i)
+ {
+ /* General information */
+ Bstr id;
+ CHECK_ERROR_BREAK(hardDiskFormats[i],
+ COMGETTER(Id)(id.asOutParam()));
+
+ Bstr description;
+ CHECK_ERROR_BREAK(hardDiskFormats[i],
+ COMGETTER(Id)(description.asOutParam()));
+
+ ULONG caps;
+ CHECK_ERROR_BREAK(hardDiskFormats[i],
+ COMGETTER(Capabilities)(&caps));
+
+ RTPrintf("Backend %u: id='%ls' description='%ls' capabilities=%#06x extensions='",
+ i, id.raw(), description.raw(), caps);
+
+ /* File extensions */
+ com::SafeArray<BSTR> fileExtensions;
+ CHECK_ERROR_BREAK(hardDiskFormats[i],
+ COMGETTER(FileExtensions)(ComSafeArrayAsOutParam(fileExtensions)));
+ for (size_t a = 0; a < fileExtensions.size(); ++ a)
+ {
+ RTPrintf("%ls", Bstr(fileExtensions[a]).raw());
+ if (a != fileExtensions.size()-1)
+ RTPrintf(",");
+ }
+ RTPrintf("'");
+
+ /* Configuration keys */
+ com::SafeArray<BSTR> propertyNames;
+ com::SafeArray<BSTR> propertyDescriptions;
+ com::SafeArray<ULONG> propertyTypes;
+ com::SafeArray<ULONG> propertyFlags;
+ com::SafeArray<BSTR> propertyDefaults;
+ CHECK_ERROR_BREAK(hardDiskFormats[i],
+ DescribeProperties(ComSafeArrayAsOutParam(propertyNames),
+ ComSafeArrayAsOutParam(propertyDescriptions),
+ ComSafeArrayAsOutParam(propertyTypes),
+ ComSafeArrayAsOutParam(propertyFlags),
+ ComSafeArrayAsOutParam(propertyDefaults)));
+
+ RTPrintf(" config=(");
+ if (propertyNames.size() > 0)
+ {
+ for (size_t a = 0; a < propertyNames.size(); ++ a)
+ {
+ RTPrintf("key='%ls' desc='%ls' type=", Bstr(propertyNames[a]).raw(), Bstr(propertyDescriptions[a]).raw());
+ switch (propertyTypes[a])
+ {
+ case DataType_Int32Type: RTPrintf("int"); break;
+ case DataType_Int8Type: RTPrintf("byte"); break;
+ case DataType_StringType: RTPrintf("string"); break;
+ }
+ RTPrintf(" flags=%#04x", propertyFlags[a]);
+ RTPrintf(" default='%ls'", Bstr(propertyDefaults[a]).raw());
+ if (a != propertyNames.size()-1)
+ RTPrintf(",");
+ }
+ }
+ RTPrintf(")\n");
+ }
+ RTPrintf("-------------------------------------------------------\n");
+ }
+#endif
+
+#if 0
+ // enumerate hard disks & dvd images
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ {
+ com::SafeIfaceArray<IHardDisk> disks;
+ CHECK_ERROR_BREAK(virtualBox,
+ COMGETTER(HardDisks)(ComSafeArrayAsOutParam(disks)));
+
+ RTPrintf("%u base hard disks registered (disks.isNull()=%d).\n",
+ disks.size(), disks.isNull());
+
+ for (size_t i = 0; i < disks.size(); ++ i)
+ {
+ Bstr loc;
+ CHECK_ERROR_BREAK(disks[i], COMGETTER(Location)(loc.asOutParam()));
+ Guid id;
+ CHECK_ERROR_BREAK(disks[i], COMGETTER(Id)(id.asOutParam()));
+ MediaState_T state;
+ CHECK_ERROR_BREAK(disks[i], COMGETTER(State)(&state));
+ Bstr format;
+ CHECK_ERROR_BREAK(disks[i], COMGETTER(Format)(format.asOutParam()));
+
+ RTPrintf(" disks[%u]: '%ls'\n"
+ " UUID: {%RTuuid}\n"
+ " State: %s\n"
+ " Format: %ls\n",
+ i, loc.raw(), id.raw(),
+ state == MediaState_NotCreated ? "Not Created" :
+ state == MediaState_Created ? "Created" :
+ state == MediaState_Inaccessible ? "Inaccessible" :
+ state == MediaState_LockedRead ? "Locked Read" :
+ state == MediaState_LockedWrite ? "Locked Write" :
+ "???",
+ format.raw());
+
+ if (state == MediaState_Inaccessible)
+ {
+ Bstr error;
+ CHECK_ERROR_BREAK(disks[i],
+ COMGETTER(LastAccessError)(error.asOutParam()));
+ RTPrintf(" Access Error: %ls\n", error.raw());
+ }
+
+ /* get usage */
+
+ RTPrintf(" Used by VMs:\n");
+
+ com::SafeGUIDArray ids;
+ CHECK_ERROR_BREAK(disks[i],
+ COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids)));
+ if (ids.size() == 0)
+ {
+ RTPrintf(" <not used>\n");
+ }
+ else
+ {
+ for (size_t j = 0; j < ids.size(); ++ j)
+ {
+ RTPrintf(" {%RTuuid}\n", &ids[j]);
+ }
+ }
+ }
+ }
+ {
+ com::SafeIfaceArray<IDVDImage> images;
+ CHECK_ERROR_BREAK(virtualBox,
+ COMGETTER(DVDImages)(ComSafeArrayAsOutParam(images)));
+
+ RTPrintf("%u DVD images registered (images.isNull()=%d).\n",
+ images.size(), images.isNull());
+
+ for (size_t i = 0; i < images.size(); ++ i)
+ {
+ Bstr loc;
+ CHECK_ERROR_BREAK(images[i], COMGETTER(Location)(loc.asOutParam()));
+ Guid id;
+ CHECK_ERROR_BREAK(images[i], COMGETTER(Id)(id.asOutParam()));
+ MediaState_T state;
+ CHECK_ERROR_BREAK(images[i], COMGETTER(State)(&state));
+
+ RTPrintf(" images[%u]: '%ls'\n"
+ " UUID: {%RTuuid}\n"
+ " State: %s\n",
+ i, loc.raw(), id.raw(),
+ state == MediaState_NotCreated ? "Not Created" :
+ state == MediaState_Created ? "Created" :
+ state == MediaState_Inaccessible ? "Inaccessible" :
+ state == MediaState_LockedRead ? "Locked Read" :
+ state == MediaState_LockedWrite ? "Locked Write" :
+ "???");
+
+ if (state == MediaState_Inaccessible)
+ {
+ Bstr error;
+ CHECK_ERROR_BREAK(images[i],
+ COMGETTER(LastAccessError)(error.asOutParam()));
+ RTPrintf(" Access Error: %ls\n", error.raw());
+ }
+
+ /* get usage */
+
+ RTPrintf(" Used by VMs:\n");
+
+ com::SafeGUIDArray ids;
+ CHECK_ERROR_BREAK(images[i],
+ COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids)));
+ if (ids.size() == 0)
+ {
+ RTPrintf(" <not used>\n");
+ }
+ else
+ {
+ for (size_t j = 0; j < ids.size(); ++ j)
+ {
+ RTPrintf(" {%RTuuid}\n", &ids[j]);
+ }
+ }
+ }
+ }
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // open a (direct) session
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ ComPtr<IMachine> machine;
+ Bstr name = argc > 1 ? argv[1] : "dos";
+ RTPrintf("Getting a machine object named '%ls'...\n", name.raw());
+ CHECK_ERROR_BREAK(virtualBox, FindMachine(name, machine.asOutParam()));
+ Guid guid;
+ CHECK_RC_BREAK(machine->COMGETTER(Id)(guid.asOutParam()));
+ RTPrintf("Opening a session for this machine...\n");
+ CHECK_RC_BREAK(virtualBox->OpenSession(session, guid));
+#if 1
+ ComPtr<IMachine> sessionMachine;
+ RTPrintf("Getting machine session object...\n");
+ CHECK_RC_BREAK(session->COMGETTER(Machine)(sessionMachine.asOutParam()));
+ RTPrintf("Accessing the machine within the session:\n");
+ readAndChangeMachineSettings(sessionMachine, machine);
+#if 0
+ RTPrintf("\n");
+ RTPrintf("Enabling the VRDE server (must succeed even if the VM is saved):\n");
+ ComPtr<IVRDEServer> vrdeServer;
+ CHECK_ERROR_BREAK(sessionMachine, COMGETTER(VRDEServer)(vrdeServer.asOutParam()));
+ if (FAILED(vrdeServer->COMSETTER(Enabled)(TRUE)))
+ {
+ PRINT_ERROR_INFO(com::ErrorInfo(vrdeServer));
+ }
+ else
+ {
+ BOOL enabled = FALSE;
+ CHECK_ERROR_BREAK(vrdeServer, COMGETTER(Enabled)(&enabled));
+ RTPrintf("VRDE server is %s\n", enabled ? "enabled" : "disabled");
+ }
+#endif
+#endif
+#if 0
+ ComPtr<IConsole> console;
+ RTPrintf("Getting the console object...\n");
+ CHECK_RC_BREAK(session->COMGETTER(Console)(console.asOutParam()));
+ RTPrintf("Discarding the current machine state...\n");
+ ComPtr<IProgress> progress;
+ CHECK_ERROR_BREAK(console, DiscardCurrentState(progress.asOutParam()));
+ RTPrintf("Waiting for completion...\n");
+ CHECK_ERROR_BREAK(progress, WaitForCompletion(-1));
+ ProgressErrorInfo ei(progress);
+ if (FAILED(ei.getResultCode()))
+ {
+ PRINT_ERROR_INFO(ei);
+
+ ComPtr<IUnknown> initiator;
+ CHECK_ERROR_BREAK(progress, COMGETTER(Initiator)(initiator.asOutParam()));
+
+ RTPrintf("initiator(unk) = %p\n", (IUnknown *)initiator);
+ RTPrintf("console(unk) = %p\n", (IUnknown *)ComPtr<IUnknown>((IConsole *)console));
+ RTPrintf("console = %p\n", (IConsole *)console);
+ }
+#endif
+ RTPrintf("Press enter to close session...");
+ getchar();
+ session->Close();
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // open a remote session
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ ComPtr<IMachine> machine;
+ Bstr name = L"dos";
+ RTPrintf("Getting a machine object named '%ls'...\n", name.raw());
+ CHECK_RC_BREAK(virtualBox->FindMachine(name, machine.asOutParam()));
+ Guid guid;
+ CHECK_RC_BREAK(machine->COMGETTER(Id)(guid.asOutParam()));
+ RTPrintf("Opening a remote session for this machine...\n");
+ ComPtr<IProgress> progress;
+ CHECK_RC_BREAK(virtualBox->OpenRemoteSession(session, guid, Bstr("gui"),
+ NULL, progress.asOutParam()));
+ RTPrintf("Waiting for the session to open...\n");
+ CHECK_RC_BREAK(progress->WaitForCompletion(-1));
+ ComPtr<IMachine> sessionMachine;
+ RTPrintf("Getting machine session object...\n");
+ CHECK_RC_BREAK(session->COMGETTER(Machine)(sessionMachine.asOutParam()));
+ ComPtr<IConsole> console;
+ RTPrintf("Getting console object...\n");
+ CHECK_RC_BREAK(session->COMGETTER(Console)(console.asOutParam()));
+ RTPrintf("Press enter to pause the VM execution in the remote session...");
+ getchar();
+ CHECK_RC(console->Pause());
+ RTPrintf("Press enter to close this session...");
+ getchar();
+ session->Close();
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 0
+ // open an existing remote session
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ ComPtr<IMachine> machine;
+ Bstr name = "dos";
+ RTPrintf("Getting a machine object named '%ls'...\n", name.raw());
+ CHECK_RC_BREAK(virtualBox->FindMachine(name, machine.asOutParam()));
+ Guid guid;
+ CHECK_RC_BREAK(machine->COMGETTER(Id)(guid.asOutParam()));
+ RTPrintf("Opening an existing remote session for this machine...\n");
+ CHECK_RC_BREAK(virtualBox->OpenExistingSession(session, guid));
+ ComPtr<IMachine> sessionMachine;
+ RTPrintf("Getting machine session object...\n");
+ CHECK_RC_BREAK(session->COMGETTER(Machine)(sessionMachine.asOutParam()));
+#if 0
+ ComPtr<IConsole> console;
+ RTPrintf("Getting console object...\n");
+ CHECK_RC_BREAK(session->COMGETTER(Console)(console.asOutParam()));
+ RTPrintf("Press enter to pause the VM execution in the remote session...");
+ getchar();
+ CHECK_RC(console->Pause());
+ RTPrintf("Press enter to close this session...");
+ getchar();
+#endif
+ session->Close();
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+
+#if 1
+ do {
+ // Get host
+ ComPtr<IHost> host;
+ CHECK_ERROR_BREAK(virtualBox, COMGETTER(Host)(host.asOutParam()));
+
+ ULONG uMemSize, uMemAvail;
+ CHECK_ERROR_BREAK(host, COMGETTER(MemorySize)(&uMemSize));
+ RTPrintf("Total memory (MB): %u\n", uMemSize);
+ CHECK_ERROR_BREAK(host, COMGETTER(MemoryAvailable)(&uMemAvail));
+ RTPrintf("Free memory (MB): %u\n", uMemAvail);
+ } while (0);
+#endif
+
+#if 0
+ do {
+ // Get host
+ ComPtr<IHost> host;
+ CHECK_ERROR_BREAK(virtualBox, COMGETTER(Host)(host.asOutParam()));
+
+ com::SafeIfaceArray<IHostNetworkInterface> hostNetworkInterfaces;
+ CHECK_ERROR_BREAK(host,
+ COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(hostNetworkInterfaces)));
+ if (hostNetworkInterfaces.size() > 0)
+ {
+ ComPtr<IHostNetworkInterface> networkInterface = hostNetworkInterfaces[0];
+ Bstr interfaceName;
+ networkInterface->COMGETTER(Name)(interfaceName.asOutParam());
+ RTPrintf("Found %d network interfaces, testing with %ls...\n", hostNetworkInterfaces.size(), interfaceName.raw());
+ Bstr interfaceGuid;
+ networkInterface->COMGETTER(Id)(interfaceGuid.asOutParam());
+ // Find the interface by its name
+ networkInterface.setNull();
+ CHECK_ERROR_BREAK(host,
+ FindHostNetworkInterfaceByName(interfaceName.raw(), networkInterface.asOutParam()));
+ Bstr interfaceGuid2;
+ networkInterface->COMGETTER(Id)(interfaceGuid2.asOutParam());
+ if (interfaceGuid2 != interfaceGuid)
+ RTPrintf("Failed to retrieve an interface by name %ls.\n", interfaceName.raw());
+ // Find the interface by its guid
+ networkInterface.setNull();
+ CHECK_ERROR_BREAK(host,
+ FindHostNetworkInterfaceById(interfaceGuid.raw(), networkInterface.asOutParam()));
+ Bstr interfaceName2;
+ networkInterface->COMGETTER(Name)(interfaceName2.asOutParam());
+ if (interfaceName != interfaceName2)
+ RTPrintf("Failed to retrieve an interface by GUID %ls.\n", interfaceGuid.raw());
+ }
+ else
+ {
+ RTPrintf("No network interfaces found!\n");
+ }
+ } while (0);
+#endif
+
+#if 0
+ // DNS & Co.
+ ///////////////////////////////////////////////////////////////////////////
+ /* XXX: Note it's endless loop */
+ do
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR_BREAK(virtualBox, COMGETTER(Host)(host.asOutParam()));
+
+ {
+ Bstr domainName;
+ CHECK_ERROR_BREAK(host,COMGETTER(DomainName)(domainName.asOutParam()));
+ RTPrintf("Domain name: %ls\n", domainName.raw());
+ }
+
+ com::SafeArray<BSTR> strs;
+ CHECK_ERROR_BREAK(host, COMGETTER(NameServers)(ComSafeArrayAsOutParam(strs)));
+
+ unsigned int i;
+ for (i = 0; i < strs.size(); ++i)
+ RTPrintf("Name server[%d]:%s\n", i, com::Utf8Str(strs[i]).c_str());
+
+ RTThreadSleep(1000);
+ }
+ while (1);
+ RTPrintf("\n");
+#endif
+
+
+#if 0 && defined(VBOX_WITH_RESOURCE_USAGE_API)
+ do {
+ // Get collector
+ ComPtr<IPerformanceCollector> collector;
+ CHECK_ERROR_BREAK(virtualBox,
+ COMGETTER(PerformanceCollector)(collector.asOutParam()));
+
+
+ // Fill base metrics array
+ Bstr baseMetricNames[] = { L"Net/eth0/Load" };
+ com::SafeArray<BSTR> baseMetrics(1);
+ baseMetricNames[0].cloneTo(&baseMetrics[0]);
+
+ // Get host
+ ComPtr<IHost> host;
+ CHECK_ERROR_BREAK(virtualBox, COMGETTER(Host)(host.asOutParam()));
+
+ // Get host network interfaces
+ // com::SafeIfaceArray<IHostNetworkInterface> hostNetworkInterfaces;
+ // CHECK_ERROR_BREAK(host,
+ // COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(hostNetworkInterfaces)));
+
+ // Setup base metrics
+ // Note that one needs to set up metrics after a session is open for a machine.
+ com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
+ com::SafeIfaceArray<IUnknown> objects(1);
+ host.queryInterfaceTo(&objects[0]);
+ CHECK_ERROR_BREAK(collector, SetupMetrics(ComSafeArrayAsInParam(baseMetrics),
+ ComSafeArrayAsInParam(objects), 1u, 10u,
+ ComSafeArrayAsOutParam(affectedMetrics)));
+ listAffectedMetrics(virtualBox,
+ ComSafeArrayAsInParam(affectedMetrics));
+ affectedMetrics.setNull();
+
+ RTPrintf("Sleeping for 5 seconds...\n");
+ RTThreadSleep(5000); // Sleep for 5 seconds
+
+ RTPrintf("\nMetrics collected: --------------------\n");
+ queryMetrics(virtualBox, collector, ComSafeArrayAsInParam(objects));
+ } while (false);
+#endif /* VBOX_WITH_RESOURCE_USAGE_API */
+#if 0 && defined(VBOX_WITH_RESOURCE_USAGE_API)
+ do {
+ // Get collector
+ ComPtr<IPerformanceCollector> collector;
+ CHECK_ERROR_BREAK(virtualBox,
+ COMGETTER(PerformanceCollector)(collector.asOutParam()));
+
+
+ // Fill base metrics array
+ //Bstr baseMetricNames[] = { L"CPU/Load,RAM/Usage" };
+ Bstr baseMetricNames[] = { L"RAM/VMM" };
+ com::SafeArray<BSTR> baseMetrics(1);
+ baseMetricNames[0].cloneTo(&baseMetrics[0]);
+
+ // Get host
+ ComPtr<IHost> host;
+ CHECK_ERROR_BREAK(virtualBox, COMGETTER(Host)(host.asOutParam()));
+
+ // Get machine
+ ComPtr<IMachine> machine;
+ Bstr name = argc > 1 ? argv[1] : "dsl";
+ Bstr sessionType = argc > 2 ? argv[2] : "headless";
+ RTPrintf("Getting a machine object named '%ls'...\n", name.raw());
+ CHECK_ERROR_BREAK(virtualBox, FindMachine(name.raw(), machine.asOutParam()));
+
+ // Open session
+ ComPtr<IProgress> progress;
+ RTPrintf("Launching VM process...\n");
+ CHECK_ERROR_BREAK(machine, LaunchVMProcess(session, sessionType.raw(),
+ ComSafeArrayNullInParam(), progress.asOutParam()));
+ RTPrintf("Waiting for the VM to power on...\n");
+ CHECK_ERROR_BREAK(progress, WaitForCompletion(-1));
+
+ // ComPtr<IMachine> sessionMachine;
+ // RTPrintf("Getting machine session object...\n");
+ // CHECK_ERROR_BREAK(session, COMGETTER(Machine)(sessionMachine.asOutParam()));
+
+ // Setup base metrics
+ // Note that one needs to set up metrics after a session is open for a machine.
+ com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
+ com::SafeIfaceArray<IUnknown> objects(1);
+ host.queryInterfaceTo(&objects[0]);
+ //machine.queryInterfaceTo(&objects[1]);
+ CHECK_ERROR_BREAK(collector, SetupMetrics(ComSafeArrayAsInParam(baseMetrics),
+ ComSafeArrayAsInParam(objects), 1u, 10u,
+ ComSafeArrayAsOutParam(affectedMetrics)));
+ listAffectedMetrics(virtualBox,
+ ComSafeArrayAsInParam(affectedMetrics));
+ affectedMetrics.setNull();
+
+ // Get console
+ ComPtr<IConsole> console;
+ RTPrintf("Getting console object...\n");
+ CHECK_ERROR_BREAK(session, COMGETTER(Console)(console.asOutParam()));
+
+ RTThreadSleep(5000); // Sleep for 5 seconds
+
+ RTPrintf("\nMetrics collected with VM running: --------------------\n");
+ queryMetrics(virtualBox, collector, ComSafeArrayAsInParam(objects));
+
+ // Pause
+ //RTPrintf("Press enter to pause the VM execution in the remote session...");
+ //getchar();
+ CHECK_ERROR_BREAK(console, Pause());
+
+ RTThreadSleep(5000); // Sleep for 5 seconds
+
+ RTPrintf("\nMetrics collected with VM paused: ---------------------\n");
+ queryMetrics(virtualBox, collector, ComSafeArrayAsInParam(objects));
+
+ RTPrintf("\nDrop collected metrics: ----------------------------------------\n");
+ CHECK_ERROR_BREAK(collector,
+ SetupMetrics(ComSafeArrayAsInParam(baseMetrics),
+ ComSafeArrayAsInParam(objects),
+ 1u, 5u, ComSafeArrayAsOutParam(affectedMetrics)));
+ listAffectedMetrics(virtualBox,
+ ComSafeArrayAsInParam(affectedMetrics));
+ affectedMetrics.setNull();
+ queryMetrics(virtualBox, collector, ComSafeArrayAsInParam(objects));
+
+ com::SafeIfaceArray<IUnknown> vmObject(1);
+ machine.queryInterfaceTo(&vmObject[0]);
+
+ RTPrintf("\nDisable collection of VM metrics: ------------------------------\n");
+ CHECK_ERROR_BREAK(collector,
+ DisableMetrics(ComSafeArrayAsInParam(baseMetrics),
+ ComSafeArrayAsInParam(vmObject),
+ ComSafeArrayAsOutParam(affectedMetrics)));
+ listAffectedMetrics(virtualBox,
+ ComSafeArrayAsInParam(affectedMetrics));
+ affectedMetrics.setNull();
+ RTThreadSleep(5000); // Sleep for 5 seconds
+ queryMetrics(virtualBox, collector, ComSafeArrayAsInParam(objects));
+
+ RTPrintf("\nRe-enable collection of all metrics: ---------------------------\n");
+ CHECK_ERROR_BREAK(collector,
+ EnableMetrics(ComSafeArrayAsInParam(baseMetrics),
+ ComSafeArrayAsInParam(objects),
+ ComSafeArrayAsOutParam(affectedMetrics)));
+ listAffectedMetrics(virtualBox,
+ ComSafeArrayAsInParam(affectedMetrics));
+ affectedMetrics.setNull();
+ RTThreadSleep(5000); // Sleep for 5 seconds
+ queryMetrics(virtualBox, collector, ComSafeArrayAsInParam(objects));
+
+ // Power off
+ RTPrintf("Press enter to power off VM...");
+ getchar();
+ CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
+ RTPrintf("Waiting for the VM to power down...\n");
+ CHECK_ERROR_BREAK(progress, WaitForCompletion(-1));
+ RTPrintf("Press enter to close this session...");
+ getchar();
+ session->UnlockMachine();
+ } while (false);
+#endif /* VBOX_WITH_RESOURCE_USAGE_API */
+#if 0
+ // check of OVF appliance handling
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ Bstr ovf = argc > 1 ? argv[1] : "someOVF.ovf";
+ RTPrintf("Try to open %ls ...\n", ovf.raw());
+
+ ComPtr<IAppliance> appliance;
+ CHECK_ERROR_BREAK(virtualBox,
+ CreateAppliance(appliance.asOutParam()));
+ CHECK_ERROR_BREAK(appliance, Read(ovf));
+ Bstr path;
+ CHECK_ERROR_BREAK(appliance, COMGETTER(Path)(path.asOutParam()));
+ RTPrintf("Successfully opened %ls.\n", path.raw());
+ CHECK_ERROR_BREAK(appliance, Interpret());
+ RTPrintf("Successfully interpreted %ls.\n", path.raw());
+ RTPrintf("Appliance:\n");
+ // Fetch all disks
+ com::SafeArray<BSTR> retDisks;
+ CHECK_ERROR_BREAK(appliance,
+ COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
+ if (retDisks.size() > 0)
+ {
+ RTPrintf("Disks:");
+ for (unsigned i = 0; i < retDisks.size(); i++)
+ RTPrintf(" %ls", Bstr(retDisks[i]).raw());
+ RTPrintf("\n");
+ }
+ /* Fetch all virtual system descriptions */
+ com::SafeIfaceArray<IVirtualSystemDescription> retVSD;
+ CHECK_ERROR_BREAK(appliance,
+ COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(retVSD)));
+ if (retVSD.size() > 0)
+ {
+ for (unsigned i = 0; i < retVSD.size(); ++i)
+ {
+ com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
+ com::SafeArray<BSTR> retRefValues;
+ com::SafeArray<BSTR> retOrigValues;
+ com::SafeArray<BSTR> retAutoValues;
+ com::SafeArray<BSTR> retConfiguration;
+ CHECK_ERROR_BREAK(retVSD[i],
+ GetDescription(ComSafeArrayAsOutParam(retTypes),
+ ComSafeArrayAsOutParam(retRefValues),
+ ComSafeArrayAsOutParam(retOrigValues),
+ ComSafeArrayAsOutParam(retAutoValues),
+ ComSafeArrayAsOutParam(retConfiguration)));
+
+ RTPrintf("VirtualSystemDescription:\n");
+ for (unsigned a = 0; a < retTypes.size(); ++a)
+ {
+ RTPrintf(" %d %ls %ls %ls\n",
+ retTypes[a],
+ Bstr(retOrigValues[a]).raw(),
+ Bstr(retAutoValues[a]).raw(),
+ Bstr(retConfiguration[a]).raw());
+ }
+ /* Show warnings from interpret */
+ com::SafeArray<BSTR> retWarnings;
+ CHECK_ERROR_BREAK(retVSD[i],
+ GetWarnings(ComSafeArrayAsOutParam(retWarnings)));
+ if (retWarnings.size() > 0)
+ {
+ RTPrintf("The following warnings occurs on interpret:\n");
+ for (unsigned r = 0; r < retWarnings.size(); ++r)
+ RTPrintf("%ls\n", Bstr(retWarnings[r]).raw());
+ RTPrintf("\n");
+ }
+ }
+ RTPrintf("\n");
+ }
+ RTPrintf("Try to import the appliance ...\n");
+ ComPtr<IProgress> progress;
+ CHECK_ERROR_BREAK(appliance,
+ ImportMachines(progress.asOutParam()));
+ CHECK_ERROR(progress, WaitForCompletion(-1));
+ if (SUCCEEDED(rc))
+ {
+ /* Check if the import was successfully */
+ progress->COMGETTER(ResultCode)(&rc);
+ if (FAILED(rc))
+ {
+ com::ProgressErrorInfo info(progress);
+ if (info.isBasicAvailable())
+ RTPrintf("Error: failed to import appliance. Error message: %ls\n", info.getText().raw());
+ else
+ RTPrintf("Error: failed to import appliance. No error message available!\n");
+ }
+ else
+ RTPrintf("Successfully imported the appliance.\n");
+ }
+
+ }
+ while (FALSE);
+ RTPrintf("\n");
+#endif
+#if 0
+ // check of network bandwidth control
+ ///////////////////////////////////////////////////////////////////////////
+ do
+ {
+ Bstr name = argc > 1 ? argv[1] : "ubuntu";
+ Bstr sessionType = argc > 2 ? argv[2] : "headless";
+ Bstr grpName = "tstAPI";
+ {
+ // Get machine
+ ComPtr<IMachine> machine;
+ ComPtr<IBandwidthControl> bwCtrl;
+ ComPtr<IBandwidthGroup> bwGroup;
+ ComPtr<INetworkAdapter> nic;
+ RTPrintf("Getting a machine object named '%ls'...\n", name.raw());
+ CHECK_ERROR_BREAK(virtualBox, FindMachine(name.raw(), machine.asOutParam()));
+ /* open a session for the VM (new or shared) */
+ CHECK_ERROR_BREAK(machine, LockMachine(session, LockType_Shared));
+ SessionType_T st;
+ CHECK_ERROR_BREAK(session, COMGETTER(Type)(&st));
+ bool fRunTime = (st == SessionType_Shared);
+ if (fRunTime)
+ {
+ RTPrintf("Machine %ls must not be running!\n");
+ break;
+ }
+ /* get the mutable session machine */
+ session->COMGETTER(Machine)(machine.asOutParam());
+ CHECK_ERROR_BREAK(machine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam()));
+
+ RTPrintf("Creating bandwidth group named '%ls'...\n", grpName.raw());
+ CHECK_ERROR_BREAK(bwCtrl, CreateBandwidthGroup(grpName.raw(), BandwidthGroupType_Network, 123));
+
+
+ CHECK_ERROR_BREAK(bwCtrl, GetBandwidthGroup(grpName.raw(), bwGroup.asOutParam()));
+ CHECK_ERROR_BREAK(machine, GetNetworkAdapter(0, nic.asOutParam()));
+ RTPrintf("Assigning the group to the first network adapter...\n");
+ CHECK_ERROR_BREAK(nic, COMSETTER(BandwidthGroup)(bwGroup));
+ CHECK_ERROR_BREAK(machine, SaveSettings());
+ RTPrintf("Press enter to close this session...");
+ getchar();
+ session->UnlockMachine();
+ }
+ {
+ // Get machine
+ ComPtr<IMachine> machine;
+ ComPtr<IBandwidthControl> bwCtrl;
+ ComPtr<IBandwidthGroup> bwGroup;
+ Bstr grpNameReadFromNic;
+ ComPtr<INetworkAdapter> nic;
+ RTPrintf("Getting a machine object named '%ls'...\n", name.raw());
+ CHECK_ERROR_BREAK(virtualBox, FindMachine(name.raw(), machine.asOutParam()));
+ /* open a session for the VM (new or shared) */
+ CHECK_ERROR_BREAK(machine, LockMachine(session, LockType_Shared));
+ /* get the mutable session machine */
+ session->COMGETTER(Machine)(machine.asOutParam());
+ CHECK_ERROR_BREAK(machine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam()));
+ CHECK_ERROR_BREAK(machine, GetNetworkAdapter(0, nic.asOutParam()));
+
+ RTPrintf("Reading the group back from the first network adapter...\n");
+ CHECK_ERROR_BREAK(nic, COMGETTER(BandwidthGroup)(bwGroup.asOutParam()));
+ if (bwGroup.isNull())
+ RTPrintf("Error: Bandwidth group is null at the first network adapter!\n");
+ else
+ {
+ CHECK_ERROR_BREAK(bwGroup, COMGETTER(Name)(grpNameReadFromNic.asOutParam()));
+ if (grpName != grpNameReadFromNic)
+ RTPrintf("Error: Bandwidth group names do not match (%ls != %ls)!\n", grpName.raw(), grpNameReadFromNic.raw());
+ else
+ RTPrintf("Successfully retrieved bandwidth group attribute from NIC (name=%ls)\n", grpNameReadFromNic.raw());
+ ComPtr<IBandwidthGroup> bwGroupEmpty;
+ RTPrintf("Assigning an empty group to the first network adapter...\n");
+ CHECK_ERROR_BREAK(nic, COMSETTER(BandwidthGroup)(bwGroupEmpty));
+ }
+ RTPrintf("Removing bandwidth group named '%ls'...\n", grpName.raw());
+ CHECK_ERROR_BREAK(bwCtrl, DeleteBandwidthGroup(grpName.raw()));
+ CHECK_ERROR_BREAK(machine, SaveSettings());
+ RTPrintf("Press enter to close this session...");
+ getchar();
+ session->UnlockMachine();
+ }
+ } while (FALSE);
+ RTPrintf("\n");
+#endif
+
+ RTPrintf("Press enter to release Session and VirtualBox instances...");
+ getchar();
+
+ // end "all-stuff" scope
+ ////////////////////////////////////////////////////////////////////////////
+ }
+ while (0);
+
+ RTPrintf("Press enter to shutdown COM...");
+ getchar();
+
+ com::Shutdown();
+
+ RTPrintf("tstAPI FINISHED.\n");
+
+ return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+#ifdef VBOX_WITH_RESOURCE_USAGE_API
+
+static void queryMetrics(ComPtr<IVirtualBox> aVirtualBox,
+ ComPtr<IPerformanceCollector> collector,
+ ComSafeArrayIn(IUnknown *, objects))
+{
+ HRESULT rc;
+
+ //Bstr metricNames[] = { L"CPU/Load/User:avg,CPU/Load/System:avg,CPU/Load/Idle:avg,RAM/Usage/Total,RAM/Usage/Used:avg" };
+ Bstr metricNames[] = { L"*" };
+ com::SafeArray<BSTR> metrics(1);
+ metricNames[0].cloneTo(&metrics[0]);
+ com::SafeArray<BSTR> retNames;
+ com::SafeIfaceArray<IUnknown> retObjects;
+ com::SafeArray<BSTR> retUnits;
+ com::SafeArray<ULONG> retScales;
+ com::SafeArray<ULONG> retSequenceNumbers;
+ com::SafeArray<ULONG> retIndices;
+ com::SafeArray<ULONG> retLengths;
+ com::SafeArray<LONG> retData;
+ CHECK_ERROR(collector, QueryMetricsData(ComSafeArrayAsInParam(metrics),
+ ComSafeArrayInArg(objects),
+ ComSafeArrayAsOutParam(retNames),
+ ComSafeArrayAsOutParam(retObjects),
+ ComSafeArrayAsOutParam(retUnits),
+ ComSafeArrayAsOutParam(retScales),
+ ComSafeArrayAsOutParam(retSequenceNumbers),
+ ComSafeArrayAsOutParam(retIndices),
+ ComSafeArrayAsOutParam(retLengths),
+ ComSafeArrayAsOutParam(retData)));
+ RTPrintf("Object Metric Values\n"
+ "---------- -------------------- --------------------------------------------\n");
+ for (unsigned i = 0; i < retNames.size(); i++)
+ {
+ Bstr metricUnit(retUnits[i]);
+ Bstr metricName(retNames[i]);
+ RTPrintf("%-10ls %-20ls ", getObjectName(aVirtualBox, retObjects[i]).raw(), metricName.raw());
+ const char *separator = "";
+ for (unsigned j = 0; j < retLengths[i]; j++)
+ {
+ if (retScales[i] == 1)
+ RTPrintf("%s%d %ls", separator, retData[retIndices[i] + j], metricUnit.raw());
+ else
+ RTPrintf("%s%d.%02d%ls", separator, retData[retIndices[i] + j] / retScales[i],
+ (retData[retIndices[i] + j] * 100 / retScales[i]) % 100, metricUnit.raw());
+ separator = ", ";
+ }
+ RTPrintf("\n");
+ }
+}
+
+static Bstr getObjectName(ComPtr<IVirtualBox> aVirtualBox,
+ ComPtr<IUnknown> aObject)
+{
+ HRESULT rc;
+
+ ComPtr<IHost> host = aObject;
+ if (!host.isNull())
+ return Bstr("host");
+
+ ComPtr<IMachine> machine = aObject;
+ if (!machine.isNull())
+ {
+ Bstr name;
+ CHECK_ERROR(machine, COMGETTER(Name)(name.asOutParam()));
+ if (SUCCEEDED(rc))
+ return name;
+ }
+ return Bstr("unknown");
+}
+
+static void listAffectedMetrics(ComPtr<IVirtualBox> aVirtualBox,
+ ComSafeArrayIn(IPerformanceMetric*, aMetrics))
+{
+ HRESULT rc;
+ com::SafeIfaceArray<IPerformanceMetric> metrics(ComSafeArrayInArg(aMetrics));
+ if (metrics.size())
+ {
+ ComPtr<IUnknown> object;
+ Bstr metricName;
+ RTPrintf("The following metrics were modified:\n\n"
+ "Object Metric\n"
+ "---------- --------------------\n");
+ for (size_t i = 0; i < metrics.size(); i++)
+ {
+ CHECK_ERROR(metrics[i], COMGETTER(Object)(object.asOutParam()));
+ CHECK_ERROR(metrics[i], COMGETTER(MetricName)(metricName.asOutParam()));
+ RTPrintf("%-10ls %-20ls\n",
+ getObjectName(aVirtualBox, object).raw(), metricName.raw());
+ }
+ RTPrintf("\n");
+ }
+ else
+ {
+ RTPrintf("No metrics match the specified filter!\n");
+ }
+}
+
+#endif /* VBOX_WITH_RESOURCE_USAGE_API */
+/* vim: set shiftwidth=4 tabstop=4 expandtab: */
diff --git a/src/VBox/Main/testcase/tstBstr.cpp b/src/VBox/Main/testcase/tstBstr.cpp
new file mode 100644
index 00000000..4e80ed0a
--- /dev/null
+++ b/src/VBox/Main/testcase/tstBstr.cpp
@@ -0,0 +1,295 @@
+/* $Id: tstBstr.cpp $ */
+/** @file
+ * API Glue Testcase - Bstr.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/string.h>
+
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+#include <iprt/uni.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define CHECK_BSTR(a_Expr, a_bstr, a_szExpected) \
+ do { \
+ a_Expr; \
+ size_t cchExpect = RTStrCalcUtf16Len(a_szExpected); \
+ if ((a_bstr).length() != cchExpect) \
+ RTTestFailed(hTest, "line %u: length() -> %zu, expected %zu (%ls vs %s)", \
+ __LINE__, (a_bstr).length(), cchExpect, (a_bstr).raw(), a_szExpected); \
+ else \
+ { \
+ int iDiff = (a_bstr).compareUtf8(a_szExpected); \
+ if (iDiff) \
+ RTTestFailed(hTest, "line %u: compareUtf8() -> %d: %ls vs %s", \
+ __LINE__, iDiff, (a_bstr).raw(), a_szExpected); \
+ } \
+ } while (0)
+
+
+
+static void testBstrPrintf(RTTEST hTest)
+{
+ RTTestSub(hTest, "Bstr::printf");
+
+ com::Bstr bstr1;
+ CHECK_BSTR(bstr1.printf(""), bstr1, "");
+ CHECK_BSTR(bstr1.printf("1234098694"), bstr1, "1234098694");
+ CHECK_BSTR(bstr1.printf("%u-%u-%u-%s-%u", 42, 999, 42, "asdkfhjasldfk0", 42),
+ bstr1, "42-999-42-asdkfhjasldfk0-42");
+
+ com::Bstr bstr2;
+ CHECK_BSTR(bstr2.printf("%ls-%ls-%ls-%ls-%ls-%ls-%ls-%ls-%ls-%ls::%ls-%ls-%ls-%ls-%ls-%ls-%ls-%ls-%ls-%ls::%ls-%ls-%ls-%ls-%ls-%ls-%ls-%ls-%ls-%ls",
+ bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(),
+ bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(),
+ bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw(), bstr1.raw()),
+ bstr2, "42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42::42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42::42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42");
+ CHECK_BSTR(bstr2.appendPrintf("-9999998888888777776666655554443322110!"),
+ bstr2, "42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42::42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42::42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-9999998888888777776666655554443322110!");
+ CHECK_BSTR(bstr2.appendPrintf("!"),
+ bstr2, "42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42::42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42::42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-42-999-42-asdkfhjasldfk0-42-9999998888888777776666655554443322110!!");
+}
+
+static void testBstrAppend(RTTEST hTest)
+{
+ RTTestSub(hTest, "Bstr::append");
+
+ /* C-string source: */
+ com::Bstr bstr1;
+ CHECK_BSTR(bstr1.append("1234"), bstr1, "1234");
+ CHECK_BSTR(bstr1.append("56"), bstr1, "123456");
+ CHECK_BSTR(bstr1.append("7"), bstr1, "1234567");
+ CHECK_BSTR(bstr1.append("89abcdefghijklmnopqrstuvwxyz"), bstr1, "123456789abcdefghijklmnopqrstuvwxyz");
+ CHECK_BSTR(bstr1.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), bstr1, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ CHECK_BSTR(bstr1.append("123456789abcdefghijklmnopqrstuvwxyz"), bstr1, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz");
+ CHECK_BSTR(bstr1.append("_"), bstr1, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+ CHECK_BSTR(bstr1.append(""), bstr1, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+
+ com::Bstr bstr2;
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(""), VINF_SUCCESS), bstr2, "");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow("1234"), VINF_SUCCESS), bstr2, "1234");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow("56"), VINF_SUCCESS), bstr2, "123456");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow("7"), VINF_SUCCESS), bstr2, "1234567");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow("89abcdefghijklmnopqrstuvwxyz"), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyz");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow("123456789abcdefghijklmnopqrstuvwxyz"), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow("_"), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(""), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+
+ /* Bstr source: */
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(bstr1), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr1.appendNoThrow(bstr2), VINF_SUCCESS), bstr1, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+
+ com::Bstr const bstrWord1("word");
+ CHECK_BSTR(bstr1.setNull(), bstr1, "");
+ CHECK_BSTR(bstr1.append(bstr2, 5, 10), bstr1, "6789abcdef");
+ CHECK_BSTR(bstr1.append(bstr2, 4096, 10), bstr1, "6789abcdef");
+ CHECK_BSTR(bstr1.append(bstrWord1, 1), bstr1, "6789abcdeford");
+ CHECK_BSTR(bstr1.append(bstrWord1, 1,1), bstr1, "6789abcdefordo");
+ CHECK_BSTR(bstr1.append(bstrWord1, 1,2), bstr1, "6789abcdefordoor");
+ CHECK_BSTR(bstr1.append(bstrWord1, 1,3), bstr1, "6789abcdefordoorord");
+ CHECK_BSTR(bstr1.append(bstrWord1, 1,4), bstr1, "6789abcdefordoorordord");
+ CHECK_BSTR(bstr1.append(bstrWord1, 3,1), bstr1, "6789abcdefordoorordordd");
+ CHECK_BSTR(bstr1.append(bstrWord1, 3,2), bstr1, "6789abcdefordoorordorddd");
+ CHECK_BSTR(bstr1.append(bstrWord1, 3), bstr1, "6789abcdefordoorordordddd");
+
+ com::Bstr bstr3;
+ CHECK_BSTR(bstr3.setNull(), bstr3, "");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstr2, 5, 10), VINF_SUCCESS), bstr3, "6789abcdef");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstr2, 4096, 10),VINF_SUCCESS), bstr3, "6789abcdef");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord1, 1), VINF_SUCCESS), bstr3, "6789abcdeford");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord1, 1,1), VINF_SUCCESS), bstr3, "6789abcdefordo");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord1, 1,2), VINF_SUCCESS), bstr3, "6789abcdefordoor");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord1, 1,3), VINF_SUCCESS), bstr3, "6789abcdefordoorord");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord1, 1,4), VINF_SUCCESS), bstr3, "6789abcdefordoorordord");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord1, 3,1), VINF_SUCCESS), bstr3, "6789abcdefordoorordordd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord1, 3,2), VINF_SUCCESS), bstr3, "6789abcdefordoorordorddd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord1, 3), VINF_SUCCESS), bstr3, "6789abcdefordoorordordddd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord1, 3), VINF_SUCCESS), bstr3, "6789abcdefordoorordorddddd");
+ com::Bstr const bstrWord2("-SEP-");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstrWord2, 0), VINF_SUCCESS), bstr3, "6789abcdefordoorordorddddd-SEP-");
+
+ /* CBSTR source: */
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(bstr1.raw()), VINF_SUCCESS), bstr3, "6789abcdefordoorordorddddd-SEP-6789abcdefordoorordordddd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr1.appendNoThrow(bstr3.raw()), VINF_SUCCESS), bstr1, "6789abcdefordoorordordddd6789abcdefordoorordorddddd-SEP-6789abcdefordoorordordddd");
+
+ CBSTR const pwsz2 = bstr2.raw();
+ CBSTR const pwszWord1 = bstrWord1.raw();
+ CHECK_BSTR(bstr1.setNull(), bstr1, "");
+ CHECK_BSTR(bstr1.append(pwsz2 + 5, 10), bstr1, "6789abcdef");
+ CHECK_BSTR(bstr1.append(pwszWord1 + 1), bstr1, "6789abcdeford");
+ CHECK_BSTR(bstr1.append(pwszWord1 + 1, 1), bstr1, "6789abcdefordo");
+ CHECK_BSTR(bstr1.append(pwszWord1 + 1, 2), bstr1, "6789abcdefordoor");
+ CHECK_BSTR(bstr1.append(pwszWord1 + 1, 3), bstr1, "6789abcdefordoorord");
+ CHECK_BSTR(bstr1.append(pwszWord1 + 1, 4), bstr1, "6789abcdefordoorordord");
+ CHECK_BSTR(bstr1.append(pwszWord1 + 3, 1), bstr1, "6789abcdefordoorordordd");
+ CHECK_BSTR(bstr1.append(pwszWord1 + 3, 2), bstr1, "6789abcdefordoorordorddd");
+ CHECK_BSTR(bstr1.append(pwszWord1 + 3), bstr1, "6789abcdefordoorordordddd");
+
+ CHECK_BSTR(bstr3.setNull(), bstr3, "");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwsz2 + 5, 10), VINF_SUCCESS), bstr3, "6789abcdef");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwszWord1 + 1), VINF_SUCCESS), bstr3, "6789abcdeford");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwszWord1 + 1, 1), VINF_SUCCESS), bstr3, "6789abcdefordo");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwszWord1 + 1, 2), VINF_SUCCESS), bstr3, "6789abcdefordoor");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwszWord1 + 1, 3), VINF_SUCCESS), bstr3, "6789abcdefordoorord");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwszWord1 + 1, 4), VINF_SUCCESS), bstr3, "6789abcdefordoorordord");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwszWord1 + 3, 1), VINF_SUCCESS), bstr3, "6789abcdefordoorordordd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwszWord1 + 3, 2), VINF_SUCCESS), bstr3, "6789abcdefordoorordorddd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwszWord1 + 3), VINF_SUCCESS), bstr3, "6789abcdefordoorordordddd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(pwszWord1 + 3), VINF_SUCCESS), bstr3, "6789abcdefordoorordorddddd");
+
+ /* RTCString source: */
+ bstr1.setNull();
+ CHECK_BSTR(bstr1.append(RTCString("1234")), bstr1, "1234");
+ CHECK_BSTR(bstr1.append(RTCString("56")), bstr1, "123456");
+ CHECK_BSTR(bstr1.append(RTCString("7")), bstr1, "1234567");
+ CHECK_BSTR(bstr1.append(RTCString("89abcdefghijklmnopqrstuvwxyz")), bstr1, "123456789abcdefghijklmnopqrstuvwxyz");
+ CHECK_BSTR(bstr1.append(RTCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), bstr1, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ CHECK_BSTR(bstr1.append(RTCString("123456789abcdefghijklmnopqrstuvwxyz")), bstr1, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz");
+ CHECK_BSTR(bstr1.append(RTCString("_")), bstr1, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+ CHECK_BSTR(bstr1.append(RTCString()), bstr1, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+
+ bstr2.setNull();
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(RTCString("")), VINF_SUCCESS), bstr2, "");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(RTCString("1234")), VINF_SUCCESS), bstr2, "1234");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(RTCString("56")), VINF_SUCCESS), bstr2, "123456");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(RTCString("7")), VINF_SUCCESS), bstr2, "1234567");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(RTCString("89abcdefghijklmnopqrstuvwxyz")), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyz");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(RTCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(RTCString("123456789abcdefghijklmnopqrstuvwxyz")), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(RTCString("_")), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr2.appendNoThrow(RTCString("")), VINF_SUCCESS), bstr2, "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789abcdefghijklmnopqrstuvwxyz_");
+
+ RTCString const strWord1 = "word";
+ CHECK_BSTR(bstr1.setNull(), bstr1, "");
+ CHECK_BSTR(bstr1.append(com::Utf8Str(bstr2), 5, 10), bstr1, "6789abcdef");
+ CHECK_BSTR(bstr1.append(com::Utf8Str(bstr2), 4096, 10), bstr1, "6789abcdef");
+ CHECK_BSTR(bstr1.append(strWord1, 1), bstr1, "6789abcdeford");
+ CHECK_BSTR(bstr1.append(strWord1, 1,1), bstr1, "6789abcdefordo");
+ CHECK_BSTR(bstr1.append(strWord1, 1,2), bstr1, "6789abcdefordoor");
+ CHECK_BSTR(bstr1.append(strWord1, 1,3), bstr1, "6789abcdefordoorord");
+ CHECK_BSTR(bstr1.append(strWord1, 1,4), bstr1, "6789abcdefordoorordord");
+ CHECK_BSTR(bstr1.append(strWord1, 3,1), bstr1, "6789abcdefordoorordordd");
+ CHECK_BSTR(bstr1.append(strWord1, 3,2), bstr1, "6789abcdefordoorordorddd");
+ CHECK_BSTR(bstr1.append(strWord1, 3), bstr1, "6789abcdefordoorordordddd");
+
+ CHECK_BSTR(bstr3.setNull(), bstr3, "");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(com::Utf8Str(bstr2), 5, 10), VINF_SUCCESS), bstr3, "6789abcdef");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(com::Utf8Str(bstr2), 4096, 10),VINF_SUCCESS), bstr3, "6789abcdef");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(strWord1, 1), VINF_SUCCESS), bstr3, "6789abcdeford");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(strWord1, 1,1), VINF_SUCCESS), bstr3, "6789abcdefordo");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(strWord1, 1,2), VINF_SUCCESS), bstr3, "6789abcdefordoor");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(strWord1, 1,3), VINF_SUCCESS), bstr3, "6789abcdefordoorord");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(strWord1, 1,4), VINF_SUCCESS), bstr3, "6789abcdefordoorordord");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(strWord1, 3,1), VINF_SUCCESS), bstr3, "6789abcdefordoorordordd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(strWord1, 3,2), VINF_SUCCESS), bstr3, "6789abcdefordoorordorddd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(strWord1, 3), VINF_SUCCESS), bstr3, "6789abcdefordoorordordddd");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow(strWord1, 3), VINF_SUCCESS), bstr3, "6789abcdefordoorordorddddd");
+
+ /* char: */
+ CHECK_BSTR(bstr1.setNull(), bstr1, "");
+ CHECK_BSTR(bstr1.append('-'), bstr1, "-");
+ CHECK_BSTR(bstr1.append('a'), bstr1, "-a");
+ CHECK_BSTR(bstr1.append('b'), bstr1, "-ab");
+ CHECK_BSTR(bstr1.append('Z'), bstr1, "-abZ");
+ CHECK_BSTR(bstr1.append('-'), bstr1, "-abZ-");
+
+ CHECK_BSTR(bstr3.setNull(), bstr3, "");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow('-'), VINF_SUCCESS), bstr3, "-");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow('a'), VINF_SUCCESS), bstr3, "-a");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow('b'), VINF_SUCCESS), bstr3, "-ab");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow('Z'), VINF_SUCCESS), bstr3, "-abZ");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendNoThrow('-'), VINF_SUCCESS), bstr3, "-abZ-");
+
+ /* unicode codepoint: */
+ CHECK_BSTR(bstr1.setNull(), bstr1, "");
+ CHECK_BSTR(bstr1.appendCodePoint('-'), bstr1, "-");
+ CHECK_BSTR(bstr1.appendCodePoint('a'), bstr1, "-a");
+ CHECK_BSTR(bstr1.appendCodePoint('b'), bstr1, "-ab");
+ CHECK_BSTR(bstr1.appendCodePoint('Z'), bstr1, "-abZ");
+ CHECK_BSTR(bstr1.appendCodePoint('-'), bstr1, "-abZ-");
+ CHECK_BSTR(bstr1.appendCodePoint(0x39f), bstr1, "-abZ-\xce\x9f");
+ CHECK_BSTR(bstr1.appendCodePoint(0x1f50), bstr1, "-abZ-\xce\x9f\xe1\xbd\x90");
+ CHECK_BSTR(bstr1.appendCodePoint(0x3c7), bstr1, "-abZ-\xce\x9f\xe1\xbd\x90\xcf\x87");
+ CHECK_BSTR(bstr1.appendCodePoint(0x1f76), bstr1, "-abZ-\xce\x9f\xe1\xbd\x90\xcf\x87\xe1\xbd\xb6");
+
+ CHECK_BSTR(bstr3.setNull(), bstr3, "");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendCodePointNoThrow('-'), VINF_SUCCESS), bstr3, "-");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendCodePointNoThrow('a'), VINF_SUCCESS), bstr3, "-a");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendCodePointNoThrow('b'), VINF_SUCCESS), bstr3, "-ab");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendCodePointNoThrow('Z'), VINF_SUCCESS), bstr3, "-abZ");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendCodePointNoThrow('-'), VINF_SUCCESS), bstr3, "-abZ-");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendCodePointNoThrow(0x39f), VINF_SUCCESS), bstr3, "-abZ-\xce\x9f");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendCodePointNoThrow(0x1f50), VINF_SUCCESS), bstr3, "-abZ-\xce\x9f\xe1\xbd\x90");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendCodePointNoThrow(0x3c7), VINF_SUCCESS), bstr3, "-abZ-\xce\x9f\xe1\xbd\x90\xcf\x87");
+ CHECK_BSTR(RTTESTI_CHECK_RC(bstr3.appendCodePointNoThrow(0x1f76), VINF_SUCCESS), bstr3, "-abZ-\xce\x9f\xe1\xbd\x90\xcf\x87\xe1\xbd\xb6");
+}
+
+
+static void testBstrErase(RTTEST hTest)
+{
+ RTTestSub(hTest, "Bstr::erase");
+
+ com::Bstr bstr1;
+ CHECK_BSTR(bstr1.erase(), bstr1, "");
+ CHECK_BSTR(bstr1.erase(99), bstr1, "");
+ CHECK_BSTR(bstr1.erase(99,999), bstr1, "");
+
+ CHECK_BSTR(bstr1 = "asdlfjhasldfjhaldfhjalhjsdf", bstr1, "asdlfjhasldfjhaldfhjalhjsdf");
+ CHECK_BSTR(bstr1.erase(8, 9), bstr1, "asdlfjhafhjalhjsdf");
+ CHECK_BSTR(bstr1.erase(17, 20), bstr1, "asdlfjhafhjalhjsd");
+ CHECK_BSTR(bstr1.erase(16, 1), bstr1, "asdlfjhafhjalhjs");
+ CHECK_BSTR(bstr1.erase(15, 0), bstr1, "asdlfjhafhjalhjs");
+ CHECK_BSTR(bstr1.erase(13, 3), bstr1, "asdlfjhafhjal");
+ CHECK_BSTR(bstr1.erase(3, 3), bstr1, "asdhafhjal");
+ CHECK_BSTR(bstr1.erase(), bstr1, "");
+}
+
+
+int main()
+{
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstBstr", &hTest);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ RTTestBanner(hTest);
+
+ testBstrPrintf(hTest);
+ testBstrAppend(hTest);
+ testBstrErase(hTest);
+
+ rcExit = RTTestSummaryAndDestroy(hTest);
+ }
+ return rcExit;
+}
+
diff --git a/src/VBox/Main/testcase/tstCollector.cpp b/src/VBox/Main/testcase/tstCollector.cpp
new file mode 100644
index 00000000..415caf43
--- /dev/null
+++ b/src/VBox/Main/testcase/tstCollector.cpp
@@ -0,0 +1,588 @@
+/* $Id: tstCollector.cpp $ */
+/** @file
+ * VirtualBox Main - Performance collector classes test cases.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifdef RT_OS_DARWIN
+# include "../src-server/darwin/PerformanceDarwin.cpp"
+#endif
+#ifdef RT_OS_FREEBSD
+# include "../src-server/freebsd/PerformanceFreeBSD.cpp"
+#endif
+#ifdef RT_OS_LINUX
+# include "../src-server/linux/PerformanceLinux.cpp"
+#endif
+#ifdef RT_OS_OS2
+# include "../src-server/os2/PerformanceOS2.cpp"
+#endif
+#ifdef RT_OS_SOLARIS
+# include "../src-server/solaris/PerformanceSolaris.cpp"
+#endif
+#ifdef RT_OS_WINDOWS
+# define _WIN32_DCOM
+# include <iprt/win/objidl.h>
+# include <iprt/win/objbase.h>
+# include "../src-server/win/PerformanceWin.cpp"
+#endif
+
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#define RUN_TIME_MS 1000
+
+#define N_CALLS(n, fn) \
+ do {\
+ for (int call = 0; call < n; ++call) \
+ rc = collector->fn; \
+ if (RT_FAILURE(rc)) \
+ RTPrintf("tstCollector: "#fn" -> %Rrc\n", rc); \
+ } while (0)
+
+#define CALLS_PER_SECOND(fn, args) \
+ do { \
+ nCalls = 0; \
+ start = RTTimeMilliTS(); \
+ do { \
+ rc = collector->fn args; \
+ if (RT_FAILURE(rc)) \
+ break; \
+ ++nCalls; \
+ } while (RTTimeMilliTS() - start < RUN_TIME_MS); \
+ if (RT_FAILURE(rc)) \
+ RTPrintf("tstCollector: "#fn" -> %Rrc\n", rc); \
+ else \
+ RTPrintf("%70s -- %u calls per second\n", #fn, nCalls); \
+ } while (0)
+
+void shutdownProcessList(std::vector<RTPROCESS> const &rProcesses)
+{
+ for (size_t i = 0; i < rProcesses.size(); i++)
+ RTProcTerminate(rProcesses[i]);
+}
+
+void measurePerformance(pm::CollectorHAL *collector, const char *pszName, int cVMs)
+{
+
+ const char * const args[] = { pszName, "-child", NULL };
+ pm::CollectorHints hints;
+ std::vector<RTPROCESS> processes;
+
+ hints.collectHostCpuLoad();
+ hints.collectHostRamUsage();
+ /* Start fake VMs */
+ for (int i = 0; i < cVMs; ++i)
+ {
+ RTPROCESS pid;
+ int rc = RTProcCreate(pszName, args, RTENV_DEFAULT, 0, &pid);
+ if (RT_FAILURE(rc))
+ {
+ hints.getProcesses(processes);
+ shutdownProcessList(processes);
+
+ RTPrintf("tstCollector: RTProcCreate() -> %Rrc\n", rc);
+ return;
+ }
+ hints.collectProcessCpuLoad(pid);
+ hints.collectProcessRamUsage(pid);
+ }
+
+ hints.getProcesses(processes);
+ RTThreadSleep(30000); // Let children settle for half a minute
+
+ int rc;
+ ULONG tmp;
+ uint64_t tmp64;
+ uint64_t start;
+ unsigned int nCalls;
+ /* Pre-collect */
+ CALLS_PER_SECOND(preCollect, (hints, 0));
+ /* Host CPU load */
+ CALLS_PER_SECOND(getRawHostCpuLoad, (&tmp64, &tmp64, &tmp64));
+ /* Process CPU load */
+ CALLS_PER_SECOND(getRawProcessCpuLoad, (processes[nCalls % cVMs], &tmp64, &tmp64, &tmp64));
+ /* Host CPU speed */
+ CALLS_PER_SECOND(getHostCpuMHz, (&tmp));
+ /* Host RAM usage */
+ CALLS_PER_SECOND(getHostMemoryUsage, (&tmp, &tmp, &tmp));
+ /* Process RAM usage */
+ CALLS_PER_SECOND(getProcessMemoryUsage, (processes[nCalls % cVMs], &tmp));
+
+ start = RTTimeNanoTS();
+
+ int times;
+ for (times = 0; times < 100; times++)
+ {
+ /* Pre-collect */
+ N_CALLS(1, preCollect(hints, 0));
+ /* Host CPU load */
+ N_CALLS(1, getRawHostCpuLoad(&tmp64, &tmp64, &tmp64));
+ /* Host CPU speed */
+ N_CALLS(1, getHostCpuMHz(&tmp));
+ /* Host RAM usage */
+ N_CALLS(1, getHostMemoryUsage(&tmp, &tmp, &tmp));
+ /* Process CPU load */
+ N_CALLS(cVMs, getRawProcessCpuLoad(processes[call], &tmp64, &tmp64, &tmp64));
+ /* Process RAM usage */
+ N_CALLS(cVMs, getProcessMemoryUsage(processes[call], &tmp));
+ }
+ RTPrintf("\n%d VMs -- %u%% of CPU time\n", cVMs, (unsigned)((double)(RTTimeNanoTS() - start) / 10000000.0 / times));
+
+ /* Shut down fake VMs */
+ shutdownProcessList(processes);
+}
+
+#ifdef RT_OS_SOLARIS
+#define NETIFNAME "net0"
+#else
+#define NETIFNAME "eth0"
+#endif
+int testNetwork(pm::CollectorHAL *collector)
+{
+ pm::CollectorHints hints;
+ uint64_t hostRxStart, hostTxStart;
+ uint64_t hostRxStop, hostTxStop, speed = 125000000; /* Assume 1Gbit/s */
+
+ RTPrintf("tstCollector: TESTING - Network load, sleeping for 5 s...\n");
+
+ hostRxStart = hostTxStart = 0;
+ int rc = collector->preCollect(hints, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawHostNetworkLoad(NETIFNAME, &hostRxStart, &hostTxStart);
+ if (rc == VERR_NOT_IMPLEMENTED)
+ RTPrintf("tstCollector: getRawHostNetworkLoad() not implemented, skipping\n");
+ else
+ {
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawHostNetworkLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+
+ RTThreadSleep(5000); // Sleep for five seconds
+
+ rc = collector->preCollect(hints, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+ return 1;
+ }
+ hostRxStop = hostRxStart;
+ hostTxStop = hostTxStart;
+ rc = collector->getRawHostNetworkLoad(NETIFNAME, &hostRxStop, &hostTxStop);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawHostNetworkLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+ RTPrintf("tstCollector: host network speed = %llu bytes/sec (%llu mbit/sec)\n",
+ speed, speed/(1000000/8));
+ RTPrintf("tstCollector: host network rx = %llu bytes/sec (%llu mbit/sec, %u.%u %%)\n",
+ (hostRxStop - hostRxStart)/5, (hostRxStop - hostRxStart)/(5000000/8),
+ (hostRxStop - hostRxStart) * 100 / (speed * 5),
+ (hostRxStop - hostRxStart) * 10000 / (speed * 5) % 100);
+ RTPrintf("tstCollector: host network tx = %llu bytes/sec (%llu mbit/sec, %u.%u %%)\n\n",
+ (hostTxStop - hostTxStart)/5, (hostTxStop - hostTxStart)/(5000000/8),
+ (hostTxStop - hostTxStart) * 100 / (speed * 5),
+ (hostTxStop - hostTxStart) * 10000 / (speed * 5) % 100);
+ }
+
+ return 0;
+}
+
+#define FSNAME "/"
+int testFsUsage(pm::CollectorHAL *collector)
+{
+ RTPrintf("tstCollector: TESTING - File system usage\n");
+
+ ULONG total, used, available;
+
+ int rc = collector->getHostFilesystemUsage(FSNAME, &total, &used, &available);
+ if (rc == VERR_NOT_IMPLEMENTED)
+ RTPrintf("tstCollector: getHostFilesystemUsage() not implemented, skipping\n");
+ else
+ {
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getHostFilesystemUsage() -> %Rrc\n", rc);
+ return 1;
+ }
+ RTPrintf("tstCollector: host root fs total = %lu MB\n", total);
+ RTPrintf("tstCollector: host root fs used = %lu MB\n", used);
+ RTPrintf("tstCollector: host root fs available = %lu MB\n\n", available);
+ }
+ return 0;
+}
+
+int testDisk(pm::CollectorHAL *collector)
+{
+ pm::CollectorHints hints;
+ uint64_t diskMsStart, totalMsStart;
+ uint64_t diskMsStop, totalMsStop;
+
+ pm::DiskList disksUsage, disksLoad;
+ int rc = collector->getDiskListByFs(FSNAME, disksUsage, disksLoad);
+ if (rc == VERR_NOT_IMPLEMENTED)
+ RTPrintf("tstCollector: getDiskListByFs() not implemented, skipping\n");
+ else
+ {
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getDiskListByFs(%s) -> %Rrc\n", FSNAME, rc);
+ return 1;
+ }
+ if (disksUsage.empty())
+ {
+ RTPrintf("tstCollector: getDiskListByFs(%s) returned empty usage list\n", FSNAME);
+ return 0;
+ }
+ if (disksLoad.empty())
+ {
+ RTPrintf("tstCollector: getDiskListByFs(%s) returned empty usage list\n", FSNAME);
+ return 0;
+ }
+
+ pm::DiskList::iterator it;
+ for (it = disksUsage.begin(); it != disksUsage.end(); ++it)
+ {
+ uint64_t diskSize = 0;
+ rc = collector->getHostDiskSize(it->c_str(), &diskSize);
+ RTPrintf("tstCollector: TESTING - Disk size (%s) = %llu\n", it->c_str(), diskSize);
+ if (rc == VERR_FILE_NOT_FOUND)
+ RTPrintf("tstCollector: getHostDiskSize(%s) returned VERR_FILE_NOT_FOUND\n", it->c_str());
+ else if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getHostDiskSize() -> %Rrc\n", rc);
+ return 1;
+ }
+ }
+
+ for (it = disksLoad.begin(); it != disksLoad.end(); ++it)
+ {
+ RTPrintf("tstCollector: TESTING - Disk utilization (%s), sleeping for 5 s...\n", it->c_str());
+
+ hints.collectHostCpuLoad();
+ rc = collector->preCollect(hints, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawHostDiskLoad(it->c_str(), &diskMsStart, &totalMsStart);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawHostDiskLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+
+ RTThreadSleep(5000); // Sleep for five seconds
+
+ rc = collector->preCollect(hints, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawHostDiskLoad(it->c_str(), &diskMsStop, &totalMsStop);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawHostDiskLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+ RTPrintf("tstCollector: host disk util = %llu msec (%u.%u %%), total = %llu msec\n\n",
+ (diskMsStop - diskMsStart),
+ (unsigned)((diskMsStop - diskMsStart) * 100 / (totalMsStop - totalMsStart)),
+ (unsigned)((diskMsStop - diskMsStart) * 10000 / (totalMsStop - totalMsStart) % 100),
+ totalMsStop - totalMsStart);
+ }
+ }
+
+ return 0;
+}
+
+
+
+int main(int argc, char *argv[])
+{
+ bool cpuTest, ramTest, netTest, diskTest, fsTest, perfTest;
+ cpuTest = ramTest = netTest = diskTest = fsTest = perfTest = false;
+ /*
+ * Initialize the VBox runtime without loading
+ * the support driver.
+ */
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: RTR3InitExe() -> %d\n", rc);
+ return 1;
+ }
+ if (argc > 1)
+ {
+ if (!strcmp(argv[1], "-child"))
+ {
+ /* We have spawned ourselves as a child process -- scratch the leg */
+ RTThreadSleep(1000000);
+ return 1;
+ }
+ for (int i = 1; i < argc; i++)
+ {
+ if (!strcmp(argv[i], "-cpu"))
+ cpuTest = true;
+ else if (!strcmp(argv[i], "-ram"))
+ ramTest = true;
+ else if (!strcmp(argv[i], "-net"))
+ netTest = true;
+ else if (!strcmp(argv[i], "-disk"))
+ diskTest = true;
+ else if (!strcmp(argv[i], "-fs"))
+ fsTest = true;
+ else if (!strcmp(argv[i], "-perf"))
+ perfTest = true;
+ else
+ {
+ RTPrintf("tstCollector: Unknown option: %s\n", argv[i]);
+ return 2;
+ }
+ }
+ }
+ else
+ cpuTest = ramTest = netTest = diskTest = fsTest = perfTest = true;
+
+#ifdef RT_OS_WINDOWS
+ HRESULT hRes = CoInitialize(NULL);
+ /*
+ * Need to initialize security to access performance enumerators.
+ */
+ hRes = CoInitializeSecurity(
+ NULL,
+ -1,
+ NULL,
+ NULL,
+ RPC_C_AUTHN_LEVEL_NONE,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ NULL, EOAC_NONE, 0);
+#endif
+
+ pm::CollectorHAL *collector = pm::createHAL();
+ if (!collector)
+ {
+ RTPrintf("tstCollector: createMetricFactory() failed\n");
+ return 1;
+ }
+
+ pm::CollectorHints hints;
+ if (cpuTest)
+ {
+ hints.collectHostCpuLoad();
+ hints.collectProcessCpuLoad(RTProcSelf());
+ }
+ if (ramTest)
+ {
+ hints.collectHostRamUsage();
+ hints.collectProcessRamUsage(RTProcSelf());
+ }
+
+ uint64_t start;
+
+ uint64_t hostUserStart, hostKernelStart, hostIdleStart;
+ uint64_t hostUserStop, hostKernelStop, hostIdleStop, hostTotal;
+
+ uint64_t processUserStart, processKernelStart, processTotalStart;
+ uint64_t processUserStop, processKernelStop, processTotalStop;
+
+ rc = collector->preCollect(hints, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+ return 1;
+ }
+ if (cpuTest)
+ {
+ RTPrintf("tstCollector: TESTING - CPU load, sleeping for 5 s...\n");
+
+ rc = collector->getRawHostCpuLoad(&hostUserStart, &hostKernelStart, &hostIdleStart);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawHostCpuLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawProcessCpuLoad(RTProcSelf(), &processUserStart, &processKernelStart, &processTotalStart);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawProcessCpuLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+
+ RTThreadSleep(5000); // Sleep for 5 seconds
+
+ rc = collector->preCollect(hints, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawHostCpuLoad(&hostUserStop, &hostKernelStop, &hostIdleStop);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawHostCpuLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawProcessCpuLoad(RTProcSelf(), &processUserStop, &processKernelStop, &processTotalStop);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawProcessCpuLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+ hostTotal = hostUserStop - hostUserStart
+ + hostKernelStop - hostKernelStart
+ + hostIdleStop - hostIdleStart;
+ RTPrintf("tstCollector: host cpu user = %u.%u %%\n",
+ (unsigned)((hostUserStop - hostUserStart) * 100 / hostTotal),
+ (unsigned)((hostUserStop - hostUserStart) * 10000 / hostTotal % 100));
+ RTPrintf("tstCollector: host cpu kernel = %u.%u %%\n",
+ (unsigned)((hostKernelStop - hostKernelStart) * 100 / hostTotal),
+ (unsigned)((hostKernelStop - hostKernelStart) * 10000 / hostTotal % 100));
+ RTPrintf("tstCollector: host cpu idle = %u.%u %%\n",
+ (unsigned)((hostIdleStop - hostIdleStart) * 100 / hostTotal),
+ (unsigned)((hostIdleStop - hostIdleStart) * 10000 / hostTotal % 100));
+ RTPrintf("tstCollector: process cpu user = %u.%u %%\n",
+ (unsigned)((processUserStop - processUserStart) * 100 / (processTotalStop - processTotalStart)),
+ (unsigned)((processUserStop - processUserStart) * 10000 / (processTotalStop - processTotalStart) % 100));
+ RTPrintf("tstCollector: process cpu kernel = %u.%u %%\n\n",
+ (unsigned)((processKernelStop - processKernelStart) * 100 / (processTotalStop - processTotalStart)),
+ (unsigned)((processKernelStop - processKernelStart) * 10000 / (processTotalStop - processTotalStart) % 100));
+
+ RTPrintf("tstCollector: TESTING - CPU load, looping for 5 s...\n");
+ rc = collector->preCollect(hints, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawHostCpuLoad(&hostUserStart, &hostKernelStart, &hostIdleStart);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawHostCpuLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawProcessCpuLoad(RTProcSelf(), &processUserStart, &processKernelStart, &processTotalStart);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawProcessCpuLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+ start = RTTimeMilliTS();
+ while (RTTimeMilliTS() - start < 5000)
+ ; // Loop for 5 seconds
+ rc = collector->preCollect(hints, 0);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: preCollect() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawHostCpuLoad(&hostUserStop, &hostKernelStop, &hostIdleStop);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawHostCpuLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getRawProcessCpuLoad(RTProcSelf(), &processUserStop, &processKernelStop, &processTotalStop);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getRawProcessCpuLoad() -> %Rrc\n", rc);
+ return 1;
+ }
+ hostTotal = hostUserStop - hostUserStart
+ + hostKernelStop - hostKernelStart
+ + hostIdleStop - hostIdleStart;
+ RTPrintf("tstCollector: host cpu user = %u.%u %%\n",
+ (unsigned)((hostUserStop - hostUserStart) * 100 / hostTotal),
+ (unsigned)((hostUserStop - hostUserStart) * 10000 / hostTotal % 100));
+ RTPrintf("tstCollector: host cpu kernel = %u.%u %%\n",
+ (unsigned)((hostKernelStop - hostKernelStart) * 100 / hostTotal),
+ (unsigned)((hostKernelStop - hostKernelStart) * 10000 / hostTotal % 100));
+ RTPrintf("tstCollector: host cpu idle = %u.%u %%\n",
+ (unsigned)((hostIdleStop - hostIdleStart) * 100 / hostTotal),
+ (unsigned)((hostIdleStop - hostIdleStart) * 10000 / hostTotal % 100));
+ RTPrintf("tstCollector: process cpu user = %u.%u %%\n",
+ (unsigned)((processUserStop - processUserStart) * 100 / (processTotalStop - processTotalStart)),
+ (unsigned)((processUserStop - processUserStart) * 10000 / (processTotalStop - processTotalStart) % 100));
+ RTPrintf("tstCollector: process cpu kernel = %u.%u %%\n\n",
+ (unsigned)((processKernelStop - processKernelStart) * 100 / (processTotalStop - processTotalStart)),
+ (unsigned)((processKernelStop - processKernelStart) * 10000 / (processTotalStop - processTotalStart) % 100));
+ }
+
+ if (ramTest)
+ {
+ RTPrintf("tstCollector: TESTING - Memory usage\n");
+
+ ULONG total, used, available, processUsed;
+
+ rc = collector->getHostMemoryUsage(&total, &used, &available);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getHostMemoryUsage() -> %Rrc\n", rc);
+ return 1;
+ }
+ rc = collector->getProcessMemoryUsage(RTProcSelf(), &processUsed);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstCollector: getProcessMemoryUsage() -> %Rrc\n", rc);
+ return 1;
+ }
+ RTPrintf("tstCollector: host mem total = %lu kB\n", total);
+ RTPrintf("tstCollector: host mem used = %lu kB\n", used);
+ RTPrintf("tstCollector: host mem available = %lu kB\n", available);
+ RTPrintf("tstCollector: process mem used = %lu kB\n\n", processUsed);
+ }
+
+ if (netTest)
+ rc = testNetwork(collector);
+ if (fsTest)
+ rc = testFsUsage(collector);
+ if (diskTest)
+ rc = testDisk(collector);
+ if (perfTest)
+ {
+ RTPrintf("tstCollector: TESTING - Performance\n\n");
+
+ measurePerformance(collector, argv[0], 100);
+ }
+
+ delete collector;
+
+ RTPrintf("\ntstCollector FINISHED.\n");
+
+ return RTEXITCODE_SUCCESS;
+}
+
diff --git a/src/VBox/Main/testcase/tstGuestCtrlContextID.cpp b/src/VBox/Main/testcase/tstGuestCtrlContextID.cpp
new file mode 100644
index 00000000..1c402134
--- /dev/null
+++ b/src/VBox/Main/testcase/tstGuestCtrlContextID.cpp
@@ -0,0 +1,125 @@
+/* $Id: tstGuestCtrlContextID.cpp $ */
+/** @file
+ * Context ID makeup/extraction test cases.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_ENABLED
+#define LOG_GROUP LOG_GROUP_MAIN
+#include <VBox/log.h>
+
+#include "../include/GuestCtrlImplPrivate.h"
+
+using namespace com;
+
+#include <iprt/env.h>
+#include <iprt/rand.h>
+#include <iprt/stream.h>
+#include <iprt/test.h>
+
+int main()
+{
+ RTTEST hTest;
+ int rc = RTTestInitAndCreate("tstGuestCtrlContextID", &hTest);
+ if (rc)
+ return rc;
+ RTTestBanner(hTest);
+
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Initializing COM...\n");
+ HRESULT hrc = com::Initialize();
+ if (FAILED(hrc))
+ {
+ RTTestFailed(hTest, "Failed to initialize COM (%Rhrc)!\n", hrc);
+ return RTEXITCODE_FAILURE;
+ }
+
+ /* Don't let the assertions trigger here
+ * -- we rely on the return values in the test(s) below. */
+ RTAssertSetQuiet(true);
+
+#if 0
+ for (int t = 0; t < 4 && !RTTestErrorCount(hTest); t++)
+ {
+ uint32_t uSession = RTRandU32Ex(0, VBOX_GUESTCTRL_MAX_SESSIONS - 1);
+ uint32_t uFilter = VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(uSession);
+ RTTestIPrintf(RTTESTLVL_INFO, "Session: %RU32, Filter: %x\n", uSession, uFilter);
+
+ uint32_t uSession2 = RTRandU32Ex(0, VBOX_GUESTCTRL_MAX_SESSIONS - 1);
+ uint32_t uCID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSession2,
+ RTRandU32Ex(0, VBOX_GUESTCTRL_MAX_OBJECTS - 1),
+ RTRandU32Ex(0, VBOX_GUESTCTRL_MAX_CONTEXTS - 1));
+ RTTestIPrintf(RTTESTLVL_INFO, "CID: %x (Session: %d), Masked: %x\n",
+ uCID, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uCID), uCID & uFilter);
+ if ((uCID & uFilter) == uCID)
+ {
+ RTTestIPrintf(RTTESTLVL_INFO, "=========== Masking works: %x vs. %x\n",
+ uCID & uFilter, uFilter);
+ }
+ }
+#endif
+
+ uint32_t uContextMax = UINT32_MAX;
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Max context is: %RU32\n", uContextMax);
+
+ /* Do 4048 tests total. */
+ for (int t = 0; t < 4048 && !RTTestErrorCount(hTest); t++)
+ {
+ /* VBOX_GUESTCTRL_MAX_* includes 0 as an object, so subtract one. */
+ uint32_t s = RTRandU32Ex(0, VBOX_GUESTCTRL_MAX_SESSIONS - 1);
+ uint32_t p = RTRandU32Ex(0, VBOX_GUESTCTRL_MAX_OBJECTS - 1);
+ uint32_t c = RTRandU32Ex(0, VBOX_GUESTCTRL_MAX_CONTEXTS - 1);
+
+ uint64_t uContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(s, p, c);
+ RTTestIPrintf(RTTESTLVL_DEBUG, "ContextID (%d,%d,%d) = %RU32\n", s, p, c, uContextID);
+ if (s != VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID))
+ {
+ RTTestFailed(hTest, "%d,%d,%d: Session is %d, expected %d -> %RU64\n",
+ s, p, c, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID), s, uContextID);
+ }
+ else if (p != VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID))
+ {
+ RTTestFailed(hTest, "%d,%d,%d: Object is %d, expected %d -> %RU64\n",
+ s, p, c, VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID), p, uContextID);
+ }
+ if (c != VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID))
+ {
+ RTTestFailed(hTest, "%d,%d,%d: Count is %d, expected %d -> %RU64\n",
+ s, p, c, VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID), c, uContextID);
+ }
+ if (uContextID > UINT32_MAX)
+ {
+ RTTestFailed(hTest, "%d,%d,%d: Value overflow; does not fit anymore: %RU64\n",
+ s, p, c, uContextID);
+ }
+ }
+
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Shutting down COM...\n");
+ com::Shutdown();
+
+ /*
+ * Summary.
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp b/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp
new file mode 100644
index 00000000..41a6776f
--- /dev/null
+++ b/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp
@@ -0,0 +1,409 @@
+/* $Id: tstGuestCtrlParseBuffer.cpp $ */
+/** @file
+ * Tests for VBoxService toolbox output streams.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN
+#include <VBox/err.h>
+#include <VBox/log.h>
+
+#include "../include/GuestCtrlImplPrivate.h"
+
+using namespace com;
+
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/test.h>
+#include <iprt/rand.h>
+#include <iprt/stream.h>
+
+#ifndef BYTE
+# define BYTE uint8_t
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Defines a test entry string size (in bytes). */
+#define TST_STR_BYTES(a_sz) (sizeof(a_sz) - 1)
+/** Defines a test entry string, followed by its size (in bytes). */
+#define TST_STR_AND_BYTES(a_sz) a_sz, (sizeof(a_sz) - 1)
+/** Defines the termination sequence for a single key/value pair. */
+#define TST_STR_VAL_TRM GUESTTOOLBOX_STRM_TERM_PAIR_STR
+/** Defines the termination sequence for a single stream block. */
+#define TST_STR_BLK_TRM GUESTTOOLBOX_STRM_TERM_BLOCK_STR
+/** Defines the termination sequence for the stream. */
+#define TST_STR_STM_TRM GUESTTOOLBOX_STRM_TERM_STREAM_STR
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct VBOXGUESTCTRL_BUFFER_VALUE
+{
+ char *pszValue;
+} VBOXGUESTCTRL_BUFFER_VALUE, *PVBOXGUESTCTRL_BUFFER_VALUE;
+typedef std::map< RTCString, VBOXGUESTCTRL_BUFFER_VALUE > GuestBufferMap;
+typedef std::map< RTCString, VBOXGUESTCTRL_BUFFER_VALUE >::iterator GuestBufferMapIter;
+typedef std::map< RTCString, VBOXGUESTCTRL_BUFFER_VALUE >::const_iterator GuestBufferMapIterConst;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+char szUnterm1[] = { 'a', 's', 'd', 'f' };
+char szUnterm2[] = { 'f', 'o', 'o', '3', '=', 'b', 'a', 'r', '3' };
+
+PRTLOGGER g_pLog = NULL;
+
+/**
+ * Tests single block parsing.
+ */
+static struct
+{
+ const char *pbData;
+ size_t cbData;
+ uint32_t offStart;
+ uint32_t offAfter;
+ uint32_t cMapElements;
+ int iResult;
+} g_aTestBlocks[] =
+{
+ /* Invalid stuff. */
+ { NULL, 0, 0, 0, 0, VERR_INVALID_POINTER },
+ { NULL, 512, 0, 0, 0, VERR_INVALID_POINTER },
+ { "", 0, 0, 0, 0, VERR_INVALID_PARAMETER },
+ { "", 0, 0, 0, 0, VERR_INVALID_PARAMETER },
+ { "foo=bar1", 0, 0, 0, 0, VERR_INVALID_PARAMETER },
+ { "foo=bar2", 0, 50, 50, 0, VERR_INVALID_PARAMETER },
+ /* Has a empty key (not allowed). */
+ { TST_STR_AND_BYTES("=test2" TST_STR_VAL_TRM), 0, TST_STR_BYTES(""), 0, VERR_INVALID_PARAMETER },
+ /* Empty buffers, i.e. nothing to process. */
+ /* Index 6*/
+ { "", 1, 0, 0, 0, VINF_SUCCESS },
+ { TST_STR_VAL_TRM, 1, 0, 0, 0, VINF_SUCCESS },
+ /* Stream termination sequence. */
+ { TST_STR_AND_BYTES(TST_STR_STM_TRM), 0,
+ TST_STR_BYTES (TST_STR_STM_TRM), 0, VINF_EOF },
+ /* Trash after stream termination sequence (skipped / ignored). */
+ { TST_STR_AND_BYTES(TST_STR_STM_TRM "trash"), 0,
+ TST_STR_BYTES (TST_STR_STM_TRM "trash"), 0, VINF_EOF },
+ { TST_STR_AND_BYTES("a=b" TST_STR_STM_TRM), 0,
+ TST_STR_BYTES ("a=b" TST_STR_STM_TRM), 1, VINF_EOF },
+ { TST_STR_AND_BYTES("a=b" TST_STR_VAL_TRM "c=d" TST_STR_STM_TRM), 0,
+ TST_STR_BYTES ("a=b" TST_STR_VAL_TRM "c=d" TST_STR_STM_TRM), 2, VINF_EOF },
+ /* Unterminated values (missing separator, i.e. no valid pair). */
+ { TST_STR_AND_BYTES("test1"), 0, 0, 0, VINF_SUCCESS },
+ /* Has a NULL value (allowed). */
+ { TST_STR_AND_BYTES("test2=" TST_STR_VAL_TRM), 0,
+ TST_STR_BYTES ("test2="), 1, VINF_SUCCESS },
+ /* One completed pair only. */
+ { TST_STR_AND_BYTES("test3=test3" TST_STR_VAL_TRM), 0,
+ TST_STR_BYTES ("test3=test3"), 1, VINF_SUCCESS },
+ /* One completed pair, plus an unfinished pair (separator + terminator missing). */
+ { TST_STR_AND_BYTES("test4=test4" TST_STR_VAL_TRM "t41"), 0,
+ TST_STR_BYTES ("test4=test4" TST_STR_VAL_TRM), 1, VINF_SUCCESS },
+ /* Two completed pairs. */
+ { TST_STR_AND_BYTES("test5=test5" TST_STR_VAL_TRM "t51=t51" TST_STR_VAL_TRM), 0,
+ TST_STR_BYTES ("test5=test5" TST_STR_VAL_TRM "t51=t51"), 2, VINF_SUCCESS },
+ /* One complete block, next block unterminated. */
+ { TST_STR_AND_BYTES("a51=b51" TST_STR_VAL_TRM "c52=d52" TST_STR_BLK_TRM "e53=f53"), 0,
+ TST_STR_BYTES ("a51=b51" TST_STR_VAL_TRM "c52=d52" TST_STR_BLK_TRM), 2, VINF_SUCCESS },
+ /* Ditto. */
+ { TST_STR_AND_BYTES("test6=test6" TST_STR_BLK_TRM "t61=t61"), 0,
+ TST_STR_BYTES ("test6=test6" TST_STR_BLK_TRM), 1, VINF_SUCCESS },
+ /* Two complete pairs with a complete stream. */
+ { TST_STR_AND_BYTES("test61=" TST_STR_VAL_TRM "test611=test612" TST_STR_STM_TRM), 0,
+ TST_STR_BYTES ("test61=" TST_STR_VAL_TRM "test611=test612" TST_STR_STM_TRM), 2, VINF_EOF },
+ /* One complete block. */
+ { TST_STR_AND_BYTES("test7=test7" TST_STR_BLK_TRM), 0,
+ TST_STR_BYTES ("test7=test7"), 1, VINF_SUCCESS },
+ /* Ditto. */
+ { TST_STR_AND_BYTES("test81=test82" TST_STR_VAL_TRM "t81=t82" TST_STR_BLK_TRM), 0,
+ TST_STR_BYTES ("test81=test82" TST_STR_VAL_TRM "t81=t82"), 2, VINF_SUCCESS },
+ /* Good stuff, but with a second block -- should be *not* taken into account since
+ * we're only interested in parsing/handling the first object. */
+ { TST_STR_AND_BYTES("t91=t92" TST_STR_VAL_TRM "t93=t94" TST_STR_BLK_TRM "t95=t96" TST_STR_BLK_TRM), 0,
+ TST_STR_BYTES ("t91=t92" TST_STR_VAL_TRM "t93=t94" TST_STR_BLK_TRM), 2, VINF_SUCCESS },
+ /* Nasty stuff. */
+ /* iso 8859-1 encoding (?) of 'aou' all with diaeresis '=f' and 'ao' with diaeresis. */
+ { TST_STR_AND_BYTES("1\xe4\xf6\xfc=\x66\xe4\xf6" TST_STR_BLK_TRM), 0,
+ TST_STR_BYTES ("1\xe4\xf6\xfc=\x66\xe4\xf6"), 1, VINF_SUCCESS },
+ /* Like above, but after the first '\0' it adds 'ooo=aaa' all letters with diaeresis. */
+ { TST_STR_AND_BYTES("2\xe4\xf6\xfc=\x66\xe4\xf6" TST_STR_VAL_TRM "\xf6\xf6\xf6=\xe4\xe4\xe4"), 0,
+ TST_STR_BYTES ("2\xe4\xf6\xfc=\x66\xe4\xf6" TST_STR_VAL_TRM), 1, VINF_SUCCESS },
+ /* Some "real world" examples from VBoxService toolbox. */
+ { TST_STR_AND_BYTES("hdr_id=vbt_stat" TST_STR_VAL_TRM "hdr_ver=1" TST_STR_VAL_TRM "name=foo.txt" TST_STR_BLK_TRM), 0,
+ TST_STR_BYTES ("hdr_id=vbt_stat" TST_STR_VAL_TRM "hdr_ver=1" TST_STR_VAL_TRM "name=foo.txt"), 3, VINF_SUCCESS }
+};
+
+/**
+ * Tests parsing multiple stream blocks.
+ *
+ * Same parsing behavior as for the tests above apply.
+ */
+static struct
+{
+ /** Stream data. */
+ const char *pbData;
+ /** Size of stream data (in bytes). */
+ size_t cbData;
+ /** Number of data blocks retrieved. These are separated by "\0\0". */
+ uint32_t cBlocks;
+ /** Overall result when done parsing. */
+ int iResult;
+} const g_aTestStream[] =
+{
+ /* No blocks. */
+ { "", sizeof(""), 0, VINF_SUCCESS },
+ /* Empty block (no key/value pairs), will not be accounted. */
+ { TST_STR_STM_TRM,
+ TST_STR_BYTES(TST_STR_STM_TRM), 0, VINF_EOF },
+ /* Good stuff. */
+ { TST_STR_AND_BYTES(TST_STR_VAL_TRM "b1=b2" TST_STR_STM_TRM), 1, VINF_EOF },
+ { TST_STR_AND_BYTES("b3=b31" TST_STR_STM_TRM), 1, VINF_EOF },
+ { TST_STR_AND_BYTES("b4=b41" TST_STR_BLK_TRM "b51=b61" TST_STR_STM_TRM), 2, VINF_EOF },
+ { TST_STR_AND_BYTES("b5=b51" TST_STR_VAL_TRM "b61=b71" TST_STR_STM_TRM), 1, VINF_EOF }
+};
+
+/**
+ * Reads and parses the stream from a given file.
+ *
+ * @returns RTEXITCODE
+ * @param pszFile Absolute path to file to parse.
+ */
+static int tstReadFromFile(const char *pszFile)
+{
+ RTFILE fh;
+ int rc = RTFileOpen(&fh, pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ uint64_t cbFileSize;
+ rc = RTFileQuerySize(fh, &cbFileSize);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ GuestProcessStream stream;
+ GuestProcessStreamBlock block;
+
+ size_t cPairs = 0;
+ size_t cBlocks = 0;
+
+ unsigned aToRead[] = { 256, 23, 13 };
+ unsigned i = 0;
+
+ uint64_t cbToRead = cbFileSize;
+
+ for (unsigned a = 0; a < 32; a++)
+ {
+ uint8_t buf[_64K];
+ do
+ {
+ size_t cbChunk = RT_MIN(cbToRead, i < RT_ELEMENTS(aToRead) ? aToRead[i++] : RTRandU64Ex(8, RT_MIN(sizeof(buf), 64)));
+ if (cbChunk > cbToRead)
+ cbChunk = cbToRead;
+ if (cbChunk)
+ {
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Reading %zu bytes (of %zu left) ...\n", cbChunk, cbToRead);
+
+ size_t cbRead;
+ rc = RTFileRead(fh, &buf, cbChunk, &cbRead);
+ AssertRCBreak(rc);
+
+ if (!cbRead)
+ continue;
+
+ cbToRead -= cbRead;
+
+ rc = stream.AddData((BYTE *)buf, cbRead);
+ AssertRCBreak(rc);
+ }
+
+ rc = stream.ParseBlock(block);
+ Assert(rc != VERR_INVALID_PARAMETER);
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Parsing ended with %Rrc\n", rc);
+ if (block.IsComplete())
+ {
+ /* Sanity checks; disable this if you parse anything else but fsinfo output from VBoxService toolbox. */
+ //Assert(block.GetString("name") != NULL);
+
+ cPairs += block.GetCount();
+ cBlocks = stream.GetBlocks();
+ block.Clear();
+ }
+ } while (VINF_SUCCESS == rc /* Might also be VINF_EOF when finished */);
+
+ RTTestIPrintf(RTTESTLVL_ALWAYS, "Total %zu blocks + %zu pairs\n", cBlocks, cPairs);
+
+ /* Reset. */
+ RTFileSeek(fh, 0, RTFILE_SEEK_BEGIN, NULL);
+ cbToRead = cbFileSize;
+ cPairs = 0;
+ cBlocks = 0;
+ block.Clear();
+ stream.Destroy();
+ }
+
+ int rc2 = RTFileClose(fh);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+int main(int argc, char **argv)
+{
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstParseBuffer", &hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(hTest);
+
+#ifdef DEBUG
+ RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ fFlags |= RTLOGFLAGS_USECRLF;
+#endif
+ static const char * const s_apszLogGroups[] = VBOX_LOGGROUP_NAMES;
+ int rc = RTLogCreate(&g_pLog, fFlags, "guest_control.e.l.l2.l3.f", NULL,
+ RT_ELEMENTS(s_apszLogGroups), s_apszLogGroups, RTLOGDEST_STDOUT, NULL /*"vkat-release.log"*/);
+ AssertRCReturn(rc, rc);
+ RTLogSetDefaultInstance(g_pLog);
+#endif
+
+ if (argc > 1)
+ return tstReadFromFile(argv[1]);
+
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Initializing COM...\n");
+ HRESULT hrc = com::Initialize();
+ if (FAILED(hrc))
+ {
+ RTTestFailed(hTest, "Failed to initialize COM (%Rhrc)!\n", hrc);
+ return RTEXITCODE_FAILURE;
+ }
+
+ AssertCompile(TST_STR_BYTES("1") == 1);
+ AssertCompile(TST_STR_BYTES("sizecheck") == 9);
+ AssertCompile(TST_STR_BYTES("off=rab") == 7);
+ AssertCompile(TST_STR_BYTES("off=rab\0\0") == 9);
+
+ RTTestSub(hTest, "Blocks");
+
+ RTTestDisableAssertions(hTest);
+
+ for (unsigned iTest = 0; iTest < RT_ELEMENTS(g_aTestBlocks); iTest++)
+ {
+ RTTestIPrintf(RTTESTLVL_DEBUG, "=> Block test #%u:\n'%.*Rhxd\n", iTest, g_aTestBlocks[iTest].cbData, g_aTestBlocks[iTest].pbData);
+
+ GuestProcessStream stream;
+ int iResult = stream.AddData((BYTE *)g_aTestBlocks[iTest].pbData, g_aTestBlocks[iTest].cbData);
+ if (RT_SUCCESS(iResult))
+ {
+ GuestProcessStreamBlock curBlock;
+ iResult = stream.ParseBlock(curBlock);
+ if (iResult != g_aTestBlocks[iTest].iResult)
+ RTTestFailed(hTest, "Block #%u: Returned %Rrc, expected %Rrc\n", iTest, iResult, g_aTestBlocks[iTest].iResult);
+ else if (stream.GetOffset() != g_aTestBlocks[iTest].offAfter)
+ RTTestFailed(hTest, "Block #%u: Offset %zu wrong ('%#x'), expected %u ('%#x')\n",
+ iTest, stream.GetOffset(), g_aTestBlocks[iTest].pbData[stream.GetOffset()],
+ g_aTestBlocks[iTest].offAfter, g_aTestBlocks[iTest].pbData[g_aTestBlocks[iTest].offAfter]);
+ else if (iResult == VERR_MORE_DATA)
+ RTTestIPrintf(RTTESTLVL_DEBUG, "\tMore data (Offset: %zu)\n", stream.GetOffset());
+
+ if (RT_SUCCESS(iResult) || iResult == VERR_MORE_DATA)
+ if (curBlock.GetCount() != g_aTestBlocks[iTest].cMapElements)
+ RTTestFailed(hTest, "Block #%u: Map has %u elements, expected %u\n",
+ iTest, curBlock.GetCount(), g_aTestBlocks[iTest].cMapElements);
+
+ /* There is remaining data left in the buffer (which needs to be merged
+ * with a following buffer) -- print it. */
+ size_t off = stream.GetOffset();
+ size_t cbToWrite = g_aTestBlocks[iTest].cbData - off;
+ if (cbToWrite)
+ {
+ RTTestIPrintf(RTTESTLVL_DEBUG, "\tRemaining (%u):\n", cbToWrite);
+
+ /* How to properly get the current RTTESTLVL (aka IPRT_TEST_MAX_LEVEL) here?
+ * Hack alert: Using RTEnvGet for now. */
+ if (!RTStrICmp(RTEnvGet("IPRT_TEST_MAX_LEVEL"), "debug"))
+ RTStrmWriteEx(g_pStdOut, &g_aTestBlocks[iTest].pbData[off], cbToWrite - 1, NULL);
+ }
+
+ if (RTTestIErrorCount())
+ break;
+ }
+ }
+
+ RTTestSub(hTest, "Streams");
+
+ for (unsigned iTest = 0; iTest < RT_ELEMENTS(g_aTestStream); iTest++)
+ {
+ RTTestIPrintf(RTTESTLVL_DEBUG, "=> Stream test #%u\n%.*Rhxd\n",
+ iTest, g_aTestStream[iTest].cbData, g_aTestStream[iTest].pbData);
+
+ GuestProcessStream stream;
+ int iResult = stream.AddData((BYTE*)g_aTestStream[iTest].pbData, g_aTestStream[iTest].cbData);
+ if (RT_SUCCESS(iResult))
+ {
+ uint32_t cBlocksComplete = 0;
+ uint8_t cSafety = 0;
+ do
+ {
+ GuestProcessStreamBlock curBlock;
+ iResult = stream.ParseBlock(curBlock);
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Stream #%u: Returned with %Rrc\n", iTest, iResult);
+ if (cSafety++ > 8)
+ break;
+ if (curBlock.IsComplete())
+ cBlocksComplete++;
+ } while (iResult != VINF_EOF);
+
+ if (iResult != g_aTestStream[iTest].iResult)
+ RTTestFailed(hTest, "Stream #%u: Returned %Rrc, expected %Rrc\n", iTest, iResult, g_aTestStream[iTest].iResult);
+ else if (cBlocksComplete != g_aTestStream[iTest].cBlocks)
+ RTTestFailed(hTest, "Stream #%u: Returned %u blocks, expected %u\n", iTest, cBlocksComplete, g_aTestStream[iTest].cBlocks);
+ }
+ else
+ RTTestFailed(hTest, "Stream #%u: Adding data failed with %Rrc\n", iTest, iResult);
+
+ if (RTTestIErrorCount())
+ break;
+ }
+
+ RTTestRestoreAssertions(hTest);
+
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Shutting down COM...\n");
+ com::Shutdown();
+
+ /*
+ * Summary.
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/Main/testcase/tstGuestCtrlPaths.cpp b/src/VBox/Main/testcase/tstGuestCtrlPaths.cpp
new file mode 100644
index 00000000..b292e5a3
--- /dev/null
+++ b/src/VBox/Main/testcase/tstGuestCtrlPaths.cpp
@@ -0,0 +1,167 @@
+/* $Id */
+/** @file
+ * Guest Control path test cases.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_ENABLED
+#define LOG_GROUP LOG_GROUP_MAIN
+#include <VBox/log.h>
+
+#include "../include/GuestCtrlImplPrivate.h"
+
+using namespace com;
+
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/rand.h>
+#include <iprt/stream.h>
+#include <iprt/test.h>
+
+
+DECLINLINE(void) tstPathBuildDestination(const Utf8Str &strSrcPath, PathStyle_T enmSrcPathStyle,
+ const Utf8Str &strDstPath, PathStyle_T enmDstPathStyle,
+ int rcExp, Utf8Str strPathExp)
+{
+ Utf8Str strDstPath2 = strDstPath;
+ int vrc = GuestPath::BuildDestinationPath(strSrcPath, enmSrcPathStyle, strDstPath2, enmDstPathStyle);
+ RTTESTI_CHECK_MSG_RETV(vrc == rcExp, ("Expected %Rrc, got %Rrc for '%s'\n", rcExp, vrc, strDstPath.c_str()));
+ RTTESTI_CHECK_MSG_RETV(strDstPath2 == strPathExp, ("Expected '%s', got '%s'\n", strPathExp.c_str(), strDstPath2.c_str()));
+}
+
+DECLINLINE(void) tstPathTranslate(Utf8Str strPath, PathStyle_T enmSrcPathStyle, PathStyle_T enmDstPathStyle, int rcExp, Utf8Str strPathExp)
+{
+ Utf8Str strPath2 = strPath;
+ int vrc = GuestPath::Translate(strPath2, enmSrcPathStyle, enmDstPathStyle);
+ RTTESTI_CHECK_MSG_RETV(vrc == rcExp, ("Expected %Rrc, got %Rrc for '%s'\n", rcExp, vrc, strPath.c_str()));
+ RTTESTI_CHECK_MSG_RETV(strPath2 == strPathExp, ("Expected '%s', got '%s'\n", strPathExp.c_str(), strPath2.c_str()));
+}
+
+int main()
+{
+ RTTEST hTest;
+ int vrc = RTTestInitAndCreate("tstGuestCtrlPaths", &hTest);
+ if (vrc)
+ return vrc;
+ RTTestBanner(hTest);
+
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Initializing COM...\n");
+ HRESULT hrc = com::Initialize();
+ if (FAILED(hrc))
+ {
+ RTTestFailed(hTest, "Failed to initialize COM (%Rhrc)!\n", hrc);
+ return RTEXITCODE_FAILURE;
+ }
+
+ /* Don't let the assertions trigger here
+ * -- we rely on the return values in the test(s) below. */
+ RTAssertSetQuiet(true);
+
+ /*
+ * Path translation testing.
+ */
+ tstPathTranslate("", PathStyle_DOS, PathStyle_DOS, VINF_SUCCESS, "");
+
+ tstPathTranslate("foo", PathStyle_DOS, PathStyle_DOS, VINF_SUCCESS, "foo");
+ tstPathTranslate("foo", PathStyle_UNIX, PathStyle_UNIX, VINF_SUCCESS, "foo");
+ tstPathTranslate("foo", PathStyle_DOS, PathStyle_UNIX, VINF_SUCCESS, "foo");
+ tstPathTranslate("foo", PathStyle_UNIX, PathStyle_UNIX, VINF_SUCCESS, "foo");
+
+ tstPathTranslate("foo\\bar", PathStyle_DOS, PathStyle_DOS, VINF_SUCCESS, "foo\\bar");
+ tstPathTranslate("foo/bar", PathStyle_UNIX, PathStyle_UNIX, VINF_SUCCESS, "foo/bar");
+
+ tstPathTranslate("foo\\bar\\", PathStyle_DOS, PathStyle_DOS, VINF_SUCCESS, "foo\\bar\\");
+ tstPathTranslate("foo/bar/", PathStyle_UNIX, PathStyle_UNIX, VINF_SUCCESS, "foo/bar/");
+ /* Actually also allowed on Windows. */
+ tstPathTranslate("foo/bar/", PathStyle_DOS, PathStyle_UNIX, VINF_SUCCESS, "foo/bar/");
+
+ tstPathTranslate("foo\\bar\\BAZ", PathStyle_DOS, PathStyle_DOS, VINF_SUCCESS, "foo\\bar\\BAZ");
+ tstPathTranslate("foo/bar/BAZ", PathStyle_UNIX, PathStyle_UNIX, VINF_SUCCESS, "foo/bar/BAZ");
+
+ tstPathTranslate("foo\\bar\\dir with space\\", PathStyle_DOS, PathStyle_UNIX, VINF_SUCCESS, "foo/bar/dir with space/");
+ tstPathTranslate("foo/bar/dir with space/", PathStyle_UNIX, PathStyle_UNIX, VINF_SUCCESS, "foo/bar/dir with space/");
+
+#if 0
+ /** Do a mapping of "\", which marks an escape sequence for paths on UNIX-y OSes to DOS-based OSes (like Windows),
+ * however, on DOS "\" is a path separator. See @bugref{21095} */
+ tstPathTranslate("foo/bar/dir_with_escape_sequence\\ space", PathStyle_UNIX, PathStyle_UNIX, VINF_SUCCESS, "foo/bar/dir_with_escape_sequence\\ space");
+ tstPathTranslate("foo/bar/dir_with_escape_sequence\\ space", PathStyle_UNIX, PathStyle_DOS, VINF_SUCCESS, "foo\\bar\\dir_with_escape_sequence space");
+ tstPathTranslate("foo/bar/1_dir_with_escape_sequence/the\\ space", PathStyle_UNIX, PathStyle_DOS, VINF_SUCCESS, "foo\\bar\\1_dir_with_escape_sequence\\the space");
+ tstPathTranslate("foo/bar/2_dir_with_escape_sequence/the\\ \\ space", PathStyle_UNIX, PathStyle_DOS, VINF_SUCCESS, "foo\\bar\\2_dir_with_escape_sequence\\the space");
+ tstPathTranslate("foo/bar/dir_with_escape_sequence/spaces at end\\ \\ ", PathStyle_UNIX, PathStyle_DOS, VINF_SUCCESS, "foo\\bar\\dir_with_escape_sequence\\spaces at end ");
+#endif
+
+ /* Filter out double slashes (cosmetic only). */
+ tstPathTranslate("\\\\", PathStyle_DOS, PathStyle_DOS, VINF_SUCCESS, "\\");
+ tstPathTranslate("foo\\\\bar\\", PathStyle_DOS, PathStyle_DOS, VINF_SUCCESS, "foo\\bar\\");
+
+ /* Mixed slashes. */
+ tstPathTranslate("\\\\foo/bar\\\\baz", PathStyle_UNIX, PathStyle_UNIX, VINF_SUCCESS, "\\\\foo/bar\\\\baz");
+#if 0 /** @todo Not clear what to expect here. */
+ tstPathTranslate("with spaces\\ foo/\\ bar", PathStyle_UNIX, PathStyle_DOS, VINF_SUCCESS, "with spaces foo\\ bar");
+#endif
+
+ /*
+ * Destination path building testing.
+ */
+ bool fQuiet = RTAssertSetQuiet(true);
+ bool fMayPanic = RTAssertSetMayPanic(false);
+ tstPathBuildDestination("", PathStyle_UNIX, "", PathStyle_UNIX, VERR_PATH_ZERO_LENGTH, "");
+ tstPathBuildDestination(".", PathStyle_UNIX, ".", PathStyle_UNIX, VINF_SUCCESS, ".");
+ tstPathBuildDestination("..", PathStyle_UNIX, "..", PathStyle_UNIX, VERR_INVALID_PARAMETER, "..");
+ tstPathBuildDestination("/tmp/", PathStyle_UNIX, "/root/../foo", PathStyle_UNIX, VERR_INVALID_PARAMETER, "/root/../foo");
+ /* ".." in actual file names are allowed. */
+ tstPathBuildDestination("/tmp/", PathStyle_UNIX, "/root/foo..bar", PathStyle_UNIX, VINF_SUCCESS, "/root/foo..bar");
+ /* Ditto for path names which consist of more than just "..". */
+ tstPathBuildDestination("/tmp/", PathStyle_UNIX, "/root/foo..bar/baz", PathStyle_UNIX, VINF_SUCCESS, "/root/foo..bar/baz");
+ tstPathBuildDestination("...", PathStyle_UNIX, "...", PathStyle_UNIX, VINF_SUCCESS, "...");
+ tstPathBuildDestination("foo", PathStyle_UNIX, "bar", PathStyle_UNIX, VINF_SUCCESS, "bar");
+ tstPathBuildDestination("foo/", PathStyle_UNIX, "bar/", PathStyle_UNIX, VINF_SUCCESS, "bar/");
+ tstPathBuildDestination("foo/", PathStyle_UNIX, "bar/baz", PathStyle_UNIX, VINF_SUCCESS, "bar/baz");
+ tstPathBuildDestination("foo/baz", PathStyle_UNIX, "bar/", PathStyle_UNIX, VINF_SUCCESS, "bar/baz");
+ tstPathBuildDestination("foo/baz", PathStyle_UNIX, "bar\\", PathStyle_DOS, VINF_SUCCESS, "bar\\baz");
+
+ tstPathBuildDestination("c:\\temp\\", PathStyle_DOS, "/tmp/", PathStyle_UNIX, VINF_SUCCESS, "/tmp/");
+ tstPathBuildDestination("c:\\TEMP\\", PathStyle_DOS, "/TmP/", PathStyle_UNIX, VINF_SUCCESS, "/TmP/");
+ tstPathBuildDestination("c:\\temp\\foo.txt", PathStyle_DOS, "/tmp/foo.txt", PathStyle_UNIX, VINF_SUCCESS, "/tmp/foo.txt");
+ tstPathBuildDestination("c:\\temp\\bar\\foo.txt", PathStyle_DOS, "/tmp/foo2.txt", PathStyle_UNIX, VINF_SUCCESS, "/tmp/foo2.txt");
+ tstPathBuildDestination("c:\\temp\\bar\\foo3.txt", PathStyle_DOS, "/tmp/", PathStyle_UNIX, VINF_SUCCESS, "/tmp/foo3.txt");
+
+ tstPathBuildDestination("/tmp/bar/", PathStyle_UNIX, "c:\\temp\\", PathStyle_DOS, VINF_SUCCESS, "c:\\temp\\");
+ tstPathBuildDestination("/tmp/BaR/", PathStyle_UNIX, "c:\\tEmP\\", PathStyle_DOS, VINF_SUCCESS, "c:\\tEmP\\");
+ tstPathBuildDestination("/tmp/foo.txt", PathStyle_UNIX, "c:\\temp\\foo.txt", PathStyle_DOS, VINF_SUCCESS, "c:\\temp\\foo.txt");
+ tstPathBuildDestination("/tmp/bar/foo.txt", PathStyle_UNIX, "c:\\temp\\foo2.txt", PathStyle_DOS, VINF_SUCCESS, "c:\\temp\\foo2.txt");
+ tstPathBuildDestination("/tmp/bar/foo3.txt", PathStyle_UNIX, "c:\\temp\\", PathStyle_DOS, VINF_SUCCESS, "c:\\temp\\foo3.txt");
+ RTAssertSetMayPanic(fMayPanic);
+ RTAssertSetQuiet(fQuiet);
+
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Shutting down COM...\n");
+ com::Shutdown();
+
+ /*
+ * Summary.
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
+
diff --git a/src/VBox/Main/testcase/tstGuid.cpp b/src/VBox/Main/testcase/tstGuid.cpp
new file mode 100644
index 00000000..0bffd820
--- /dev/null
+++ b/src/VBox/Main/testcase/tstGuid.cpp
@@ -0,0 +1,110 @@
+/* $Id: tstGuid.cpp $ */
+/** @file
+ * API Glue Testcase - Guid.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/Guid.h>
+
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+#include <iprt/uni.h>
+
+
+static void test1(RTTEST hTest)
+{
+ RTTestSub(hTest, "Basics");
+
+#define CHECK(expr) RTTESTI_CHECK(expr)
+#define CHECK_DUMP(expr, value) \
+ do { \
+ if (!(expr)) \
+ RTTestFailed(hTest, "%d: FAILED %s, got \"%s\"", __LINE__, #expr, value); \
+ } while (0)
+
+#define CHECK_DUMP_I(expr) \
+ do { \
+ if (!(expr)) \
+ RTTestFailed(hTest, "%d: FAILED %s, got \"%d\"", __LINE__, #expr, expr); \
+ } while (0)
+#define CHECK_EQUAL(Str, szExpect) \
+ do { \
+ if (!(Str).equals(szExpect)) \
+ RTTestIFailed("line %u: expected \"%s\" got \"%s\"", __LINE__, szExpect, (Str).c_str()); \
+ } while (0)
+#define CHECK_EQUAL_I(iRes, iExpect) \
+ do { \
+ if (iRes != iExpect) \
+ RTTestIFailed("line %u: expected \"%zd\" got \"%zd\"", __LINE__, iExpect, iRes); \
+ } while (0)
+
+ com::Guid zero;
+ CHECK(zero.isZero());
+
+ com::Guid copyZero(zero);
+ CHECK(copyZero.isZero());
+
+ com::Guid assignZero(zero);
+ CHECK(assignZero.isZero());
+
+ com::Guid random;
+ random.create();
+ CHECK(!random.isZero());
+
+ com::Guid copyRandom(random);
+ CHECK(!copyRandom.isZero());
+
+ com::Guid assignRandom(random);
+ CHECK(!assignRandom.isZero());
+
+ /** @todo extend this a lot, it needs to cover many more cases */
+
+#undef CHECK
+#undef CHECK_DUMP
+#undef CHECK_DUMP_I
+#undef CHECK_EQUAL
+}
+
+
+int main()
+{
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstGuid", &hTest);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ RTTestBanner(hTest);
+
+ test1(hTest);
+
+ rcExit = RTTestSummaryAndDestroy(hTest);
+ }
+ return rcExit;
+}
+
diff --git a/src/VBox/Main/testcase/tstMediumLock.cpp b/src/VBox/Main/testcase/tstMediumLock.cpp
new file mode 100644
index 00000000..b121be53
--- /dev/null
+++ b/src/VBox/Main/testcase/tstMediumLock.cpp
@@ -0,0 +1,309 @@
+/* $Id: tstMediumLock.cpp $ */
+/** @file
+ * Medium lock test cases.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_ENABLED
+#define LOG_GROUP LOG_GROUP_MAIN
+#include <VBox/log.h>
+
+#include <VBox/com/com.h>
+#include <VBox/com/ptr.h>
+#include <VBox/com/defs.h>
+#include <VBox/com/array.h>
+#include <VBox/com/string.h>
+#include <VBox/com/VirtualBox.h>
+
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/test.h>
+
+using namespace com;
+
+
+#define TEST_RT_SUCCESS(x,y,z) \
+ do \
+ { \
+ int rc = (y); \
+ if (RT_FAILURE(rc)) \
+ RTTestFailed((x), "%s %Rrc", (z), rc); \
+ } while (0)
+
+#define TEST_COM_SUCCESS(x,y,z) \
+ do \
+ { \
+ HRESULT hrc = (y); \
+ if (FAILED(hrc)) \
+ RTTestFailed((x), "%s %Rhrc", (z), hrc); \
+ } while (0)
+
+#define TEST_COM_FAILURE(x,y,z) \
+ do \
+ { \
+ HRESULT hrc = (y); \
+ if (SUCCEEDED(hrc)) \
+ RTTestFailed((x), "%s", (z)); \
+ } while (0)
+
+int main(int argc, char *argv[])
+{
+ /* Init the runtime without loading the support driver. */
+ RTR3InitExe(argc, &argv, 0);
+
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstMediumLock", &hTest);
+ if (rcExit)
+ return rcExit;
+ RTTestBanner(hTest);
+
+ bool fComInit = false;
+ ComPtr<IVirtualBoxClient> pVirtualBoxClient;
+ ComPtr<IVirtualBox> pVirtualBox;
+ char szPathTemp[RTPATH_MAX] = "";
+ ComPtr<IMedium> pMedium;
+
+ if (!RTTestSubErrorCount(hTest))
+ {
+ RTTestSub(hTest, "Constructing temp image name");
+ TEST_RT_SUCCESS(hTest, RTPathTemp(szPathTemp, sizeof(szPathTemp)), "temp directory");
+ RTUUID uuid;
+ RTUuidCreate(&uuid);
+ char szFile[50];
+ RTStrPrintf(szFile, sizeof(szFile), "%RTuuid.vdi", &uuid);
+ TEST_RT_SUCCESS(hTest, RTPathAppend(szPathTemp, sizeof(szPathTemp), szFile), "concatenate image name");
+ }
+
+ if (!RTTestSubErrorCount(hTest))
+ {
+ RTTestSub(hTest, "Initializing COM");
+ TEST_COM_SUCCESS(hTest, Initialize(), "init");
+ }
+
+ if (!RTTestSubErrorCount(hTest))
+ {
+ fComInit = true;
+
+ RTTestSub(hTest, "Getting VirtualBox reference");
+ TEST_COM_SUCCESS(hTest, pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient), "vboxclient reference");
+ TEST_COM_SUCCESS(hTest, pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam()), "vbox reference");
+ }
+
+ if (!RTTestSubErrorCount(hTest))
+ {
+ RTTestSub(hTest, "Creating temp hard disk medium");
+ TEST_COM_SUCCESS(hTest, pVirtualBox->CreateMedium(Bstr("VDI").raw(), Bstr(szPathTemp).raw(), AccessMode_ReadWrite, DeviceType_HardDisk, pMedium.asOutParam()), "create medium");
+ if (!pMedium.isNull())
+ {
+ ComPtr<IProgress> pProgress;
+ SafeArray<MediumVariant_T> variant;
+ variant.push_back(MediumVariant_Standard);
+ TEST_COM_SUCCESS(hTest, pMedium->CreateBaseStorage(_1M, ComSafeArrayAsInParam(variant), pProgress.asOutParam()), "create base storage");
+ if (!pProgress.isNull())
+ TEST_COM_SUCCESS(hTest, pProgress->WaitForCompletion(30000), "waiting for completion of create");
+ }
+ }
+
+ if (!RTTestSubErrorCount(hTest))
+ {
+ RTTestSub(hTest, "Write locks");
+ ComPtr<IToken> pToken1, pToken2;
+
+ MediumState_T mediumState = MediumState_NotCreated;
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
+ if (mediumState != MediumState_Created)
+ RTTestFailed(hTest, "wrong medium state %d", mediumState);
+
+ TEST_COM_SUCCESS(hTest, pMedium->LockWrite(pToken1.asOutParam()), "write lock");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock write state");
+ if (mediumState != MediumState_LockedWrite)
+ RTTestFailed(hTest, "wrong lock write medium state %d", mediumState);
+
+ TEST_COM_FAILURE(hTest, pMedium->LockWrite(pToken2.asOutParam()), "nested write lock succeeded");
+ if (!pToken2.isNull())
+ RTTestFailed(hTest, "pToken2 is not null");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock write state");
+ if (mediumState != MediumState_LockedWrite)
+ RTTestFailed(hTest, "wrong after nested lock write medium state %d", mediumState);
+
+ if (!pToken1.isNull())
+ TEST_COM_SUCCESS(hTest, pToken1->Abandon(), "write unlock");
+ else
+ RTTestFailed(hTest, "pToken1 is null");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock write state");
+ if (mediumState != MediumState_Created)
+ RTTestFailed(hTest, "wrong unlock write medium state %d", mediumState);
+ }
+
+ if (!RTTestSubErrorCount(hTest))
+ {
+ RTTestSub(hTest, "Read locks");
+ ComPtr<IToken> pToken1, pToken2;
+
+ MediumState_T mediumState = MediumState_NotCreated;
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
+ if (mediumState != MediumState_Created)
+ RTTestFailed(hTest, "wrong medium state %d", mediumState);
+
+ TEST_COM_SUCCESS(hTest, pMedium->LockRead(pToken1.asOutParam()), "read lock");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock read state");
+ if (mediumState != MediumState_LockedRead)
+ RTTestFailed(hTest, "wrong lock read medium state %d", mediumState);
+
+ TEST_COM_SUCCESS(hTest, pMedium->LockRead(pToken2.asOutParam()), "nested read lock failed");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock read state");
+ if (mediumState != MediumState_LockedRead)
+ RTTestFailed(hTest, "wrong after nested lock read medium state %d", mediumState);
+
+ if (!pToken2.isNull())
+ TEST_COM_SUCCESS(hTest, pToken2->Abandon(), "read nested unlock");
+ else
+ RTTestFailed(hTest, "pToken2 is null");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock read state");
+ if (mediumState != MediumState_LockedRead)
+ RTTestFailed(hTest, "wrong after nested lock read medium state %d", mediumState);
+
+ if (!pToken1.isNull())
+ TEST_COM_SUCCESS(hTest, pToken1->Abandon(), "read nested unlock");
+ else
+ RTTestFailed(hTest, "pToken1 is null");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock read state");
+ if (mediumState != MediumState_Created)
+ RTTestFailed(hTest, "wrong unlock read medium state %d", mediumState);
+ }
+
+ if (!RTTestSubErrorCount(hTest))
+ {
+ RTTestSub(hTest, "Mixing write and read locks");
+ ComPtr<IToken> pToken1, pToken2;
+
+ MediumState_T mediumState = MediumState_NotCreated;
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
+ if (mediumState != MediumState_Created)
+ RTTestFailed(hTest, "wrong medium state %d", mediumState);
+
+ TEST_COM_SUCCESS(hTest, pMedium->LockWrite(pToken1.asOutParam()), "write lock");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock write state");
+ if (mediumState != MediumState_LockedWrite)
+ RTTestFailed(hTest, "wrong lock write medium state %d", mediumState);
+
+ TEST_COM_FAILURE(hTest, pMedium->LockRead(pToken2.asOutParam()), "write+read lock succeeded");
+ if (!pToken2.isNull())
+ RTTestFailed(hTest, "pToken2 is not null");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock write state");
+ if (mediumState != MediumState_LockedWrite)
+ RTTestFailed(hTest, "wrong after nested lock write medium state %d", mediumState);
+
+ if (!pToken1.isNull())
+ TEST_COM_SUCCESS(hTest, pToken1->Abandon(), "write unlock");
+ else
+ RTTestFailed(hTest, "pToken1 is null");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock write state");
+ if (mediumState != MediumState_Created)
+ RTTestFailed(hTest, "wrong unlock write medium state %d", mediumState);
+ }
+
+ if (!RTTestSubErrorCount(hTest))
+ {
+ RTTestSub(hTest, "Mixing read and write locks");
+ ComPtr<IToken> pToken1, pToken2;
+
+ MediumState_T mediumState = MediumState_NotCreated;
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
+ if (mediumState != MediumState_Created)
+ RTTestFailed(hTest, "wrong medium state %d", mediumState);
+
+ TEST_COM_SUCCESS(hTest, pMedium->LockRead(pToken1.asOutParam()), "read lock");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock read state");
+ if (mediumState != MediumState_LockedRead)
+ RTTestFailed(hTest, "wrong lock read medium state %d", mediumState);
+
+ TEST_COM_FAILURE(hTest, pMedium->LockWrite(pToken2.asOutParam()), "read+write lock succeeded");
+ if (!pToken2.isNull())
+ RTTestFailed(hTest, "pToken2 is not null");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock read state");
+ if (mediumState != MediumState_LockedRead)
+ RTTestFailed(hTest, "wrong after nested lock read medium state %d", mediumState);
+
+ if (!pToken1.isNull())
+ TEST_COM_SUCCESS(hTest, pToken1->Abandon(), "read unlock");
+ else
+ RTTestFailed(hTest, "pToken1 is null");
+
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock read state");
+ if (mediumState != MediumState_Created)
+ RTTestFailed(hTest, "wrong unlock read medium state %d", mediumState);
+ }
+
+ /* Cleanup, also part of the testcase */
+
+ if (!pMedium.isNull())
+ {
+ RTTestSub(hTest, "Closing medium");
+ MediumState_T mediumState = MediumState_NotCreated;
+ TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
+ if (mediumState == MediumState_Created)
+ {
+ ComPtr<IProgress> pProgress;
+ TEST_COM_SUCCESS(hTest, pMedium->DeleteStorage(pProgress.asOutParam()), "deleting storage");
+ if (!pProgress.isNull())
+ TEST_COM_SUCCESS(hTest, pProgress->WaitForCompletion(30000), "waiting for completion of delete");
+ }
+ TEST_COM_SUCCESS(hTest, pMedium->Close(), "closing");
+ pMedium.setNull();
+ }
+
+ pVirtualBox.setNull();
+ pVirtualBoxClient.setNull();
+
+ /* Make sure that there are no object references alive here, XPCOM does
+ * a very bad job at cleaning up such leftovers, spitting out warning
+ * messages in a debug build. */
+
+ if (fComInit)
+ {
+ RTTestIPrintf(RTTESTLVL_DEBUG, "Shutting down COM...\n");
+ Shutdown();
+ }
+
+ /*
+ * Summary.
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
diff --git a/src/VBox/Main/testcase/tstOVF.cpp b/src/VBox/Main/testcase/tstOVF.cpp
new file mode 100644
index 00000000..ba0500f3
--- /dev/null
+++ b/src/VBox/Main/testcase/tstOVF.cpp
@@ -0,0 +1,435 @@
+/* $Id: tstOVF.cpp $ */
+/** @file
+ *
+ * tstOVF - testcases for OVF import and export
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <VBox/com/VirtualBox.h>
+
+#include <VBox/com/com.h>
+#include <VBox/com/array.h>
+#include <VBox/com/string.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+
+#include <list>
+
+using namespace com;
+
+// main
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Quick hack exception structure.
+ *
+ */
+struct MyError
+{
+ MyError(HRESULT rc,
+ const char *pcsz,
+ IProgress *pProgress = NULL)
+ : m_rc(rc)
+ {
+ m_str = "ERROR: ";
+ m_str += pcsz;
+
+ if (pProgress)
+ {
+ com::ProgressErrorInfo info(pProgress);
+ com::GluePrintErrorInfo(info);
+ }
+ else if (rc != S_OK)
+ {
+ com::ErrorInfo info;
+ if (!info.isFullAvailable() && !info.isBasicAvailable())
+ com::GluePrintRCMessage(rc);
+ else
+ com::GluePrintErrorInfo(info);
+ }
+ }
+
+ Utf8Str m_str;
+ HRESULT m_rc;
+};
+
+/**
+ * Imports the given OVF file, with all bells and whistles.
+ * Throws MyError on errors.
+ * @param pcszPrefix Descriptive short prefix string for console output.
+ * @param pVirtualBox VirtualBox instance.
+ * @param pcszOVF0 File to import.
+ * @param llMachinesCreated out: UUIDs of machines that were created so that caller can clean up.
+ */
+void importOVF(const char *pcszPrefix,
+ ComPtr<IVirtualBox> &pVirtualBox,
+ const char *pcszOVF0,
+ std::list<Guid> &llMachinesCreated)
+{
+ char szAbsOVF[RTPATH_MAX];
+ RTPathExecDir(szAbsOVF, sizeof(szAbsOVF));
+ RTPathAppend(szAbsOVF, sizeof(szAbsOVF), pcszOVF0);
+
+ RTPrintf("%s: reading appliance \"%s\"...\n", pcszPrefix, szAbsOVF);
+ ComPtr<IAppliance> pAppl;
+ HRESULT rc = pVirtualBox->CreateAppliance(pAppl.asOutParam());
+ if (FAILED(rc)) throw MyError(rc, "failed to create appliance\n");
+
+ ComPtr<IProgress> pProgress;
+ rc = pAppl->Read(Bstr(szAbsOVF).raw(), pProgress.asOutParam());
+ if (FAILED(rc)) throw MyError(rc, "Appliance::Read() failed\n");
+ rc = pProgress->WaitForCompletion(-1);
+ if (FAILED(rc)) throw MyError(rc, "Progress::WaitForCompletion() failed\n");
+ LONG rc2;
+ pProgress->COMGETTER(ResultCode)(&rc2);
+ if (FAILED(rc2)) throw MyError(rc2, "Appliance::Read() failed\n", pProgress);
+
+ RTPrintf("%s: interpreting appliance \"%s\"...\n", pcszPrefix, szAbsOVF);
+ rc = pAppl->Interpret();
+ if (FAILED(rc)) throw MyError(rc, "Appliance::Interpret() failed\n");
+
+ com::SafeIfaceArray<IVirtualSystemDescription> aDescriptions;
+ rc = pAppl->COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aDescriptions));
+ for (uint32_t u = 0;
+ u < aDescriptions.size();
+ ++u)
+ {
+ ComPtr<IVirtualSystemDescription> pVSys = aDescriptions[u];
+
+ com::SafeArray<VirtualSystemDescriptionType_T> aTypes;
+ com::SafeArray<BSTR> aRefs;
+ com::SafeArray<BSTR> aOvfValues;
+ com::SafeArray<BSTR> aVBoxValues;
+ com::SafeArray<BSTR> aExtraConfigValues;
+ rc = pVSys->GetDescription(ComSafeArrayAsOutParam(aTypes),
+ ComSafeArrayAsOutParam(aRefs),
+ ComSafeArrayAsOutParam(aOvfValues),
+ ComSafeArrayAsOutParam(aVBoxValues),
+ ComSafeArrayAsOutParam(aExtraConfigValues));
+ if (FAILED(rc)) throw MyError(rc, "VirtualSystemDescription::GetDescription() failed\n");
+
+ for (uint32_t u2 = 0;
+ u2 < aTypes.size();
+ ++u2)
+ {
+ const char *pcszType;
+
+ VirtualSystemDescriptionType_T t = aTypes[u2];
+ switch (t)
+ {
+ case VirtualSystemDescriptionType_OS:
+ pcszType = "ostype";
+ break;
+
+ case VirtualSystemDescriptionType_Name:
+ pcszType = "name";
+ break;
+
+ case VirtualSystemDescriptionType_Product:
+ pcszType = "product";
+ break;
+
+ case VirtualSystemDescriptionType_ProductUrl:
+ pcszType = "producturl";
+ break;
+
+ case VirtualSystemDescriptionType_Vendor:
+ pcszType = "vendor";
+ break;
+
+ case VirtualSystemDescriptionType_VendorUrl:
+ pcszType = "vendorurl";
+ break;
+
+ case VirtualSystemDescriptionType_Version:
+ pcszType = "version";
+ break;
+
+ case VirtualSystemDescriptionType_Description:
+ pcszType = "description";
+ break;
+
+ case VirtualSystemDescriptionType_License:
+ pcszType = "license";
+ break;
+
+ case VirtualSystemDescriptionType_CPU:
+ pcszType = "cpu";
+ break;
+
+ case VirtualSystemDescriptionType_Memory:
+ pcszType = "memory";
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskControllerIDE:
+ pcszType = "ide";
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskControllerSATA:
+ pcszType = "sata";
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskControllerSAS:
+ pcszType = "sas";
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskControllerSCSI:
+ pcszType = "scsi";
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskControllerVirtioSCSI:
+ pcszType = "virtio-scsi";
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskControllerNVMe:
+ pcszType = "nvme";
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskImage:
+ pcszType = "hd";
+ break;
+
+ case VirtualSystemDescriptionType_CDROM:
+ pcszType = "cdrom";
+ break;
+
+ case VirtualSystemDescriptionType_Floppy:
+ pcszType = "floppy";
+ break;
+
+ case VirtualSystemDescriptionType_NetworkAdapter:
+ pcszType = "net";
+ break;
+
+ case VirtualSystemDescriptionType_USBController:
+ pcszType = "usb";
+ break;
+
+ case VirtualSystemDescriptionType_SoundCard:
+ pcszType = "sound";
+ break;
+
+ case VirtualSystemDescriptionType_SettingsFile:
+ pcszType = "settings";
+ break;
+
+ case VirtualSystemDescriptionType_BaseFolder:
+ pcszType = "basefolder";
+ break;
+
+ case VirtualSystemDescriptionType_PrimaryGroup:
+ pcszType = "primarygroup";
+ break;
+
+ default:
+ throw MyError(E_UNEXPECTED, "Invalid VirtualSystemDescriptionType (enum)\n");
+ break;
+ }
+
+ RTPrintf(" vsys %2u item %2u: type %2d (%s), ovf: \"%ls\", vbox: \"%ls\", extra: \"%ls\"\n",
+ u, u2, t, pcszType,
+ aOvfValues[u2],
+ aVBoxValues[u2],
+ aExtraConfigValues[u2]);
+ }
+ }
+
+ RTPrintf("%s: importing %d machine(s)...\n", pcszPrefix, aDescriptions.size());
+ SafeArray<ImportOptions_T> sfaOptions;
+ rc = pAppl->ImportMachines(ComSafeArrayAsInParam(sfaOptions), pProgress.asOutParam());
+ if (FAILED(rc)) throw MyError(rc, "Appliance::ImportMachines() failed\n");
+ rc = pProgress->WaitForCompletion(-1);
+ if (FAILED(rc)) throw MyError(rc, "Progress::WaitForCompletion() failed\n");
+ pProgress->COMGETTER(ResultCode)(&rc2);
+ if (FAILED(rc2)) throw MyError(rc2, "Appliance::ImportMachines() failed\n", pProgress);
+
+ com::SafeArray<BSTR> aMachineUUIDs;
+ rc = pAppl->COMGETTER(Machines)(ComSafeArrayAsOutParam(aMachineUUIDs));
+ if (FAILED(rc)) throw MyError(rc, "Appliance::GetMachines() failed\n");
+
+ for (size_t u = 0;
+ u < aMachineUUIDs.size();
+ ++u)
+ {
+ RTPrintf("%s: created machine %u: %ls\n", pcszPrefix, u, aMachineUUIDs[u]);
+ llMachinesCreated.push_back(Guid(Bstr(aMachineUUIDs[u])));
+ }
+
+ RTPrintf("%s: success!\n", pcszPrefix);
+}
+
+/**
+ * Copies ovf-testcases/ovf-dummy.vmdk to the given target and appends that
+ * target as a string to the given list so that the caller can delete it
+ * again later.
+ * @param llFiles2Delete List of strings to append the target file path to.
+ * @param pcszDest Target for dummy VMDK.
+ */
+void copyDummyDiskImage(const char *pcszPrefix,
+ std::list<Utf8Str> &llFiles2Delete,
+ const char *pcszDest)
+{
+ char szSrc[RTPATH_MAX];
+ RTPathExecDir(szSrc, sizeof(szSrc));
+ RTPathAppend(szSrc, sizeof(szSrc), "ovf-testcases/ovf-dummy.vmdk");
+
+ char szDst[RTPATH_MAX];
+ RTPathExecDir(szDst, sizeof(szDst));
+ RTPathAppend(szDst, sizeof(szDst), pcszDest);
+ RTPrintf("%s: copying ovf-dummy.vmdk to \"%s\"...\n", pcszPrefix, pcszDest);
+
+ /* Delete the destination file if it exists or RTFileCopy will fail. */
+ if (RTFileExists(szDst))
+ {
+ RTPrintf("Deleting file %s...\n", szDst);
+ RTFileDelete(szDst);
+ }
+
+ int vrc = RTFileCopy(szSrc, szDst);
+ if (RT_FAILURE(vrc)) throw MyError(0, Utf8StrFmt("Cannot copy ovf-dummy.vmdk to %s: %Rra\n", pcszDest, vrc).c_str());
+ llFiles2Delete.push_back(szDst);
+}
+
+/**
+ *
+ * @param argc
+ * @param argv[]
+ * @return
+ */
+int main(int argc, char *argv[])
+{
+ RTR3InitExe(argc, &argv, 0);
+
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ HRESULT rc = S_OK;
+
+ std::list<Utf8Str> llFiles2Delete;
+ std::list<Guid> llMachinesCreated;
+
+ ComPtr<IVirtualBoxClient> pVirtualBoxClient;
+ ComPtr<IVirtualBox> pVirtualBox;
+
+ try
+ {
+ RTPrintf("Initializing COM...\n");
+ rc = com::Initialize();
+ if (FAILED(rc)) throw MyError(rc, "failed to initialize COM!\n");
+
+ ComPtr<ISession> pSession;
+
+ RTPrintf("Creating VirtualBox object...\n");
+ rc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
+ if (SUCCEEDED(rc))
+ rc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
+ if (FAILED(rc)) throw MyError(rc, "failed to create the VirtualBox object!\n");
+
+ rc = pSession.createInprocObject(CLSID_Session);
+ if (FAILED(rc)) throw MyError(rc, "failed to create a session object!\n");
+
+ // for each testcase, we will copy the dummy VMDK image to the subdirectory with the OVF testcase
+ // so that the import will find the disks it expects; this is just for testing the import since
+ // the imported machines will obviously not be usable.
+ // llFiles2Delete receives the paths of all the files that we need to clean up later.
+
+ // testcase 1: import ovf-joomla-0.9/joomla-1.1.4-ovf.ovf
+ copyDummyDiskImage("joomla-0.9", llFiles2Delete, "ovf-testcases/ovf-joomla-0.9/joomla-1.1.4-ovf-0.vmdk");
+ copyDummyDiskImage("joomla-0.9", llFiles2Delete, "ovf-testcases/ovf-joomla-0.9/joomla-1.1.4-ovf-1.vmdk");
+ importOVF("joomla-0.9", pVirtualBox, "ovf-testcases/ovf-joomla-0.9/joomla-1.1.4-ovf.ovf", llMachinesCreated);
+
+ // testcase 2: import ovf-winxp-vbox-sharedfolders/winxp.ovf
+ copyDummyDiskImage("winxp-vbox-sharedfolders", llFiles2Delete, "ovf-testcases/ovf-winxp-vbox-sharedfolders/Windows 5.1 XP 1 merged.vmdk");
+ copyDummyDiskImage("winxp-vbox-sharedfolders", llFiles2Delete, "ovf-testcases/ovf-winxp-vbox-sharedfolders/smallvdi.vmdk");
+ importOVF("winxp-vbox-sharedfolders", pVirtualBox, "ovf-testcases/ovf-winxp-vbox-sharedfolders/winxp.ovf", llMachinesCreated);
+
+ // testcase 3: import ovf-winxp-vbox-sharedfolders/winxp.ovf
+ importOVF("winhost-audio-nodisks", pVirtualBox, "ovf-testcases/ovf-winhost-audio-nodisks/WinXP.ovf", llMachinesCreated);
+
+ RTPrintf("Machine imports done, no errors. Cleaning up...\n");
+ }
+ catch (MyError &e)
+ {
+ rc = e.m_rc;
+ RTPrintf("%s", e.m_str.c_str());
+ rcExit = RTEXITCODE_FAILURE;
+ }
+
+ try
+ {
+ // clean up the machines created
+ for (std::list<Guid>::const_iterator it = llMachinesCreated.begin();
+ it != llMachinesCreated.end();
+ ++it)
+ {
+ const Guid &uuid = *it;
+ Bstr bstrUUID(uuid.toUtf16());
+ ComPtr<IMachine> pMachine;
+ rc = pVirtualBox->FindMachine(bstrUUID.raw(), pMachine.asOutParam());
+ if (FAILED(rc)) throw MyError(rc, "VirtualBox::FindMachine() failed\n");
+
+ RTPrintf(" Deleting machine %ls...\n", bstrUUID.raw());
+ SafeIfaceArray<IMedium> sfaMedia;
+ rc = pMachine->Unregister(CleanupMode_DetachAllReturnHardDisksOnly,
+ ComSafeArrayAsOutParam(sfaMedia));
+ if (FAILED(rc)) throw MyError(rc, "Machine::Unregister() failed\n");
+
+ ComPtr<IProgress> pProgress;
+ rc = pMachine->DeleteConfig(ComSafeArrayAsInParam(sfaMedia), pProgress.asOutParam());
+ if (FAILED(rc)) throw MyError(rc, "Machine::DeleteSettings() failed\n");
+ rc = pProgress->WaitForCompletion(-1);
+ if (FAILED(rc)) throw MyError(rc, "Progress::WaitForCompletion() failed\n");
+ }
+ }
+ catch (MyError &e)
+ {
+ rc = e.m_rc;
+ RTPrintf("%s", e.m_str.c_str());
+ rcExit = RTEXITCODE_FAILURE;
+ }
+
+ // clean up the VMDK copies that we made in copyDummyDiskImage()
+ for (std::list<Utf8Str>::const_iterator it = llFiles2Delete.begin();
+ it != llFiles2Delete.end();
+ ++it)
+ {
+ const Utf8Str &strFile = *it;
+ RTPrintf("Deleting file %s...\n", strFile.c_str());
+ RTFileDelete(strFile.c_str());
+ }
+
+ pVirtualBox.setNull();
+ pVirtualBoxClient.setNull();
+
+ RTPrintf("Shutting down COM...\n");
+ com::Shutdown();
+ RTPrintf("tstOVF all done: %s\n", rcExit ? "ERROR" : "SUCCESS");
+
+ return rcExit;
+}
+
diff --git a/src/VBox/Main/testcase/tstSafeArray.cpp b/src/VBox/Main/testcase/tstSafeArray.cpp
new file mode 100644
index 00000000..8a671363
--- /dev/null
+++ b/src/VBox/Main/testcase/tstSafeArray.cpp
@@ -0,0 +1,106 @@
+/* $Id: tstSafeArray.cpp $ */
+/** @file
+ * API Glue Testcase - SafeArray.
+ */
+
+/*
+ * Copyright (C) 2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/array.h>
+#include <VBox/com/string.h>
+
+#include <iprt/mem.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+#include <iprt/uni.h>
+
+
+int main()
+{
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstSafeArray", &hTest);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ RTTestBanner(hTest);
+
+ /* Sizes / Pre-allocations. */
+ com::SafeArray<int> aInt;
+ RTTESTI_CHECK(aInt.size() == 0);
+
+ com::SafeArray<int> aInt2(42);
+ RTTESTI_CHECK(aInt2.size() == 42);
+ aInt2.setNull();
+ RTTESTI_CHECK(aInt2.size() == 0);
+ aInt2.resize(42);
+ RTTESTI_CHECK(aInt2.size() == 42);
+ aInt2.setNull();
+
+ com::SafeArray<int> aInt3((size_t)0);
+ RTTESTI_CHECK(aInt3.size() == 0);
+ aInt3.setNull();
+ RTTESTI_CHECK(aInt3.size() == 0);
+
+ /* Push to back. */
+ int aPushToBack[] = { 51, 52, 53 };
+ for (size_t i = 0; i < RT_ELEMENTS(aPushToBack); i++)
+ {
+ RTTESTI_CHECK(aInt.push_back(aPushToBack[i]));
+ RTTESTI_CHECK(aInt.size() == i + 1);
+ RTTESTI_CHECK(aInt[i] == aPushToBack[i]);
+ }
+ for (size_t i = 0; i < RT_ELEMENTS(aPushToBack); i++)
+ RTTESTI_CHECK_MSG(aInt[i] == aPushToBack[i], ("Got %d, expected %d\n", aInt[i], aPushToBack[i]));
+
+ aInt.setNull();
+
+ /* Push to front. */
+ int aPushToFront[] = { 41, 42, 43 };
+ for (size_t i = 0; i < RT_ELEMENTS(aPushToFront); i++)
+ {
+ RTTESTI_CHECK(aInt.push_front(aPushToFront[i]));
+ RTTESTI_CHECK(aInt.size() == i + 1);
+ RTTESTI_CHECK(aInt[0] == aPushToFront[i]);
+ }
+ for (size_t i = 0; i < RT_ELEMENTS(aPushToFront); i++)
+ RTTESTI_CHECK_MSG(aInt[i] == aPushToFront[RT_ELEMENTS(aPushToFront) - i - 1],
+ ("Got %d, expected %d\n", aInt[i], aPushToFront[RT_ELEMENTS(aPushToFront) - i - 1]));
+
+ /* A bit more data. */
+ aInt.setNull();
+ for (size_t i = 0; i < RTRandU32Ex(_4K, _64M); i++)
+ {
+ RTTESTI_CHECK(aInt.push_front(42));
+ RTTESTI_CHECK(aInt.push_back(41));
+ RTTESTI_CHECK(aInt.size() == (i + 1) * 2);
+ }
+ aInt.setNull();
+
+ rcExit = RTTestSummaryAndDestroy(hTest);
+ }
+ return rcExit;
+}
+
diff --git a/src/VBox/Main/testcase/tstUSBLinux.h b/src/VBox/Main/testcase/tstUSBLinux.h
new file mode 100644
index 00000000..39557c21
--- /dev/null
+++ b/src/VBox/Main/testcase/tstUSBLinux.h
@@ -0,0 +1,79 @@
+/* $Id: tstUSBLinux.h $ */
+/** @file
+ * VirtualBox USB Proxy Service class, test version for Linux hosts.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef MAIN_INCLUDED_SRC_testcase_tstUSBLinux_h
+#define MAIN_INCLUDED_SRC_testcase_tstUSBLinux_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+typedef int HRESULT;
+enum { S_OK = 0, E_NOTIMPL = 1 };
+
+#include <VBox/usb.h>
+#include <VBox/usbfilter.h>
+
+#include <VBox/err.h>
+
+#ifdef VBOX_USB_WITH_SYSFS
+# include <libhal.h>
+#endif
+
+#include <stdio.h>
+/**
+ * The Linux hosted USB Proxy Service.
+ */
+class USBProxyServiceLinux
+{
+public:
+ USBProxyServiceLinux()
+ : mLastError(VINF_SUCCESS)
+ {}
+
+ HRESULT initSysfs(void);
+ PUSBDEVICE getDevicesFromSysfs(void);
+ int getLastError(void)
+ {
+ return mLastError;
+ }
+
+private:
+ int start(void) { return VINF_SUCCESS; }
+ static void freeDevice(PUSBDEVICE) {} /* We don't care about leaks in a test. */
+ int usbProbeInterfacesFromLibhal(const char *pszHalUuid, PUSBDEVICE pDev);
+ int mLastError;
+# ifdef VBOX_USB_WITH_SYSFS
+ /** Our connection to DBus for getting information from hal. This will be
+ * NULL if the initialisation failed. */
+ DBusConnection *mDBusConnection;
+ /** Handle to libhal. */
+ LibHalContext *mLibHalContext;
+# endif
+};
+
+#endif /* !MAIN_INCLUDED_SRC_testcase_tstUSBLinux_h */
+
diff --git a/src/VBox/Main/testcase/tstUSBProxyLinux.cpp b/src/VBox/Main/testcase/tstUSBProxyLinux.cpp
new file mode 100644
index 00000000..41f885d9
--- /dev/null
+++ b/src/VBox/Main/testcase/tstUSBProxyLinux.cpp
@@ -0,0 +1,195 @@
+/* $Id: tstUSBProxyLinux.cpp $ */
+/** @file
+ * USBProxyBackendLinux test case.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#include "USBGetDevices.h"
+
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+
+/*** BEGIN STUBS ***/
+
+static struct
+{
+ const char *pcszEnvUsb;
+ const char *pcszEnvUsbRoot;
+ const char *pcszDevicesRoot;
+ bool fDevicesAccessible;
+ const char *pcszUsbfsRoot;
+ bool fUsbfsAccessible;
+ int rcMethodInit;
+ const char *pcszDevicesRootExpected;
+ bool fUsingUsbfsExpected;
+ int rcExpected;
+} s_testEnvironment[] =
+{
+ /* "sysfs" and valid root in the environment */
+ { "sysfs", "/dev/bus/usb", "/dev/bus/usb", true, NULL, false, VINF_SUCCESS, "/dev/bus/usb", false, VINF_SUCCESS },
+ /* "sysfs" and bad root in the environment */
+ { "sysfs", "/dev/bus/usb", "/dev/vboxusb", false, "/proc/usb/bus", false, VINF_SUCCESS, "", true, VERR_NOT_FOUND },
+ /* "sysfs" and no root in the environment */
+ { "sysfs", NULL, "/dev/vboxusb", true, NULL, false, VINF_SUCCESS, "/dev/vboxusb", false, VINF_SUCCESS },
+ /* "usbfs" and valid root in the environment */
+ { "usbfs", "/dev/bus/usb", NULL, false, "/dev/bus/usb", true, VINF_SUCCESS, "/dev/bus/usb", true, VINF_SUCCESS },
+ /* "usbfs" and bad root in the environment */
+ { "usbfs", "/dev/bus/usb", "/dev/vboxusb", false, "/proc/usb/bus", false, VINF_SUCCESS, "", true, VERR_NOT_FOUND },
+ /* "usbfs" and no root in the environment */
+ { "usbfs", NULL, NULL, false, "/proc/bus/usb", true, VINF_SUCCESS, "/proc/bus/usb", true, VINF_SUCCESS },
+ /* invalid method in the environment, sysfs available */
+ { "invalid", "/dev/bus/usb", "/dev/vboxusb", true, NULL, false, VINF_SUCCESS, "/dev/vboxusb", false, VINF_SUCCESS },
+ /* invalid method in the environment, usbfs available */
+ { "invalid", "/dev/bus/usb", NULL, true, "/proc/bus/usb", true, VINF_SUCCESS, "/proc/bus/usb", true, VINF_SUCCESS },
+ /* invalid method in the environment, sysfs inaccessible */
+ { "invalid", "/dev/bus/usb", "/dev/vboxusb", false, NULL, false, VINF_SUCCESS, "", true, VERR_VUSB_USB_DEVICE_PERMISSION },
+ /* invalid method in the environment, usbfs inaccessible */
+ { "invalid", "/dev/bus/usb", NULL, false, "/proc/bus/usb", false, VINF_SUCCESS, "", true, VERR_VUSB_USBFS_PERMISSION },
+ /* No environment, sysfs and usbfs available but without access permissions. */
+ { NULL, NULL, "/dev/vboxusb", false, "/proc/bus/usb", false, VERR_NO_MEMORY, "", true, VERR_VUSB_USB_DEVICE_PERMISSION },
+ /* No environment, sysfs and usbfs available, access permissions for sysfs. */
+ { NULL, NULL, "/dev/vboxusb", true, "/proc/bus/usb", false, VINF_SUCCESS, "/dev/vboxusb", false, VINF_SUCCESS },
+ /* No environment, sysfs and usbfs available, access permissions for usbfs. */
+ { NULL, NULL, "/dev/vboxusb", false, "/proc/bus/usb", true, VINF_SUCCESS, "/proc/bus/usb", true, VINF_SUCCESS },
+ /* No environment, sysfs available but without access permissions. */
+ { NULL, NULL, "/dev/vboxusb", false, NULL, false, VERR_NO_MEMORY, "", true, VERR_VUSB_USB_DEVICE_PERMISSION },
+ /* No environment, usbfs available but without access permissions. */
+ { NULL, NULL, NULL, false, "/proc/bus/usb", false, VERR_NO_MEMORY, "", true, VERR_VUSB_USBFS_PERMISSION },
+};
+
+static void testInit(RTTEST hTest)
+{
+ RTTestSub(hTest, "Testing USBProxyLinuxChooseMethod");
+ for (unsigned i = 0; i < RT_ELEMENTS(s_testEnvironment); ++i)
+ {
+ bool fUsingUsbfs = true;
+ const char *pcszDevicesRoot = "";
+
+ TestUSBSetEnv(s_testEnvironment[i].pcszEnvUsb,
+ s_testEnvironment[i].pcszEnvUsbRoot);
+ TestUSBSetupInit(s_testEnvironment[i].pcszUsbfsRoot,
+ s_testEnvironment[i].fUsbfsAccessible,
+ s_testEnvironment[i].pcszDevicesRoot,
+ s_testEnvironment[i].fDevicesAccessible,
+ s_testEnvironment[i].rcMethodInit);
+ int rc = USBProxyLinuxChooseMethod(&fUsingUsbfs, &pcszDevicesRoot);
+ RTTESTI_CHECK_MSG(rc == s_testEnvironment[i].rcExpected,
+ ("rc=%Rrc (test index %i) instead of %Rrc!\n",
+ rc, i, s_testEnvironment[i].rcExpected));
+ RTTESTI_CHECK_MSG(!RTStrCmp(pcszDevicesRoot,
+ s_testEnvironment[i].pcszDevicesRootExpected),
+ ("testGetDevicesRoot() returned %s (test index %i) instead of %s!\n",
+ pcszDevicesRoot, i,
+ s_testEnvironment[i].pcszDevicesRootExpected));
+ RTTESTI_CHECK_MSG( fUsingUsbfs
+ == s_testEnvironment[i].fUsingUsbfsExpected,
+ ("testGetUsingUsbfs() returned %RTbool (test index %i) instead of %RTbool!\n",
+ fUsingUsbfs, i,
+ s_testEnvironment[i].fUsingUsbfsExpected));
+ }
+}
+
+static struct
+{
+ const char *pacszDeviceAddresses[16];
+ const char *pacszAccessibleFiles[16];
+ const char *pcszRoot;
+ bool fIsDeviceNodes;
+ bool fAvailableExpected;
+} s_testCheckDeviceRoot[] =
+{
+ /* /dev/vboxusb accessible -> device nodes method available */
+ { { NULL }, { "/dev/vboxusb" }, "/dev/vboxusb", true, true },
+ /* /dev/vboxusb present but not accessible -> device nodes method not
+ * available */
+ { { NULL }, { NULL }, "/dev/vboxusb", true, false },
+ /* /proc/bus/usb available but empty -> usbfs method available (we can't
+ * really check in this case) */
+ { { NULL }, { "/proc/bus/usb" }, "/proc/bus/usb", false, true },
+ /* /proc/bus/usb not available or not accessible -> usbfs method not available */
+ { { NULL }, { NULL }, "/proc/bus/usb", false, false },
+ /* /proc/bus/usb available, one inaccessible device -> usbfs method not
+ * available */
+ { { "/proc/bus/usb/001/001" }, { "/proc/bus/usb" }, "/proc/bus/usb", false, false },
+ /* /proc/bus/usb available, one device of two inaccessible -> usbfs method
+ * not available */
+ { { "/proc/bus/usb/001/001", "/proc/bus/usb/002/002" },
+ { "/proc/bus/usb", "/proc/bus/usb/001/001" }, "/proc/bus/usb", false, false },
+ /* /proc/bus/usb available, two accessible devices -> usbfs method
+ * available */
+ { { "/proc/bus/usb/001/001", "/proc/bus/usb/002/002" },
+ { "/proc/bus/usb", "/proc/bus/usb/001/001", "/proc/bus/usb/002/002" },
+ "/proc/bus/usb", false, true }
+};
+
+static void testCheckDeviceRoot(RTTEST hTest)
+{
+ RTTestSub(hTest, "Testing the USBProxyLinuxCheckDeviceRoot API");
+ for (unsigned i = 0; i < RT_ELEMENTS(s_testCheckDeviceRoot); ++i)
+ {
+ TestUSBSetAvailableUsbfsDevices(s_testCheckDeviceRoot[i]
+ .pacszDeviceAddresses);
+ TestUSBSetAccessibleFiles(s_testCheckDeviceRoot[i]
+ .pacszAccessibleFiles);
+ bool fAvailable = USBProxyLinuxCheckDeviceRoot
+ (s_testCheckDeviceRoot[i].pcszRoot,
+ s_testCheckDeviceRoot[i].fIsDeviceNodes);
+ RTTESTI_CHECK_MSG( fAvailable
+ == s_testCheckDeviceRoot[i].fAvailableExpected,
+ ("USBProxyLinuxCheckDeviceRoot() returned %RTbool (test index %i) instead of %RTbool!\n",
+ fAvailable, i,
+ s_testCheckDeviceRoot[i].fAvailableExpected));
+ }
+}
+
+int main(void)
+{
+ /*
+ * Init the runtime, test and say hello.
+ */
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstUSBProxyLinux", &hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(hTest);
+
+ /*
+ * Run the tests.
+ */
+ testInit(hTest);
+ testCheckDeviceRoot(hTest);
+
+ /*
+ * Summary
+ */
+ return RTTestSummaryAndDestroy(hTest);
+}
diff --git a/src/VBox/Main/testcase/tstUnattendedScript-1.expected b/src/VBox/Main/testcase/tstUnattendedScript-1.expected
new file mode 100644
index 00000000..1457a2e7
--- /dev/null
+++ b/src/VBox/Main/testcase/tstUnattendedScript-1.expected
@@ -0,0 +1,384 @@
+/*
+ * Regular conditions.
+ */
+01=true
+02=false
+03=true
+04=true
+05=true
+06=false
+07=true
+08=false
+09=true
+
+/*
+ * Expression conditions.
+ */
+01=true
+02=false
+03=true
+04=true
+05=true
+06=false
+07=true
+08=false
+09=true
+
+
+/*
+ * Regular inserts.
+ */
+/* variables */
+01=vboxuser
+02=changeme
+03=changeme
+04=VBox & VBox;
+05=911
+06=/bin/post-install-command arg1 arg2 --amp=& --lt=< --gt=> --dq-word="word" --sq-word='word'
+07=/aux/install/dir
+08=42
+09=x86
+10=x86
+11=i386
+12=i486
+13=i686
+14=3.4.2
+15=3
+16=cet
+17=cet
+18=cet
+19=dk_DK
+20=dk-DK
+21=dk
+22=DK
+23=my-extra-long-name.hostname.com
+24=my-extra-long-name
+25=my-extra-long-n
+26=hostname.com
+27=http://proxy.intranet.com:443
+
+/* indicators */
+01=1
+02=0
+03=1
+04=1
+05=1
+06=0
+07=1
+08=0
+09=1
+
+
+/*
+ * Expression inserts
+ */
+/* variables */
+01=vboxuser
+02=changeme
+03=changeme
+04=VBox & VBox;
+05=911
+06=/bin/post-install-command arg1 arg2 --amp=& --lt=< --gt=> --dq-word="word" --sq-word='word'
+07=/aux/install/dir
+08=42
+09=x86
+10=x86
+11=i386
+12=i486
+13=i686
+14=3.4.2
+15=3
+16=cet
+17=cet
+18=cet
+19=dk_DK
+20=dk-DK
+21=dk
+22=DK
+23=my-extra-long-name.hostname.com
+24=my-extra-long-name
+25=my-extra-long-n
+26=hostname.com
+27=http://proxy.intranet.com:443
+
+/* indicators */
+01=1
+02=0
+03=1
+04=1
+05=1
+06=0
+07=1
+08=0
+09=1
+
+
+/*
+ * Regular inserts with shell quoting.
+ */
+/* variables */
+01=vboxuser
+02=changeme
+03=changeme
+04='VBox & VBox;'
+05=911
+06='/bin/post-install-command arg1 arg2 --amp=& --lt=< --gt=> --dq-word="word" --sq-word='"'"'word'"'"''
+07=/aux/install/dir
+08=42
+09=x86
+10=x86
+11=i386
+12=i486
+13=i686
+14=3.4.2
+15=3
+16=cet
+17=cet
+18=cet
+19=dk_DK
+20=dk-DK
+21=dk
+22=DK
+23=my-extra-long-name.hostname.com
+24=my-extra-long-name
+25=my-extra-long-n
+26=hostname.com
+27=http://proxy.intranet.com:443
+
+/* indicators */
+01=1
+02=0
+03=1
+04=1
+05=1
+06=0
+07=1
+08=0
+09=1
+
+
+/*
+ * Expression inserts with shell quoting.
+ */
+/* variables */
+01=vboxuser
+02=changeme
+03=changeme
+04='VBox & VBox;'
+05=911
+06='/bin/post-install-command arg1 arg2 --amp=& --lt=< --gt=> --dq-word="word" --sq-word='"'"'word'"'"''
+07=/aux/install/dir
+08=42
+09=x86
+10=x86
+11=i386
+12=i486
+13=i686
+14=3.4.2
+15=3
+16=cet
+17=cet
+18=cet
+19=dk_DK
+20=dk-DK
+21=dk
+22=DK
+23=my-extra-long-name.hostname.com
+24=my-extra-long-name
+25=my-extra-long-n
+26=hostname.com
+27=http://proxy.intranet.com:443
+
+/* indicators */
+01=1
+02=0
+03=1
+04=1
+05=1
+06=0
+07=1
+08=0
+09=1
+
+
+/*
+ * Regular inserts escaped for use in XML element.
+ */
+/* variables */
+01=vboxuser
+02=changeme
+03=changeme
+04=VBox &amp; VBox;
+05=911
+06=/bin/post-install-command arg1 arg2 --amp=&amp; --lt=&lt; --gt=&gt; --dq-word=&quot;word&quot; --sq-word=&apos;word&apos;
+07=/aux/install/dir
+08=42
+09=x86
+10=x86
+11=i386
+12=i486
+13=i686
+14=3.4.2
+15=3
+16=cet
+17=cet
+18=cet
+19=dk_DK
+20=dk-DK
+21=dk
+22=DK
+23=my-extra-long-name.hostname.com
+24=my-extra-long-name
+25=my-extra-long-n
+26=hostname.com
+27=http://proxy.intranet.com:443
+
+/* indicators */
+01=1
+02=0
+03=1
+04=1
+05=1
+06=0
+07=1
+08=0
+09=1
+
+
+/*
+ * Expression inserts escaped for use in XML element.
+ */
+/* variables */
+01=vboxuser
+02=changeme
+03=changeme
+04=VBox &amp; VBox;
+05=911
+06=/bin/post-install-command arg1 arg2 --amp=&amp; --lt=&lt; --gt=&gt; --dq-word=&quot;word&quot; --sq-word=&apos;word&apos;
+07=/aux/install/dir
+08=42
+09=x86
+10=x86
+11=i386
+12=i486
+13=i686
+14=3.4.2
+15=3
+16=cet
+17=cet
+18=cet
+19=dk_DK
+20=dk-DK
+21=dk
+22=DK
+23=my-extra-long-name.hostname.com
+24=my-extra-long-name
+25=my-extra-long-n
+26=hostname.com
+27=http://proxy.intranet.com:443
+
+/* indicators */
+01=1
+02=0
+03=1
+04=1
+05=1
+06=0
+07=1
+08=0
+09=1
+
+
+/*
+ * Regular inserts escaped for use in double quoted attributes.
+ */
+/* variables */
+01=vboxuser
+02=changeme
+03=changeme
+04=VBox &amp; VBox;
+05=911
+06=/bin/post-install-command arg1 arg2 --amp=&amp; --lt=&lt; --gt=&gt; --dq-word=&quot;word&quot; --sq-word='word'
+07=/aux/install/dir
+08=42
+09=x86
+10=x86
+11=i386
+12=i486
+13=i686
+14=3.4.2
+15=3
+16=cet
+17=cet
+18=cet
+19=dk_DK
+20=dk-DK
+21=dk
+22=DK
+23=my-extra-long-name.hostname.com
+24=my-extra-long-name
+25=my-extra-long-n
+26=hostname.com
+27=http://proxy.intranet.com:443
+
+/* indicators */
+01=1
+02=0
+03=1
+04=1
+05=1
+06=0
+07=1
+08=0
+09=1
+
+
+/*
+ * Expression inserts escaped for use in double quoted attributes.
+ */
+/* variables */
+01=vboxuser
+02=changeme
+03=changeme
+04=VBox &amp; VBox;
+05=911
+06=/bin/post-install-command arg1 arg2 --amp=&amp; --lt=&lt; --gt=&gt; --dq-word=&quot;word&quot; --sq-word='word'
+07=/aux/install/dir
+08=42
+09=x86
+10=x86
+11=i386
+12=i486
+13=i686
+14=3.4.2
+15=3
+16=cet
+17=cet
+18=cet
+19=dk_DK
+20=dk-DK
+21=dk
+22=DK
+23=my-extra-long-name.hostname.com
+24=my-extra-long-name
+25=my-extra-long-n
+26=hostname.com
+27=http://proxy.intranet.com:443
+
+/* indicators */
+01=1
+02=0
+03=1
+04=1
+05=1
+06=0
+07=1
+08=0
+09=1
+
+
+/*
+ * Some typical expression conditions.
+ */
+01: GUEST_OS_VERSION >= 1.2.3
+02: GUEST_OS_VERSION <= 3.4.2
diff --git a/src/VBox/Main/testcase/tstUnattendedScript-1.template b/src/VBox/Main/testcase/tstUnattendedScript-1.template
new file mode 100644
index 00000000..07762c7d
--- /dev/null
+++ b/src/VBox/Main/testcase/tstUnattendedScript-1.template
@@ -0,0 +1,384 @@
+/*
+ * Regular conditions.
+ */
+01=@@VBOX_COND_IS_INSTALLING_ADDITIONS@@true@@VBOX_COND_ELSE@@ false @@VBOX_COND_END@@
+02=@@VBOX_COND_IS_USER_LOGIN_ADMINISTRATOR@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+03=@@VBOX_COND_IS_INSTALLING_TEST_EXEC_SERVICE@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+04=@@VBOX_COND_HAS_POST_INSTALL_COMMAND@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+05=@@VBOX_COND_HAS_PRODUCT_KEY@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+06=@@VBOX_COND_IS_MINIMAL_INSTALLATION@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+07=@@VBOX_COND_IS_FIRMWARE_UEFI@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+08=@@VBOX_COND_IS_RTC_USING_UTC@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+09=@@VBOX_COND_HAS_PROXY@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+
+/*
+ * Expression conditions.
+ */
+01=@@VBOX_COND[${IS_INSTALLING_ADDITIONS}]@@true@@VBOX_COND_ELSE@@ false @@VBOX_COND_END@@
+02=@@VBOX_COND[${IS_USER_LOGIN_ADMINISTRATOR}]@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+03=@@VBOX_COND[${IS_INSTALLING_TEST_EXEC_SERVICE}]@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+04=@@VBOX_COND[${HAS_POST_INSTALL_COMMAND}]@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+05=@@VBOX_COND[${HAS_PRODUCT_KEY}]@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+06=@@VBOX_COND[${IS_MINIMAL_INSTALLATION}]@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+07=@@VBOX_COND[${IS_FIRMWARE_UEFI}]@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+08=@@VBOX_COND[${IS_RTC_USING_UTC}]@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+09=@@VBOX_COND[${HAS_PROXY}]@@true@@VBOX_COND_ELSE@@false@@VBOX_COND_END@@
+
+
+/*
+ * Regular inserts.
+ */
+/* variables */
+01=@@VBOX_INSERT_USER_LOGIN@@
+02=@@VBOX_INSERT_USER_PASSWORD@@
+03=@@VBOX_INSERT_ROOT_PASSWORD@@
+04=@@VBOX_INSERT_USER_FULL_NAME@@
+05=@@VBOX_INSERT_PRODUCT_KEY@@
+06=@@VBOX_INSERT_POST_INSTALL_COMMAND@@
+07=@@VBOX_INSERT_AUXILIARY_INSTALL_DIR@@
+08=@@VBOX_INSERT_IMAGE_INDEX@@
+09=@@VBOX_INSERT_OS_ARCH@@
+10=@@VBOX_INSERT_OS_ARCH2@@
+11=@@VBOX_INSERT_OS_ARCH3@@
+12=@@VBOX_INSERT_OS_ARCH4@@
+13=@@VBOX_INSERT_OS_ARCH6@@
+14=@@VBOX_INSERT_GUEST_OS_VERSION@@
+15=@@VBOX_INSERT_GUEST_OS_MAJOR_VERSION@@
+16=@@VBOX_INSERT_TIME_ZONE_UX@@
+17=@@VBOX_INSERT_TIME_ZONE_WIN_NAME@@
+18=@@VBOX_INSERT_TIME_ZONE_WIN_INDEX@@
+19=@@VBOX_INSERT_LOCALE@@
+20=@@VBOX_INSERT_DASH_LOCALE@@
+21=@@VBOX_INSERT_LANGUAGE@@
+22=@@VBOX_INSERT_COUNTRY@@
+23=@@VBOX_INSERT_HOSTNAME_FQDN@@
+24=@@VBOX_INSERT_HOSTNAME_WITHOUT_DOMAIN@@
+25=@@VBOX_INSERT_HOSTNAME_WITHOUT_DOMAIN_MAX_15@@
+26=@@VBOX_INSERT_HOSTNAME_DOMAIN@@
+27=@@VBOX_INSERT_PROXY@@
+
+/* indicators */
+01=@@VBOX_INSERT_IS_INSTALLING_ADDITIONS@@
+02=@@VBOX_INSERT_IS_USER_LOGIN_ADMINISTRATOR@@
+03=@@VBOX_INSERT_IS_INSTALLING_TEST_EXEC_SERVICE@@
+04=@@VBOX_INSERT_HAS_POST_INSTALL_COMMAND@@
+05=@@VBOX_INSERT_HAS_PRODUCT_KEY@@
+06=@@VBOX_INSERT_IS_MINIMAL_INSTALLATION@@
+07=@@VBOX_INSERT_IS_FIRMWARE_UEFI@@
+08=@@VBOX_INSERT_IS_RTC_USING_UTC@@
+09=@@VBOX_INSERT_HAS_PROXY@@
+
+
+/*
+ * Expression inserts
+ */
+/* variables */
+01=@@VBOX_INSERT[${USER_LOGIN}]@@
+02=@@VBOX_INSERT[${USER_PASSWORD}]@@
+03=@@VBOX_INSERT[${ROOT_PASSWORD}]@@
+04=@@VBOX_INSERT[${USER_FULL_NAME}]@@
+05=@@VBOX_INSERT[${PRODUCT_KEY}]@@
+06=@@VBOX_INSERT[${POST_INSTALL_COMMAND}]@@
+07=@@VBOX_INSERT[${AUXILIARY_INSTALL_DIR}]@@
+08=@@VBOX_INSERT[${IMAGE_INDEX}]@@
+09=@@VBOX_INSERT[${OS_ARCH}]@@
+10=@@VBOX_INSERT[${OS_ARCH2}]@@
+11=@@VBOX_INSERT[${OS_ARCH3}]@@
+12=@@VBOX_INSERT[${OS_ARCH4}]@@
+13=@@VBOX_INSERT[${OS_ARCH6}]@@
+14=@@VBOX_INSERT[${GUEST_OS_VERSION}]@@
+15=@@VBOX_INSERT[${GUEST_OS_MAJOR_VERSION}]@@
+16=@@VBOX_INSERT[${TIME_ZONE_UX}]@@
+17=@@VBOX_INSERT[${TIME_ZONE_WIN_NAME}]@@
+18=@@VBOX_INSERT[${TIME_ZONE_WIN_INDEX}]@@
+19=@@VBOX_INSERT[${LOCALE}]@@
+20=@@VBOX_INSERT[${DASH_LOCALE}]@@
+21=@@VBOX_INSERT[${LANGUAGE}]@@
+22=@@VBOX_INSERT[${COUNTRY}]@@
+23=@@VBOX_INSERT[${HOSTNAME_FQDN}]@@
+24=@@VBOX_INSERT[${HOSTNAME_WITHOUT_DOMAIN}]@@
+25=@@VBOX_INSERT[${HOSTNAME_WITHOUT_DOMAIN_MAX_15}]@@
+26=@@VBOX_INSERT[${HOSTNAME_DOMAIN}]@@
+27=@@VBOX_INSERT[${PROXY}]@@
+
+/* indicators */
+01=@@VBOX_INSERT[${IS_INSTALLING_ADDITIONS}]@@
+02=@@VBOX_INSERT[${IS_USER_LOGIN_ADMINISTRATOR}]@@
+03=@@VBOX_INSERT[${IS_INSTALLING_TEST_EXEC_SERVICE}]@@
+04=@@VBOX_INSERT[${HAS_POST_INSTALL_COMMAND}]@@
+05=@@VBOX_INSERT[${HAS_PRODUCT_KEY}]@@
+06=@@VBOX_INSERT[${IS_MINIMAL_INSTALLATION}]@@
+07=@@VBOX_INSERT[${IS_FIRMWARE_UEFI}]@@
+08=@@VBOX_INSERT[${IS_RTC_USING_UTC}]@@
+09=@@VBOX_INSERT[${HAS_PROXY}]@@
+
+
+/*
+ * Regular inserts with shell quoting.
+ */
+/* variables */
+01=@@VBOX_INSERT_USER_LOGIN_SH@@
+02=@@VBOX_INSERT_USER_PASSWORD_SH@@
+03=@@VBOX_INSERT_ROOT_PASSWORD_SH@@
+04=@@VBOX_INSERT_USER_FULL_NAME_SH@@
+05=@@VBOX_INSERT_PRODUCT_KEY_SH@@
+06=@@VBOX_INSERT_POST_INSTALL_COMMAND_SH@@
+07=@@VBOX_INSERT_AUXILIARY_INSTALL_DIR_SH@@
+08=@@VBOX_INSERT_IMAGE_INDEX_SH@@
+09=@@VBOX_INSERT_OS_ARCH_SH@@
+10=@@VBOX_INSERT_OS_ARCH2_SH@@
+11=@@VBOX_INSERT_OS_ARCH3_SH@@
+12=@@VBOX_INSERT_OS_ARCH4_SH@@
+13=@@VBOX_INSERT_OS_ARCH6_SH@@
+14=@@VBOX_INSERT_GUEST_OS_VERSION_SH@@
+15=@@VBOX_INSERT_GUEST_OS_MAJOR_VERSION_SH@@
+16=@@VBOX_INSERT_TIME_ZONE_UX_SH@@
+17=@@VBOX_INSERT_TIME_ZONE_WIN_NAME_SH@@
+18=@@VBOX_INSERT_TIME_ZONE_WIN_INDEX_SH@@
+19=@@VBOX_INSERT_LOCALE_SH@@
+20=@@VBOX_INSERT_DASH_LOCALE_SH@@
+21=@@VBOX_INSERT_LANGUAGE_SH@@
+22=@@VBOX_INSERT_COUNTRY_SH@@
+23=@@VBOX_INSERT_HOSTNAME_FQDN_SH@@
+24=@@VBOX_INSERT_HOSTNAME_WITHOUT_DOMAIN_SH@@
+25=@@VBOX_INSERT_HOSTNAME_WITHOUT_DOMAIN_MAX_15_SH@@
+26=@@VBOX_INSERT_HOSTNAME_DOMAIN_SH@@
+27=@@VBOX_INSERT_PROXY_SH@@
+
+/* indicators */
+01=@@VBOX_INSERT_IS_INSTALLING_ADDITIONS_SH@@
+02=@@VBOX_INSERT_IS_USER_LOGIN_ADMINISTRATOR_SH@@
+03=@@VBOX_INSERT_IS_INSTALLING_TEST_EXEC_SERVICE_SH@@
+04=@@VBOX_INSERT_HAS_POST_INSTALL_COMMAND_SH@@
+05=@@VBOX_INSERT_HAS_PRODUCT_KEY_SH@@
+06=@@VBOX_INSERT_IS_MINIMAL_INSTALLATION_SH@@
+07=@@VBOX_INSERT_IS_FIRMWARE_UEFI_SH@@
+08=@@VBOX_INSERT_IS_RTC_USING_UTC_SH@@
+09=@@VBOX_INSERT_HAS_PROXY_SH@@
+
+
+/*
+ * Expression inserts with shell quoting.
+ */
+/* variables */
+01=@@VBOX_INSERT[${USER_LOGIN}]SH@@
+02=@@VBOX_INSERT[${USER_PASSWORD}]SH@@
+03=@@VBOX_INSERT[${ROOT_PASSWORD}]SH@@
+04=@@VBOX_INSERT[${USER_FULL_NAME}]SH@@
+05=@@VBOX_INSERT[${PRODUCT_KEY}]SH@@
+06=@@VBOX_INSERT[${POST_INSTALL_COMMAND}]SH@@
+07=@@VBOX_INSERT[${AUXILIARY_INSTALL_DIR}]SH@@
+08=@@VBOX_INSERT[${IMAGE_INDEX}]SH@@
+09=@@VBOX_INSERT[${OS_ARCH}]SH@@
+10=@@VBOX_INSERT[${OS_ARCH2}]SH@@
+11=@@VBOX_INSERT[${OS_ARCH3}]SH@@
+12=@@VBOX_INSERT[${OS_ARCH4}]SH@@
+13=@@VBOX_INSERT[${OS_ARCH6}]SH@@
+14=@@VBOX_INSERT[${GUEST_OS_VERSION}]SH@@
+15=@@VBOX_INSERT[${GUEST_OS_MAJOR_VERSION}]SH@@
+16=@@VBOX_INSERT[${TIME_ZONE_UX}]SH@@
+17=@@VBOX_INSERT[${TIME_ZONE_WIN_NAME}]SH@@
+18=@@VBOX_INSERT[${TIME_ZONE_WIN_INDEX}]SH@@
+19=@@VBOX_INSERT[${LOCALE}]SH@@
+20=@@VBOX_INSERT[${DASH_LOCALE}]SH@@
+21=@@VBOX_INSERT[${LANGUAGE}]SH@@
+22=@@VBOX_INSERT[${COUNTRY}]SH@@
+23=@@VBOX_INSERT[${HOSTNAME_FQDN}]SH@@
+24=@@VBOX_INSERT[${HOSTNAME_WITHOUT_DOMAIN}]SH@@
+25=@@VBOX_INSERT[${HOSTNAME_WITHOUT_DOMAIN_MAX_15}]SH@@
+26=@@VBOX_INSERT[${HOSTNAME_DOMAIN}]SH@@
+27=@@VBOX_INSERT[${PROXY}]SH@@
+
+/* indicators */
+01=@@VBOX_INSERT[${IS_INSTALLING_ADDITIONS}]SH@@
+02=@@VBOX_INSERT[${IS_USER_LOGIN_ADMINISTRATOR}]SH@@
+03=@@VBOX_INSERT[${IS_INSTALLING_TEST_EXEC_SERVICE}]SH@@
+04=@@VBOX_INSERT[${HAS_POST_INSTALL_COMMAND}]SH@@
+05=@@VBOX_INSERT[${HAS_PRODUCT_KEY}]SH@@
+06=@@VBOX_INSERT[${IS_MINIMAL_INSTALLATION}]SH@@
+07=@@VBOX_INSERT[${IS_FIRMWARE_UEFI}]SH@@
+08=@@VBOX_INSERT[${IS_RTC_USING_UTC}]SH@@
+09=@@VBOX_INSERT[${HAS_PROXY}]SH@@
+
+
+/*
+ * Regular inserts escaped for use in XML element.
+ */
+/* variables */
+01=@@VBOX_INSERT_USER_LOGIN_ELEMENT@@
+02=@@VBOX_INSERT_USER_PASSWORD_ELEMENT@@
+03=@@VBOX_INSERT_ROOT_PASSWORD_ELEMENT@@
+04=@@VBOX_INSERT_USER_FULL_NAME_ELEMENT@@
+05=@@VBOX_INSERT_PRODUCT_KEY_ELEMENT@@
+06=@@VBOX_INSERT_POST_INSTALL_COMMAND_ELEMENT@@
+07=@@VBOX_INSERT_AUXILIARY_INSTALL_DIR_ELEMENT@@
+08=@@VBOX_INSERT_IMAGE_INDEX_ELEMENT@@
+09=@@VBOX_INSERT_OS_ARCH_ELEMENT@@
+10=@@VBOX_INSERT_OS_ARCH2_ELEMENT@@
+11=@@VBOX_INSERT_OS_ARCH3_ELEMENT@@
+12=@@VBOX_INSERT_OS_ARCH4_ELEMENT@@
+13=@@VBOX_INSERT_OS_ARCH6_ELEMENT@@
+14=@@VBOX_INSERT_GUEST_OS_VERSION_ELEMENT@@
+15=@@VBOX_INSERT_GUEST_OS_MAJOR_VERSION_ELEMENT@@
+16=@@VBOX_INSERT_TIME_ZONE_UX_ELEMENT@@
+17=@@VBOX_INSERT_TIME_ZONE_WIN_NAME_ELEMENT@@
+18=@@VBOX_INSERT_TIME_ZONE_WIN_INDEX_ELEMENT@@
+19=@@VBOX_INSERT_LOCALE_ELEMENT@@
+20=@@VBOX_INSERT_DASH_LOCALE_ELEMENT@@
+21=@@VBOX_INSERT_LANGUAGE_ELEMENT@@
+22=@@VBOX_INSERT_COUNTRY_ELEMENT@@
+23=@@VBOX_INSERT_HOSTNAME_FQDN_ELEMENT@@
+24=@@VBOX_INSERT_HOSTNAME_WITHOUT_DOMAIN_ELEMENT@@
+25=@@VBOX_INSERT_HOSTNAME_WITHOUT_DOMAIN_MAX_15_ELEMENT@@
+26=@@VBOX_INSERT_HOSTNAME_DOMAIN_ELEMENT@@
+27=@@VBOX_INSERT_PROXY_ELEMENT@@
+
+/* indicators */
+01=@@VBOX_INSERT_IS_INSTALLING_ADDITIONS_ELEMENT@@
+02=@@VBOX_INSERT_IS_USER_LOGIN_ADMINISTRATOR_ELEMENT@@
+03=@@VBOX_INSERT_IS_INSTALLING_TEST_EXEC_SERVICE_ELEMENT@@
+04=@@VBOX_INSERT_HAS_POST_INSTALL_COMMAND_ELEMENT@@
+05=@@VBOX_INSERT_HAS_PRODUCT_KEY_ELEMENT@@
+06=@@VBOX_INSERT_IS_MINIMAL_INSTALLATION_ELEMENT@@
+07=@@VBOX_INSERT_IS_FIRMWARE_UEFI_ELEMENT@@
+08=@@VBOX_INSERT_IS_RTC_USING_UTC_ELEMENT@@
+09=@@VBOX_INSERT_HAS_PROXY_ELEMENT@@
+
+
+/*
+ * Expression inserts escaped for use in XML element.
+ */
+/* variables */
+01=@@VBOX_INSERT[${USER_LOGIN}]ELEMENT@@
+02=@@VBOX_INSERT[${USER_PASSWORD}]ELEMENT@@
+03=@@VBOX_INSERT[${ROOT_PASSWORD}]ELEMENT@@
+04=@@VBOX_INSERT[${USER_FULL_NAME}]ELEMENT@@
+05=@@VBOX_INSERT[${PRODUCT_KEY}]ELEMENT@@
+06=@@VBOX_INSERT[${POST_INSTALL_COMMAND}]ELEMENT@@
+07=@@VBOX_INSERT[${AUXILIARY_INSTALL_DIR}]ELEMENT@@
+08=@@VBOX_INSERT[${IMAGE_INDEX}]ELEMENT@@
+09=@@VBOX_INSERT[${OS_ARCH}]ELEMENT@@
+10=@@VBOX_INSERT[${OS_ARCH2}]ELEMENT@@
+11=@@VBOX_INSERT[${OS_ARCH3}]ELEMENT@@
+12=@@VBOX_INSERT[${OS_ARCH4}]ELEMENT@@
+13=@@VBOX_INSERT[${OS_ARCH6}]ELEMENT@@
+14=@@VBOX_INSERT[${GUEST_OS_VERSION}]ELEMENT@@
+15=@@VBOX_INSERT[${GUEST_OS_MAJOR_VERSION}]ELEMENT@@
+16=@@VBOX_INSERT[${TIME_ZONE_UX}]ELEMENT@@
+17=@@VBOX_INSERT[${TIME_ZONE_WIN_NAME}]ELEMENT@@
+18=@@VBOX_INSERT[${TIME_ZONE_WIN_INDEX}]ELEMENT@@
+19=@@VBOX_INSERT[${LOCALE}]ELEMENT@@
+20=@@VBOX_INSERT[${DASH_LOCALE}]ELEMENT@@
+21=@@VBOX_INSERT[${LANGUAGE}]ELEMENT@@
+22=@@VBOX_INSERT[${COUNTRY}]ELEMENT@@
+23=@@VBOX_INSERT[${HOSTNAME_FQDN}]ELEMENT@@
+24=@@VBOX_INSERT[${HOSTNAME_WITHOUT_DOMAIN}]ELEMENT@@
+25=@@VBOX_INSERT[${HOSTNAME_WITHOUT_DOMAIN_MAX_15}]ELEMENT@@
+26=@@VBOX_INSERT[${HOSTNAME_DOMAIN}]ELEMENT@@
+27=@@VBOX_INSERT[${PROXY}]ELEMENT@@
+
+/* indicators */
+01=@@VBOX_INSERT[${IS_INSTALLING_ADDITIONS}]ELEMENT@@
+02=@@VBOX_INSERT[${IS_USER_LOGIN_ADMINISTRATOR}]ELEMENT@@
+03=@@VBOX_INSERT[${IS_INSTALLING_TEST_EXEC_SERVICE}]ELEMENT@@
+04=@@VBOX_INSERT[${HAS_POST_INSTALL_COMMAND}]ELEMENT@@
+05=@@VBOX_INSERT[${HAS_PRODUCT_KEY}]ELEMENT@@
+06=@@VBOX_INSERT[${IS_MINIMAL_INSTALLATION}]ELEMENT@@
+07=@@VBOX_INSERT[${IS_FIRMWARE_UEFI}]ELEMENT@@
+08=@@VBOX_INSERT[${IS_RTC_USING_UTC}]ELEMENT@@
+09=@@VBOX_INSERT[${HAS_PROXY}]ELEMENT@@
+
+
+/*
+ * Regular inserts escaped for use in double quoted attributes.
+ */
+/* variables */
+01=@@VBOX_INSERT_USER_LOGIN_ATTRIB_DQ@@
+02=@@VBOX_INSERT_USER_PASSWORD_ATTRIB_DQ@@
+03=@@VBOX_INSERT_ROOT_PASSWORD_ATTRIB_DQ@@
+04=@@VBOX_INSERT_USER_FULL_NAME_ATTRIB_DQ@@
+05=@@VBOX_INSERT_PRODUCT_KEY_ATTRIB_DQ@@
+06=@@VBOX_INSERT_POST_INSTALL_COMMAND_ATTRIB_DQ@@
+07=@@VBOX_INSERT_AUXILIARY_INSTALL_DIR_ATTRIB_DQ@@
+08=@@VBOX_INSERT_IMAGE_INDEX_ATTRIB_DQ@@
+09=@@VBOX_INSERT_OS_ARCH_ATTRIB_DQ@@
+10=@@VBOX_INSERT_OS_ARCH2_ATTRIB_DQ@@
+11=@@VBOX_INSERT_OS_ARCH3_ATTRIB_DQ@@
+12=@@VBOX_INSERT_OS_ARCH4_ATTRIB_DQ@@
+13=@@VBOX_INSERT_OS_ARCH6_ATTRIB_DQ@@
+14=@@VBOX_INSERT_GUEST_OS_VERSION_ATTRIB_DQ@@
+15=@@VBOX_INSERT_GUEST_OS_MAJOR_VERSION_ATTRIB_DQ@@
+16=@@VBOX_INSERT_TIME_ZONE_UX_ATTRIB_DQ@@
+17=@@VBOX_INSERT_TIME_ZONE_WIN_NAME_ATTRIB_DQ@@
+18=@@VBOX_INSERT_TIME_ZONE_WIN_INDEX_ATTRIB_DQ@@
+19=@@VBOX_INSERT_LOCALE_ATTRIB_DQ@@
+20=@@VBOX_INSERT_DASH_LOCALE_ATTRIB_DQ@@
+21=@@VBOX_INSERT_LANGUAGE_ATTRIB_DQ@@
+22=@@VBOX_INSERT_COUNTRY_ATTRIB_DQ@@
+23=@@VBOX_INSERT_HOSTNAME_FQDN_ATTRIB_DQ@@
+24=@@VBOX_INSERT_HOSTNAME_WITHOUT_DOMAIN_ATTRIB_DQ@@
+25=@@VBOX_INSERT_HOSTNAME_WITHOUT_DOMAIN_MAX_15_ATTRIB_DQ@@
+26=@@VBOX_INSERT_HOSTNAME_DOMAIN_ATTRIB_DQ@@
+27=@@VBOX_INSERT_PROXY_ATTRIB_DQ@@
+
+/* indicators */
+01=@@VBOX_INSERT_IS_INSTALLING_ADDITIONS_ATTRIB_DQ@@
+02=@@VBOX_INSERT_IS_USER_LOGIN_ADMINISTRATOR_ATTRIB_DQ@@
+03=@@VBOX_INSERT_IS_INSTALLING_TEST_EXEC_SERVICE_ATTRIB_DQ@@
+04=@@VBOX_INSERT_HAS_POST_INSTALL_COMMAND_ATTRIB_DQ@@
+05=@@VBOX_INSERT_HAS_PRODUCT_KEY_ATTRIB_DQ@@
+06=@@VBOX_INSERT_IS_MINIMAL_INSTALLATION_ATTRIB_DQ@@
+07=@@VBOX_INSERT_IS_FIRMWARE_UEFI_ATTRIB_DQ@@
+08=@@VBOX_INSERT_IS_RTC_USING_UTC_ATTRIB_DQ@@
+09=@@VBOX_INSERT_HAS_PROXY_ATTRIB_DQ@@
+
+
+/*
+ * Expression inserts escaped for use in double quoted attributes.
+ */
+/* variables */
+01=@@VBOX_INSERT[${USER_LOGIN}]ATTRIB_DQ@@
+02=@@VBOX_INSERT[${USER_PASSWORD}]ATTRIB_DQ@@
+03=@@VBOX_INSERT[${ROOT_PASSWORD}]ATTRIB_DQ@@
+04=@@VBOX_INSERT[${USER_FULL_NAME}]ATTRIB_DQ@@
+05=@@VBOX_INSERT[${PRODUCT_KEY}]ATTRIB_DQ@@
+06=@@VBOX_INSERT[${POST_INSTALL_COMMAND}]ATTRIB_DQ@@
+07=@@VBOX_INSERT[${AUXILIARY_INSTALL_DIR}]ATTRIB_DQ@@
+08=@@VBOX_INSERT[${IMAGE_INDEX}]ATTRIB_DQ@@
+09=@@VBOX_INSERT[${OS_ARCH}]ATTRIB_DQ@@
+10=@@VBOX_INSERT[${OS_ARCH2}]ATTRIB_DQ@@
+11=@@VBOX_INSERT[${OS_ARCH3}]ATTRIB_DQ@@
+12=@@VBOX_INSERT[${OS_ARCH4}]ATTRIB_DQ@@
+13=@@VBOX_INSERT[${OS_ARCH6}]ATTRIB_DQ@@
+14=@@VBOX_INSERT[${GUEST_OS_VERSION}]ATTRIB_DQ@@
+15=@@VBOX_INSERT[${GUEST_OS_MAJOR_VERSION}]ATTRIB_DQ@@
+16=@@VBOX_INSERT[${TIME_ZONE_UX}]ATTRIB_DQ@@
+17=@@VBOX_INSERT[${TIME_ZONE_WIN_NAME}]ATTRIB_DQ@@
+18=@@VBOX_INSERT[${TIME_ZONE_WIN_INDEX}]ATTRIB_DQ@@
+19=@@VBOX_INSERT[${LOCALE}]ATTRIB_DQ@@
+20=@@VBOX_INSERT[${DASH_LOCALE}]ATTRIB_DQ@@
+21=@@VBOX_INSERT[${LANGUAGE}]ATTRIB_DQ@@
+22=@@VBOX_INSERT[${COUNTRY}]ATTRIB_DQ@@
+23=@@VBOX_INSERT[${HOSTNAME_FQDN}]ATTRIB_DQ@@
+24=@@VBOX_INSERT[${HOSTNAME_WITHOUT_DOMAIN}]ATTRIB_DQ@@
+25=@@VBOX_INSERT[${HOSTNAME_WITHOUT_DOMAIN_MAX_15}]ATTRIB_DQ@@
+26=@@VBOX_INSERT[${HOSTNAME_DOMAIN}]ATTRIB_DQ@@
+27=@@VBOX_INSERT[${PROXY}]ATTRIB_DQ@@
+
+/* indicators */
+01=@@VBOX_INSERT[${IS_INSTALLING_ADDITIONS}]ATTRIB_DQ@@
+02=@@VBOX_INSERT[${IS_USER_LOGIN_ADMINISTRATOR}]ATTRIB_DQ@@
+03=@@VBOX_INSERT[${IS_INSTALLING_TEST_EXEC_SERVICE}]ATTRIB_DQ@@
+04=@@VBOX_INSERT[${HAS_POST_INSTALL_COMMAND}]ATTRIB_DQ@@
+05=@@VBOX_INSERT[${HAS_PRODUCT_KEY}]ATTRIB_DQ@@
+06=@@VBOX_INSERT[${IS_MINIMAL_INSTALLATION}]ATTRIB_DQ@@
+07=@@VBOX_INSERT[${IS_FIRMWARE_UEFI}]ATTRIB_DQ@@
+08=@@VBOX_INSERT[${IS_RTC_USING_UTC}]ATTRIB_DQ@@
+09=@@VBOX_INSERT[${HAS_PROXY}]ATTRIB_DQ@@
+
+
+/*
+ * Some typical expression conditions.
+ */
+01: @@VBOX_COND[${GUEST_OS_VERSION} vge 1.2.3]@@ GUEST_OS_VERSION >= 1.2.3@@VBOX_COND_ELSE@@ failed@@VBOX_COND_END@@
+02: @@VBOX_COND[${GUEST_OS_VERSION} vle 3.4.2]@@ GUEST_OS_VERSION <= 3.4.2@@VBOX_COND_ELSE@@ failed@@VBOX_COND_END@@
diff --git a/src/VBox/Main/testcase/tstUnattendedScript.cpp b/src/VBox/Main/testcase/tstUnattendedScript.cpp
new file mode 100644
index 00000000..a4de3ca5
--- /dev/null
+++ b/src/VBox/Main/testcase/tstUnattendedScript.cpp
@@ -0,0 +1,731 @@
+/* $Id: tstUnattendedScript.cpp $ */
+/** @file
+ * tstUnattendedScript - testcases for UnattendedScript.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "UnattendedScript.h"
+
+#include <VBox/com/VirtualBox.h>
+#include <VBox/com/errorprint.h>
+
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/test.h>
+#include <iprt/stream.h>
+
+#include "VirtualBoxBase.h"
+#include "UnattendedImpl.h"
+#include "UnattendedScript.h"
+#include "VirtualBoxImpl.h"
+#include "MachineImpl.h"
+
+using namespace std;
+
+
+/*********************************************************************************************************************************
+* Unattended Stub Implementation *
+*********************************************************************************************************************************/
+Unattended::Unattended()
+ : mhThreadReconfigureVM(NIL_RTNATIVETHREAD), mfRtcUseUtc(false), mfGuestOs64Bit(false)
+ , mpInstaller(NULL), mpTimeZoneInfo(NULL), mfIsDefaultAuxiliaryBasePath(true), mfDoneDetectIsoOS(false)
+{
+ mStrUser = "vboxuser";
+ mStrPassword = "changeme";
+ mStrFullUserName = "VBox & VBox;";
+ mStrProductKey = "911";
+ mStrIsoPath = "/iso/path/file.iso";
+ mStrAdditionsIsoPath = "/iso/path/addition.iso";
+ mfInstallGuestAdditions = true;
+ mfInstallTestExecService = true;
+ mStrValidationKitIsoPath = "/iso/path/valkit.iso";
+ mStrTimeZone = "cet";
+ mpTimeZoneInfo = NULL;
+ mStrLocale = "dk_DK";
+ mStrLanguage = "dk";
+ mStrCountry = "DK";
+ //mPackageSelectionAdjustments = "minimal";
+ mStrHostname = "my-extra-long-name.hostname.com";
+ mStrAuxiliaryBasePath = "/aux/path/pfx-";
+ mfIsDefaultAuxiliaryBasePath = false;
+ midxImage = 42;
+ mStrScriptTemplatePath = "/path/to/script-template.file";
+ mStrPostInstallScriptTemplatePath = "/path/to/post-install-template.file";
+ mStrPostInstallCommand = "/bin/post-install-command arg1 arg2 --amp=& --lt=< --gt=> --dq-word=\"word\" --sq-word='word'";
+ mStrExtraInstallKernelParameters = "extra=kernel parameters quiet amp=& lt=< gt=>";
+ mStrProxy = "http://proxy.intranet.com:443";
+
+ mfDoneDetectIsoOS = true;
+ mStrDetectedOSTypeId = "MyOSTypeId";
+ mStrDetectedOSVersion = "3.4.2";
+ mStrDetectedOSFlavor = "server";
+ //mDetectedOSLanguages = "en_UK"
+ mStrDetectedOSHints = "nudge nudge wink wink";
+}
+
+Unattended::~Unattended()
+{
+}
+
+HRESULT Unattended::FinalConstruct()
+{
+ return BaseFinalConstruct();
+}
+
+void Unattended::FinalRelease()
+{
+ uninit();
+ BaseFinalRelease();
+}
+
+void Unattended::uninit()
+{
+}
+
+HRESULT Unattended::initUnattended(VirtualBox *aParent)
+{
+ unconst(mParent) = aParent;
+ return S_OK;
+}
+
+HRESULT Unattended::detectIsoOS()
+{
+ return E_NOTIMPL;
+}
+
+
+HRESULT Unattended::prepare()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::constructMedia()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::reconfigureVM()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::done()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getIsoPath(com::Utf8Str &isoPath)
+{
+ RT_NOREF(isoPath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setIsoPath(const com::Utf8Str &isoPath)
+{
+ RT_NOREF(isoPath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getUser(com::Utf8Str &user)
+{
+ RT_NOREF(user);
+ return E_NOTIMPL;
+}
+
+
+HRESULT Unattended::setUser(const com::Utf8Str &user)
+{
+ RT_NOREF(user);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getPassword(com::Utf8Str &password)
+{
+ RT_NOREF(password);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setPassword(const com::Utf8Str &password)
+{
+ RT_NOREF(password);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getFullUserName(com::Utf8Str &fullUserName)
+{
+ RT_NOREF(fullUserName);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setFullUserName(const com::Utf8Str &fullUserName)
+{
+ RT_NOREF(fullUserName);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getProductKey(com::Utf8Str &productKey)
+{
+ RT_NOREF(productKey);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setProductKey(const com::Utf8Str &productKey)
+{
+ RT_NOREF(productKey);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getAdditionsIsoPath(com::Utf8Str &additionsIsoPath)
+{
+ RT_NOREF(additionsIsoPath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setAdditionsIsoPath(const com::Utf8Str &additionsIsoPath)
+{
+ RT_NOREF(additionsIsoPath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getInstallGuestAdditions(BOOL *installGuestAdditions)
+{
+ RT_NOREF(installGuestAdditions);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setInstallGuestAdditions(BOOL installGuestAdditions)
+{
+ RT_NOREF(installGuestAdditions);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getValidationKitIsoPath(com::Utf8Str &aValidationKitIsoPath)
+{
+ RT_NOREF(aValidationKitIsoPath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setValidationKitIsoPath(const com::Utf8Str &aValidationKitIsoPath)
+{
+ RT_NOREF(aValidationKitIsoPath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getInstallTestExecService(BOOL *aInstallTestExecService)
+{
+ RT_NOREF(aInstallTestExecService);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setInstallTestExecService(BOOL aInstallTestExecService)
+{
+ RT_NOREF(aInstallTestExecService);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getTimeZone(com::Utf8Str &aTimeZone)
+{
+ RT_NOREF(aTimeZone);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setTimeZone(const com::Utf8Str &aTimezone)
+{
+ RT_NOREF(aTimezone);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getLocale(com::Utf8Str &aLocale)
+{
+ RT_NOREF(aLocale);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setLocale(const com::Utf8Str &aLocale)
+{
+ RT_NOREF(aLocale);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getLanguage(com::Utf8Str &aLanguage)
+{
+ RT_NOREF(aLanguage);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setLanguage(const com::Utf8Str &aLanguage)
+{
+ RT_NOREF(aLanguage);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getCountry(com::Utf8Str &aCountry)
+{
+ RT_NOREF(aCountry);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setCountry(const com::Utf8Str &aCountry)
+{
+ RT_NOREF(aCountry);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getProxy(com::Utf8Str &aProxy)
+{
+ RT_NOREF(aProxy);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setProxy(const com::Utf8Str &aProxy)
+{
+ RT_NOREF(aProxy);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getPackageSelectionAdjustments(com::Utf8Str &aPackageSelectionAdjustments)
+{
+ RT_NOREF(aPackageSelectionAdjustments);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setPackageSelectionAdjustments(const com::Utf8Str &aPackageSelectionAdjustments)
+{
+ RT_NOREF(aPackageSelectionAdjustments);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getHostname(com::Utf8Str &aHostname)
+{
+ RT_NOREF(aHostname);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setHostname(const com::Utf8Str &aHostname)
+{
+ RT_NOREF(aHostname);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getAuxiliaryBasePath(com::Utf8Str &aAuxiliaryBasePath)
+{
+ RT_NOREF(aAuxiliaryBasePath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setAuxiliaryBasePath(const com::Utf8Str &aAuxiliaryBasePath)
+{
+ RT_NOREF(aAuxiliaryBasePath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getImageIndex(ULONG *index)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *index = midxImage;
+ return S_OK;
+}
+
+HRESULT Unattended::setImageIndex(ULONG index)
+{
+ RT_NOREF(index);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getMachine(ComPtr<IMachine> &aMachine)
+{
+ RT_NOREF(aMachine);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setMachine(const ComPtr<IMachine> &aMachine)
+{
+ RT_NOREF(aMachine);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getScriptTemplatePath(com::Utf8Str &aScriptTemplatePath)
+{
+ RT_NOREF(aScriptTemplatePath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setScriptTemplatePath(const com::Utf8Str &aScriptTemplatePath)
+{
+ RT_NOREF(aScriptTemplatePath);
+ return E_NOTIMPL;
+
+}
+
+HRESULT Unattended::getPostInstallScriptTemplatePath(com::Utf8Str &aPostInstallScriptTemplatePath)
+{
+ RT_NOREF(aPostInstallScriptTemplatePath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setPostInstallScriptTemplatePath(const com::Utf8Str &aPostInstallScriptTemplatePath)
+{
+ RT_NOREF(aPostInstallScriptTemplatePath);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getPostInstallCommand(com::Utf8Str &aPostInstallCommand)
+{
+ RT_NOREF(aPostInstallCommand);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setPostInstallCommand(const com::Utf8Str &aPostInstallCommand)
+{
+ RT_NOREF(aPostInstallCommand);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getExtraInstallKernelParameters(com::Utf8Str &aExtraInstallKernelParameters)
+{
+ RT_NOREF(aExtraInstallKernelParameters);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setExtraInstallKernelParameters(const com::Utf8Str &aExtraInstallKernelParameters)
+{
+ RT_NOREF(aExtraInstallKernelParameters);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getDetectedOSTypeId(com::Utf8Str &aDetectedOSTypeId)
+{
+ RT_NOREF(aDetectedOSTypeId);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getDetectedOSVersion(com::Utf8Str &aDetectedOSVersion)
+{
+ RT_NOREF(aDetectedOSVersion);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getDetectedOSFlavor(com::Utf8Str &aDetectedOSFlavor)
+{
+ RT_NOREF(aDetectedOSFlavor);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getDetectedOSLanguages(com::Utf8Str &aDetectedOSLanguages)
+{
+ RT_NOREF(aDetectedOSLanguages);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getDetectedOSHints(com::Utf8Str &aDetectedOSHints)
+{
+ RT_NOREF(aDetectedOSHints);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getDetectedImageNames(std::vector<com::Utf8Str> &aDetectedImageNames)
+{
+ RT_NOREF(aDetectedImageNames);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getDetectedImageIndices(std::vector<ULONG> &aDetectedImageIndices)
+{
+ RT_NOREF(aDetectedImageIndices);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getIsUnattendedInstallSupported(BOOL *aIsUnattendedInstallSupported)
+{
+ RT_NOREF(aIsUnattendedInstallSupported);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::getAvoidUpdatesOverNetwork(BOOL *aAvoidUpdatesOverNetwork)
+{
+ RT_NOREF(aAvoidUpdatesOverNetwork);
+ return E_NOTIMPL;
+}
+
+HRESULT Unattended::setAvoidUpdatesOverNetwork(BOOL aAvoidUpdatesOverNetwork)
+{
+ RT_NOREF(aAvoidUpdatesOverNetwork);
+ return E_NOTIMPL;
+}
+
+
+/*
+ * Getters that the installer and script classes can use.
+ */
+Utf8Str const &Unattended::i_getIsoPath() const
+{
+ return mStrIsoPath;
+}
+
+Utf8Str const &Unattended::i_getUser() const
+{
+ return mStrUser;
+}
+
+Utf8Str const &Unattended::i_getPassword() const
+{
+ return mStrPassword;
+}
+
+Utf8Str const &Unattended::i_getFullUserName() const
+{
+ return mStrFullUserName.isNotEmpty() ? mStrFullUserName : mStrUser;
+}
+
+Utf8Str const &Unattended::i_getProductKey() const
+{
+ return mStrProductKey;
+}
+
+Utf8Str const &Unattended::i_getProxy() const
+{
+ return mStrProxy;
+}
+
+Utf8Str const &Unattended::i_getAdditionsIsoPath() const
+{
+ return mStrAdditionsIsoPath;
+}
+
+bool Unattended::i_getInstallGuestAdditions() const
+{
+ return mfInstallGuestAdditions;
+}
+
+Utf8Str const &Unattended::i_getValidationKitIsoPath() const
+{
+ return mStrValidationKitIsoPath;
+}
+
+bool Unattended::i_getInstallTestExecService() const
+{
+ return mfInstallTestExecService;
+}
+
+Utf8Str const &Unattended::i_getTimeZone() const
+{
+ return mStrTimeZone;
+}
+
+PCRTTIMEZONEINFO Unattended::i_getTimeZoneInfo() const
+{
+ return mpTimeZoneInfo;
+}
+
+Utf8Str const &Unattended::i_getLocale() const
+{
+ return mStrLocale;
+}
+
+Utf8Str const &Unattended::i_getLanguage() const
+{
+ return mStrLanguage;
+}
+
+Utf8Str const &Unattended::i_getCountry() const
+{
+ return mStrCountry;
+}
+
+bool Unattended::i_isMinimalInstallation() const
+{
+ size_t i = mPackageSelectionAdjustments.size();
+ while (i-- > 0)
+ if (mPackageSelectionAdjustments[i].equals("minimal"))
+ return true;
+ return false;
+}
+
+Utf8Str const &Unattended::i_getHostname() const
+{
+ return mStrHostname;
+}
+
+Utf8Str const &Unattended::i_getAuxiliaryBasePath() const
+{
+ return mStrAuxiliaryBasePath;
+}
+
+ULONG Unattended::i_getImageIndex() const
+{
+ return midxImage;
+}
+
+Utf8Str const &Unattended::i_getScriptTemplatePath() const
+{
+ return mStrScriptTemplatePath;
+}
+
+Utf8Str const &Unattended::i_getPostInstallScriptTemplatePath() const
+{
+ return mStrPostInstallScriptTemplatePath;
+}
+
+Utf8Str const &Unattended::i_getPostInstallCommand() const
+{
+ return mStrPostInstallCommand;
+}
+
+Utf8Str const &Unattended::i_getAuxiliaryInstallDir() const
+{
+ static Utf8Str s_strAuxInstallDir("/aux/install/dir");
+ return s_strAuxInstallDir;
+}
+
+Utf8Str const &Unattended::i_getExtraInstallKernelParameters() const
+{
+ return mStrExtraInstallKernelParameters;
+}
+
+bool Unattended::i_isRtcUsingUtc() const
+{
+ return mfRtcUseUtc;
+}
+
+bool Unattended::i_isGuestOs64Bit() const
+{
+ return mfGuestOs64Bit;
+}
+
+bool Unattended::i_isFirmwareEFI() const
+{
+ return menmFirmwareType != FirmwareType_BIOS;
+}
+
+Utf8Str const &Unattended::i_getDetectedOSVersion()
+{
+ return mStrDetectedOSVersion;
+}
+
+bool Unattended::i_getAvoidUpdatesOverNetwork() const
+{
+ return mfAvoidUpdatesOverNetwork;
+}
+
+
+/*********************************************************************************************************************************
+* The Testcase *
+*********************************************************************************************************************************/
+
+static bool loadFileAsString(const char *pszFilename, Utf8Str &rstrContent)
+{
+ rstrContent.setNull();
+
+ char szPath[RTPATH_MAX];
+ RTTESTI_CHECK_RC_RET(RTPathExecDir(szPath, sizeof(szPath)), VINF_SUCCESS, false);
+ RTTESTI_CHECK_RC_RET(RTPathAppend(szPath, sizeof(szPath), pszFilename), VINF_SUCCESS, false);
+
+ RTFILE hFile;
+ RTTESTI_CHECK_RC_RET(RTFileOpen(&hFile, szPath, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE), VINF_SUCCESS, false);
+
+ uint64_t cbFile = 0;
+ RTTESTI_CHECK_RC_RET(RTFileQuerySize(hFile, &cbFile), VINF_SUCCESS, false);
+
+ rstrContent.reserve((size_t)cbFile + 1);
+ RTTESTI_CHECK_RC_RET(RTFileRead(hFile, rstrContent.mutableRaw(), (size_t)cbFile, NULL), VINF_SUCCESS, false);
+ rstrContent.mutableRaw()[cbFile] = '\0';
+ rstrContent.jolt();
+
+ RTTESTI_CHECK_RC_RET(RTFileClose(hFile), VINF_SUCCESS, false);
+
+ return true;
+}
+
+static void doTest1()
+{
+ RTTestISub("tstUnattendedScript-1.template");
+
+ /* Create the parent class instance: */
+ ComObjPtr<Unattended> ptrParent;
+ HRESULT hrc = ptrParent.createObject();
+ RTTESTI_CHECK_MSG_RETV(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc));
+
+ /* Instantiate the script editor. */
+ UnattendedScriptTemplate Tmpl(ptrParent, "template.ext", "file.ext");
+#define CHECK_HRESULT(a_Expr) do { \
+ HRESULT hrcThis = a_Expr; \
+ if (SUCCEEDED(hrcThis)) break; \
+ RTTestIFailed("line %d: %s -> %Rhrc", __LINE__, #a_Expr, hrcThis); \
+ GlueHandleComError(ptrParent, NULL, hrcThis, NULL, __LINE__); \
+ } while (0)
+
+ /* Load the exercise script. */
+ char szPath[RTPATH_MAX];
+ RTTESTI_CHECK_RC_RETV(RTPathExecDir(szPath, sizeof(szPath)), VINF_SUCCESS);
+ RTTESTI_CHECK_RC_RETV(RTPathAppend(szPath, sizeof(szPath), "tstUnattendedScript-1.template"), VINF_SUCCESS);
+ CHECK_HRESULT(Tmpl.read(szPath));
+
+ /* Save the template to string. */
+ Utf8Str strActual;
+ CHECK_HRESULT(Tmpl.saveToString(strActual));
+
+ /* Load the expected result. */
+ Utf8Str strExpected;
+ RTTESTI_CHECK_RETV(loadFileAsString("tstUnattendedScript-1.expected", strExpected));
+
+ /* Compare the two. */
+ if (strExpected != strActual)
+ {
+ RTTestIFailed("Output does not match tstUnattendedScript-1.expect!");
+ RTTestIFailureDetails("------ BEGIN OUTPUT ------\n");
+ RTStrmWrite(g_pStdErr, strActual.c_str(), strActual.length());
+ RTTestIFailureDetails("------- END OUTPUT -------\n");
+
+ RTCList<RTCString, RTCString *> const lstActual = strActual.split("\n");
+ RTCList<RTCString, RTCString *> const lstExpected = strExpected.split("\n");
+ size_t const cLines = RT_MIN(lstActual.size(), lstExpected.size());
+ for (size_t i = 0; i < cLines; i++)
+ if (lstActual[i] != lstExpected[i])
+ {
+ RTTestIFailureDetails("First difference on line %u:\n%s\nexpected:\n%s\n",
+ i + 1, lstActual[i].c_str(), lstExpected[i].c_str());
+ break;
+ }
+ }
+}
+
+int main()
+{
+ RTTEST hTest;
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstUnattendedScript", &hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+#ifdef RT_OS_WINDOWS
+ /*ATL::CComModule *g_pAtlComModule = */ new(ATL::CComModule);
+#endif
+
+ doTest1();
+
+ return RTTestSummaryAndDestroy(hTest);
+}
diff --git a/src/VBox/Main/testcase/tstVBoxAPI.cpp b/src/VBox/Main/testcase/tstVBoxAPI.cpp
new file mode 100644
index 00000000..32c58cd5
--- /dev/null
+++ b/src/VBox/Main/testcase/tstVBoxAPI.cpp
@@ -0,0 +1,417 @@
+/* $Id: tstVBoxAPI.cpp $ */
+/** @file
+ * tstVBoxAPI - Checks VirtualBox API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/array.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/com/VirtualBox.h>
+#include <VBox/sup.h>
+
+#include <iprt/test.h>
+#include <iprt/time.h>
+
+using namespace com;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest;
+static Bstr tstMachineName = "tstVBoxAPI test VM";
+
+
+/** Worker for TST_COM_EXPR(). */
+static HRESULT tstComExpr(HRESULT hrc, const char *pszOperation, int iLine)
+{
+ if (FAILED(hrc))
+ RTTestFailed(g_hTest, "%s failed on line %u with hrc=%Rhrc", pszOperation, iLine, hrc);
+ return hrc;
+}
+
+/** Macro that executes the given expression and report any failure.
+ * The expression must return a HRESULT. */
+#define TST_COM_EXPR(expr) tstComExpr(expr, #expr, __LINE__)
+
+
+static BOOL tstApiIVirtualBox(IVirtualBox *pVBox)
+{
+ HRESULT hrc;
+ Bstr bstrTmp;
+ ULONG ulTmp;
+
+ RTTestSub(g_hTest, "IVirtualBox::version");
+ CHECK_ERROR(pVBox, COMGETTER(Version)(bstrTmp.asOutParam()));
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::version");
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::version failed", __LINE__);
+
+ RTTestSub(g_hTest, "IVirtualBox::versionNormalized");
+ CHECK_ERROR(pVBox, COMGETTER(VersionNormalized)(bstrTmp.asOutParam()));
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::versionNormalized");
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::versionNormalized failed", __LINE__);
+
+ RTTestSub(g_hTest, "IVirtualBox::revision");
+ CHECK_ERROR(pVBox, COMGETTER(Revision)(&ulTmp));
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::revision");
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::revision failed", __LINE__);
+
+ RTTestSub(g_hTest, "IVirtualBox::packageType");
+ CHECK_ERROR(pVBox, COMGETTER(PackageType)(bstrTmp.asOutParam()));
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::packageType");
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::packageType failed", __LINE__);
+
+ RTTestSub(g_hTest, "IVirtualBox::APIVersion");
+ CHECK_ERROR(pVBox, COMGETTER(APIVersion)(bstrTmp.asOutParam()));
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::APIVersion");
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::APIVersion failed", __LINE__);
+
+ RTTestSub(g_hTest, "IVirtualBox::homeFolder");
+ CHECK_ERROR(pVBox, COMGETTER(HomeFolder)(bstrTmp.asOutParam()));
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::homeFolder");
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::homeFolder failed", __LINE__);
+
+ RTTestSub(g_hTest, "IVirtualBox::settingsFilePath");
+ CHECK_ERROR(pVBox, COMGETTER(SettingsFilePath)(bstrTmp.asOutParam()));
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::settingsFilePath");
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::settingsFilePath failed", __LINE__);
+
+ com::SafeIfaceArray<IGuestOSType> guestOSTypes;
+ RTTestSub(g_hTest, "IVirtualBox::guestOSTypes");
+ CHECK_ERROR(pVBox, COMGETTER(GuestOSTypes)(ComSafeArrayAsOutParam(guestOSTypes)));
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::guestOSTypes");
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::guestOSTypes failed", __LINE__);
+
+ /** Create VM */
+ RTTestSub(g_hTest, "IVirtualBox::CreateMachine");
+ ComPtr<IMachine> ptrMachine;
+ com::SafeArray<BSTR> groups;
+ /** Default VM settings */
+ CHECK_ERROR(pVBox, CreateMachine(NULL, /** Settings */
+ tstMachineName.raw(), /** Name */
+ ComSafeArrayAsInParam(groups), /** Groups */
+ NULL, /** OS Type */
+ NULL, /** Create flags */
+ NULL, /** Cipher */
+ NULL, /** Password id */
+ NULL, /** Password */
+ ptrMachine.asOutParam())); /** Machine */
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::CreateMachine");
+ else
+ {
+ RTTestFailed(g_hTest, "%d: IVirtualBox::CreateMachine failed", __LINE__);
+ return FALSE;
+ }
+
+ RTTestSub(g_hTest, "IVirtualBox::RegisterMachine");
+ CHECK_ERROR(pVBox, RegisterMachine(ptrMachine));
+ if (SUCCEEDED(hrc))
+ RTTestPassed(g_hTest, "IVirtualBox::RegisterMachine");
+ else
+ {
+ RTTestFailed(g_hTest, "%d: IVirtualBox::RegisterMachine failed", __LINE__);
+ return FALSE;
+ }
+
+ ComPtr<IHost> host;
+ RTTestSub(g_hTest, "IVirtualBox::host");
+ CHECK_ERROR(pVBox, COMGETTER(Host)(host.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add IHost testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::host");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::host failed", __LINE__);
+
+ ComPtr<ISystemProperties> sysprop;
+ RTTestSub(g_hTest, "IVirtualBox::systemProperties");
+ CHECK_ERROR(pVBox, COMGETTER(SystemProperties)(sysprop.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add ISystemProperties testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::systemProperties");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::systemProperties failed", __LINE__);
+
+ com::SafeIfaceArray<IMachine> machines;
+ RTTestSub(g_hTest, "IVirtualBox::machines");
+ CHECK_ERROR(pVBox, COMGETTER(Machines)(ComSafeArrayAsOutParam(machines)));
+ if (SUCCEEDED(hrc))
+ {
+ bool bFound = FALSE;
+ for (size_t i = 0; i < machines.size(); ++i)
+ {
+ if (machines[i])
+ {
+ Bstr tmpName;
+ hrc = machines[i]->COMGETTER(Name)(tmpName.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ if (tmpName == tstMachineName)
+ {
+ bFound = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (bFound)
+ RTTestPassed(g_hTest, "IVirtualBox::machines");
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::machines failed. No created machine found", __LINE__);
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::machines failed", __LINE__);
+
+#if 0 /** Not yet implemented */
+ com::SafeIfaceArray<ISharedFolder> sharedFolders;
+ RTTestSub(g_hTest, "IVirtualBox::sharedFolders");
+ CHECK_ERROR(pVBox, COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(sharedFolders)));
+ if (SUCCEEDED(rc))
+ {
+ /** @todo Add ISharedFolders testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::sharedFolders");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::sharedFolders failed", __LINE__);
+#endif
+
+ com::SafeIfaceArray<IMedium> hardDisks;
+ RTTestSub(g_hTest, "IVirtualBox::hardDisks");
+ CHECK_ERROR(pVBox, COMGETTER(HardDisks)(ComSafeArrayAsOutParam(hardDisks)));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add hardDisks testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::hardDisks");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::hardDisks failed", __LINE__);
+
+ com::SafeIfaceArray<IMedium> DVDImages;
+ RTTestSub(g_hTest, "IVirtualBox::DVDImages");
+ CHECK_ERROR(pVBox, COMGETTER(DVDImages)(ComSafeArrayAsOutParam(DVDImages)));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add DVDImages testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::DVDImages");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::DVDImages failed", __LINE__);
+
+ com::SafeIfaceArray<IMedium> floppyImages;
+ RTTestSub(g_hTest, "IVirtualBox::floppyImages");
+ CHECK_ERROR(pVBox, COMGETTER(FloppyImages)(ComSafeArrayAsOutParam(floppyImages)));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add floppyImages testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::floppyImages");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::floppyImages failed", __LINE__);
+
+ com::SafeIfaceArray<IProgress> progressOperations;
+ RTTestSub(g_hTest, "IVirtualBox::progressOperations");
+ CHECK_ERROR(pVBox, COMGETTER(ProgressOperations)(ComSafeArrayAsOutParam(progressOperations)));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add IProgress testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::progressOperations");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::progressOperations failed", __LINE__);
+
+ ComPtr<IPerformanceCollector> performanceCollector;
+ RTTestSub(g_hTest, "IVirtualBox::performanceCollector");
+ CHECK_ERROR(pVBox, COMGETTER(PerformanceCollector)(performanceCollector.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add IPerformanceCollector testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::performanceCollector");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::performanceCollector failed", __LINE__);
+
+ com::SafeIfaceArray<IDHCPServer> DHCPServers;
+ RTTestSub(g_hTest, "IVirtualBox::DHCPServers");
+ CHECK_ERROR(pVBox, COMGETTER(DHCPServers)(ComSafeArrayAsOutParam(DHCPServers)));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add IDHCPServers testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::DHCPServers");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::DHCPServers failed", __LINE__);
+
+ com::SafeIfaceArray<INATNetwork> NATNetworks;
+ RTTestSub(g_hTest, "IVirtualBox::NATNetworks");
+ CHECK_ERROR(pVBox, COMGETTER(NATNetworks)(ComSafeArrayAsOutParam(NATNetworks)));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add INATNetworks testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::NATNetworks");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::NATNetworks failed", __LINE__);
+
+ ComPtr<IEventSource> eventSource;
+ RTTestSub(g_hTest, "IVirtualBox::eventSource");
+ CHECK_ERROR(pVBox, COMGETTER(EventSource)(eventSource.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add IEventSource testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::eventSource");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::eventSource failed", __LINE__);
+
+ ComPtr<IExtPackManager> extensionPackManager;
+ RTTestSub(g_hTest, "IVirtualBox::extensionPackManager");
+ CHECK_ERROR(pVBox, COMGETTER(ExtensionPackManager)(extensionPackManager.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ /** @todo Add IExtPackManager testing here. */
+ RTTestPassed(g_hTest, "IVirtualBox::extensionPackManager");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::extensionPackManager failed", __LINE__);
+
+ com::SafeArray<BSTR> internalNetworks;
+ RTTestSub(g_hTest, "IVirtualBox::internalNetworks");
+ CHECK_ERROR(pVBox, COMGETTER(InternalNetworks)(ComSafeArrayAsOutParam(internalNetworks)));
+ if (SUCCEEDED(hrc))
+ {
+ RTTestPassed(g_hTest, "IVirtualBox::internalNetworks");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::internalNetworks failed", __LINE__);
+
+ com::SafeArray<BSTR> genericNetworkDrivers;
+ RTTestSub(g_hTest, "IVirtualBox::genericNetworkDrivers");
+ CHECK_ERROR(pVBox, COMGETTER(GenericNetworkDrivers)(ComSafeArrayAsOutParam(genericNetworkDrivers)));
+ if (SUCCEEDED(hrc))
+ {
+ RTTestPassed(g_hTest, "IVirtualBox::genericNetworkDrivers");
+ }
+ else
+ RTTestFailed(g_hTest, "%d: IVirtualBox::genericNetworkDrivers failed", __LINE__);
+
+ return TRUE;
+}
+
+
+static BOOL tstApiClean(IVirtualBox *pVBox)
+{
+ HRESULT hrc;
+
+ /** Delete created VM and its files */
+ ComPtr<IMachine> machine;
+ CHECK_ERROR_RET(pVBox, FindMachine(Bstr(tstMachineName).raw(), machine.asOutParam()), FALSE);
+ SafeIfaceArray<IMedium> media;
+ CHECK_ERROR_RET(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly,
+ ComSafeArrayAsOutParam(media)), FALSE);
+ ComPtr<IProgress> progress;
+ CHECK_ERROR_RET(machine, DeleteConfig(ComSafeArrayAsInParam(media), progress.asOutParam()), FALSE);
+ CHECK_ERROR_RET(progress, WaitForCompletion(-1), FALSE);
+
+ return TRUE;
+}
+
+
+int main()
+{
+ /*
+ * Initialization.
+ */
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstVBoxAPI", &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ SUPR3Init(NULL); /* Better time support. */
+ RTTestBanner(g_hTest);
+
+ RTTestSub(g_hTest, "Initializing COM and singletons");
+ HRESULT hrc = com::Initialize();
+ if (SUCCEEDED(hrc))
+ {
+ ComPtr<IVirtualBoxClient> ptrVBoxClient;
+ ComPtr<IVirtualBox> ptrVBox;
+ hrc = TST_COM_EXPR(ptrVBoxClient.createInprocObject(CLSID_VirtualBoxClient));
+ if (SUCCEEDED(hrc))
+ hrc = TST_COM_EXPR(ptrVBoxClient->COMGETTER(VirtualBox)(ptrVBox.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ ComPtr<ISession> ptrSession;
+ hrc = TST_COM_EXPR(ptrSession.createInprocObject(CLSID_Session));
+ if (SUCCEEDED(hrc))
+ {
+ RTTestSubDone(g_hTest);
+
+ /*
+ * Call test functions.
+ */
+
+ /** Test IVirtualBox interface */
+ tstApiIVirtualBox(ptrVBox);
+
+
+ /** Clean files/configs */
+ tstApiClean(ptrVBox);
+ }
+ }
+
+ ptrVBox.setNull();
+ ptrVBoxClient.setNull();
+ com::Shutdown();
+ }
+ else
+ RTTestIFailed("com::Initialize failed with hrc=%Rhrc", hrc);
+ return RTTestSummaryAndDestroy(g_hTest);
+}
diff --git a/src/VBox/Main/testcase/tstVBoxAPIPerf.cpp b/src/VBox/Main/testcase/tstVBoxAPIPerf.cpp
new file mode 100644
index 00000000..0553c7ee
--- /dev/null
+++ b/src/VBox/Main/testcase/tstVBoxAPIPerf.cpp
@@ -0,0 +1,257 @@
+/* $Id: tstVBoxAPIPerf.cpp $ */
+/** @file
+ * tstVBoxAPIPerf - Checks the performance of the COM / XPOM API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/array.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/VirtualBox.h>
+#include <VBox/sup.h>
+
+#include <iprt/test.h>
+#include <iprt/time.h>
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest;
+
+
+/** Worker fro TST_COM_EXPR(). */
+static HRESULT tstComExpr(HRESULT hrc, const char *pszOperation, int iLine)
+{
+ if (FAILED(hrc))
+ RTTestFailed(g_hTest, "%s failed on line %u with hrc=%Rhrc", pszOperation, iLine, hrc);
+ return hrc;
+}
+
+/** Macro that executes the given expression and report any failure.
+ * The expression must return a HRESULT. */
+#define TST_COM_EXPR(expr) tstComExpr(expr, #expr, __LINE__)
+
+
+
+static void tstApiPrf1(IVirtualBox *pVBox)
+{
+ RTTestSub(g_hTest, "IVirtualBox::Revision performance");
+
+ uint32_t const cCalls = 65536;
+ uint32_t cLeft = cCalls;
+ uint64_t uStartTS = RTTimeNanoTS();
+ while (cLeft-- > 0)
+ {
+ ULONG uRev;
+ HRESULT hrc = pVBox->COMGETTER(Revision)(&uRev);
+ if (FAILED(hrc))
+ {
+ tstComExpr(hrc, "IVirtualBox::Revision", __LINE__);
+ return;
+ }
+ }
+ uint64_t uElapsed = RTTimeNanoTS() - uStartTS;
+ RTTestValue(g_hTest, "IVirtualBox::Revision average", uElapsed / cCalls, RTTESTUNIT_NS_PER_CALL);
+ RTTestSubDone(g_hTest);
+}
+
+
+static void tstApiPrf2(IVirtualBox *pVBox)
+{
+ RTTestSub(g_hTest, "IVirtualBox::Version performance");
+
+ uint32_t const cCalls = 65536;
+ uint32_t cLeft = cCalls;
+ uint64_t uStartTS = RTTimeNanoTS();
+ while (cLeft-- > 0)
+ {
+ com::Bstr bstrVersion;
+ HRESULT hrc = pVBox->COMGETTER(Version)(bstrVersion.asOutParam());
+ if (FAILED(hrc))
+ {
+ tstComExpr(hrc, "IVirtualBox::Version", __LINE__);
+ return;
+ }
+ }
+ uint64_t uElapsed = RTTimeNanoTS() - uStartTS;
+ RTTestValue(g_hTest, "IVirtualBox::Version average", uElapsed / cCalls, RTTESTUNIT_NS_PER_CALL);
+ RTTestSubDone(g_hTest);
+}
+
+
+static void tstApiPrf3(IVirtualBox *pVBox)
+{
+ RTTestSub(g_hTest, "IVirtualBox::Host performance");
+
+ /* The first call. */
+ uint64_t uStartTS = RTTimeNanoTS();
+ IHost *pHost = NULL;
+ HRESULT hrc = pVBox->COMGETTER(Host)(&pHost);
+ if (FAILED(hrc))
+ {
+ tstComExpr(hrc, "IVirtualBox::Host", __LINE__);
+ return;
+ }
+ pHost->Release();
+ uint64_t uElapsed = RTTimeNanoTS() - uStartTS;
+ RTTestValue(g_hTest, "IVirtualBox::Host first", uElapsed, RTTESTUNIT_NS);
+
+ /* Subsequent calls. */
+ uint32_t const cCalls1 = 4096;
+ uint32_t cLeft = cCalls1;
+ uStartTS = RTTimeNanoTS();
+ while (cLeft-- > 0)
+ {
+ IHost *pHost2 = NULL;
+ hrc = pVBox->COMGETTER(Host)(&pHost2);
+ if (FAILED(hrc))
+ {
+ tstComExpr(hrc, "IVirtualBox::Host", __LINE__);
+ return;
+ }
+ pHost2->Release();
+ }
+ uElapsed = RTTimeNanoTS() - uStartTS;
+ RTTestValue(g_hTest, "IVirtualBox::Host average", uElapsed / cCalls1, RTTESTUNIT_NS_PER_CALL);
+
+ /* Keep a reference around and see how that changes things.
+ Note! VBoxSVC is not creating and destroying Host(). */
+ pHost = NULL;
+ hrc = pVBox->COMGETTER(Host)(&pHost);
+
+ uint32_t const cCalls2 = 16384;
+ cLeft = cCalls2;
+ uStartTS = RTTimeNanoTS();
+ while (cLeft-- > 0)
+ {
+ IHost *pHost2 = NULL;
+ hrc = pVBox->COMGETTER(Host)(&pHost2);
+ if (FAILED(hrc))
+ {
+ tstComExpr(hrc, "IVirtualBox::Host", __LINE__);
+ pHost->Release();
+ return;
+ }
+ pHost2->Release();
+ }
+ uElapsed = RTTimeNanoTS() - uStartTS;
+ RTTestValue(g_hTest, "IVirtualBox::Host 2nd ref", uElapsed / cCalls2, RTTESTUNIT_NS_PER_CALL);
+ pHost->Release();
+
+ RTTestSubDone(g_hTest);
+}
+
+
+static void tstApiPrf4(IVirtualBox *pVBox)
+{
+ RTTestSub(g_hTest, "IHost::GetProcessorFeature performance");
+
+ IHost *pHost = NULL;
+ HRESULT hrc = pVBox->COMGETTER(Host)(&pHost);
+ if (FAILED(hrc))
+ {
+ tstComExpr(hrc, "IVirtualBox::Host", __LINE__);
+ return;
+ }
+
+ uint32_t const cCalls = 65536;
+ uint32_t cLeft = cCalls;
+ uint64_t uStartTS = RTTimeNanoTS();
+ while (cLeft-- > 0)
+ {
+ BOOL fSupported;
+ hrc = pHost->GetProcessorFeature(ProcessorFeature_PAE, &fSupported);
+ if (FAILED(hrc))
+ {
+ tstComExpr(hrc, "IHost::GetProcessorFeature", __LINE__);
+ pHost->Release();
+ return;
+ }
+ }
+ uint64_t uElapsed = RTTimeNanoTS() - uStartTS;
+ RTTestValue(g_hTest, "IHost::GetProcessorFeature average", uElapsed / cCalls, RTTESTUNIT_NS_PER_CALL);
+ pHost->Release();
+ RTTestSubDone(g_hTest);
+}
+
+
+
+int main()
+{
+ /*
+ * Initialization.
+ */
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstVBoxAPIPerf", &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ SUPR3Init(NULL); /* Better time support. */
+ RTTestBanner(g_hTest);
+
+ RTTestSub(g_hTest, "Initializing COM and singletons");
+ HRESULT hrc = com::Initialize();
+ if (SUCCEEDED(hrc))
+ {
+ ComPtr<IVirtualBoxClient> ptrVBoxClient;
+ ComPtr<IVirtualBox> ptrVBox;
+ hrc = TST_COM_EXPR(ptrVBoxClient.createInprocObject(CLSID_VirtualBoxClient));
+ if (SUCCEEDED(hrc))
+ hrc = TST_COM_EXPR(ptrVBoxClient->COMGETTER(VirtualBox)(ptrVBox.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ ComPtr<ISession> ptrSession;
+ hrc = TST_COM_EXPR(ptrSession.createInprocObject(CLSID_Session));
+ if (SUCCEEDED(hrc))
+ {
+ RTTestSubDone(g_hTest);
+
+ /*
+ * Call test functions.
+ */
+ tstApiPrf1(ptrVBox);
+ tstApiPrf2(ptrVBox);
+ tstApiPrf3(ptrVBox);
+
+ /** @todo Find something that returns a 2nd instance of an interface and see
+ * how if wrapper stuff is reused in any way. */
+ tstApiPrf4(ptrVBox);
+ }
+ }
+
+ ptrVBox.setNull();
+ ptrVBoxClient.setNull();
+ com::Shutdown();
+ }
+ else
+ RTTestIFailed("com::Initialize failed with hrc=%Rhrc", hrc);
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+
diff --git a/src/VBox/Main/testcase/tstVBoxAPIWin.cpp b/src/VBox/Main/testcase/tstVBoxAPIWin.cpp
new file mode 100644
index 00000000..2c9b47b8
--- /dev/null
+++ b/src/VBox/Main/testcase/tstVBoxAPIWin.cpp
@@ -0,0 +1,305 @@
+/* $Id: tstVBoxAPIWin.cpp $ */
+/** @file
+ *
+ * tstVBoxAPIWin - sample program to illustrate the VirtualBox
+ * COM API for machine management on Windows.
+ It only uses standard C/C++ and COM semantics,
+ * no additional VBox classes/macros/helpers. To
+ * make things even easier to follow, only the
+ * standard Win32 API has been used. Typically,
+ * C++ developers would make use of Microsoft's
+ * ATL to ease development.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * PURPOSE OF THIS SAMPLE PROGRAM
+ * ------------------------------
+ *
+ * This sample program is intended to demonstrate the minimal code necessary
+ * to use VirtualBox COM API for learning puroses only. The program uses pure
+ * Win32 API and doesn't have any extra dependencies to let you better
+ * understand what is going on when a client talks to the VirtualBox core
+ * using the COM framework.
+ *
+ * However, if you want to write a real application, it is highly recommended
+ * to use our MS COM XPCOM Glue library and helper C++ classes. This way, you
+ * will get at least the following benefits:
+ *
+ * a) better portability: both the MS COM (used on Windows) and XPCOM (used
+ * everywhere else) VirtualBox client application from the same source code
+ * (including common smart C++ templates for automatic interface pointer
+ * reference counter and string data management);
+ * b) simpler XPCOM initialization and shutdown (only a single method call
+ * that does everything right).
+ *
+ * Currently, there is no separate sample program that uses the VirtualBox MS
+ * COM XPCOM Glue library. Please refer to the sources of stock VirtualBox
+ * applications such as the VirtualBox GUI frontend or the VBoxManage command
+ * line frontend.
+ */
+
+
+#include <stdio.h>
+#include <iprt/win/windows.h> /* Avoid -Wall warnings. */
+#include "VirtualBox.h"
+
+#define SAFE_RELEASE(x) \
+ if (x) { \
+ x->Release(); \
+ x = NULL; \
+ }
+
+int listVMs(IVirtualBox *virtualBox)
+{
+ HRESULT rc;
+
+ /*
+ * First we have to get a list of all registered VMs
+ */
+ SAFEARRAY *machinesArray = NULL;
+
+ rc = virtualBox->get_Machines(&machinesArray);
+ if (SUCCEEDED(rc))
+ {
+ IMachine **machines;
+ rc = SafeArrayAccessData(machinesArray, (void **) &machines);
+ if (SUCCEEDED(rc))
+ {
+ for (ULONG i = 0; i < machinesArray->rgsabound[0].cElements; ++i)
+ {
+ BSTR str;
+
+ rc = machines[i]->get_Name(&str);
+ if (SUCCEEDED(rc))
+ {
+ printf("Name: %S\n", str);
+ SysFreeString(str);
+ }
+ }
+
+ SafeArrayUnaccessData(machinesArray);
+ }
+
+ SafeArrayDestroy(machinesArray);
+ }
+
+ return 0;
+}
+
+
+int testErrorInfo(IVirtualBox *virtualBox)
+{
+ HRESULT rc;
+
+ /* Try to find a machine that doesn't exist */
+ IMachine *machine = NULL;
+ BSTR machineName = SysAllocString(L"Foobar");
+
+ rc = virtualBox->FindMachine(machineName, &machine);
+
+ if (FAILED(rc))
+ {
+ IErrorInfo *errorInfo;
+
+ rc = GetErrorInfo(0, &errorInfo);
+
+ if (FAILED(rc))
+ printf("Error getting error info! rc=%#lx\n", rc);
+ else
+ {
+ BSTR errorDescription = NULL;
+
+ rc = errorInfo->GetDescription(&errorDescription);
+
+ if (FAILED(rc) || !errorDescription)
+ printf("Error getting error description! rc=%#lx\n", rc);
+ else
+ {
+ printf("Successfully retrieved error description: %S\n", errorDescription);
+
+ SysFreeString(errorDescription);
+ }
+
+ errorInfo->Release();
+ }
+ }
+
+ SAFE_RELEASE(machine);
+ SysFreeString(machineName);
+
+ return 0;
+}
+
+
+int testStartVM(IVirtualBox *virtualBox)
+{
+ HRESULT rc;
+
+ /* Try to start a VM called "WinXP SP2". */
+ IMachine *machine = NULL;
+ BSTR machineName = SysAllocString(L"WinXP SP2");
+
+ rc = virtualBox->FindMachine(machineName, &machine);
+
+ if (FAILED(rc))
+ {
+ IErrorInfo *errorInfo;
+
+ rc = GetErrorInfo(0, &errorInfo);
+
+ if (FAILED(rc))
+ printf("Error getting error info! rc=%#lx\n", rc);
+ else
+ {
+ BSTR errorDescription = NULL;
+
+ rc = errorInfo->GetDescription(&errorDescription);
+
+ if (FAILED(rc) || !errorDescription)
+ printf("Error getting error description! rc=%#lx\n", rc);
+ else
+ {
+ printf("Successfully retrieved error description: %S\n", errorDescription);
+
+ SysFreeString(errorDescription);
+ }
+
+ SAFE_RELEASE(errorInfo);
+ }
+ }
+ else
+ {
+ ISession *session = NULL;
+ IConsole *console = NULL;
+ IProgress *progress = NULL;
+ BSTR sessiontype = SysAllocString(L"gui");
+ BSTR guid;
+
+ do
+ {
+ rc = machine->get_Id(&guid); /* Get the GUID of the machine. */
+ if (!SUCCEEDED(rc))
+ {
+ printf("Error retrieving machine ID! rc=%#lx\n", rc);
+ break;
+ }
+
+ /* Create the session object. */
+ rc = CoCreateInstance(CLSID_Session, /* the VirtualBox base object */
+ NULL, /* no aggregation */
+ CLSCTX_INPROC_SERVER, /* the object lives in the current process */
+ IID_ISession, /* IID of the interface */
+ (void**)&session);
+ if (!SUCCEEDED(rc))
+ {
+ printf("Error creating Session instance! rc=%#lx\n", rc);
+ break;
+ }
+
+ /* Start a VM session using the delivered VBox GUI. */
+ rc = machine->LaunchVMProcess(session, sessiontype,
+ NULL, &progress);
+ if (!SUCCEEDED(rc))
+ {
+ printf("Could not open remote session! rc=%#lx\n", rc);
+ break;
+ }
+
+ /* Wait until VM is running. */
+ printf("Starting VM, please wait ...\n");
+ rc = progress->WaitForCompletion(-1);
+
+ /* Get console object. */
+ session->get_Console(&console);
+
+ /* Bring console window to front. */
+ machine->ShowConsoleWindow(0);
+
+ printf("Press enter to power off VM and close the session...\n");
+ getchar();
+
+ /* Power down the machine. */
+ rc = console->PowerDown(&progress);
+
+ /* Wait until VM is powered down. */
+ printf("Powering off VM, please wait ...\n");
+ rc = progress->WaitForCompletion(-1);
+
+ /* Close the session. */
+ rc = session->UnlockMachine();
+
+ } while (0);
+
+ SAFE_RELEASE(console);
+ SAFE_RELEASE(progress);
+ SAFE_RELEASE(session);
+ SysFreeString(guid);
+ SysFreeString(sessiontype);
+ SAFE_RELEASE(machine);
+ }
+
+ SysFreeString(machineName);
+
+ return 0;
+}
+
+
+int main()
+{
+ /* Initialize the COM subsystem. */
+ CoInitialize(NULL);
+
+ /* Instantiate the VirtualBox root object. */
+ IVirtualBoxClient *virtualBoxClient;
+ HRESULT rc = CoCreateInstance(CLSID_VirtualBoxClient, /* the VirtualBoxClient object */
+ NULL, /* no aggregation */
+ CLSCTX_INPROC_SERVER, /* the object lives in the current process */
+ IID_IVirtualBoxClient, /* IID of the interface */
+ (void**)&virtualBoxClient);
+ if (SUCCEEDED(rc))
+ {
+ IVirtualBox *virtualBox;
+ rc = virtualBoxClient->get_VirtualBox(&virtualBox);
+ if (SUCCEEDED(rc))
+ {
+ listVMs(virtualBox);
+
+ testErrorInfo(virtualBox);
+
+ /* Enable the following line to get a VM started. */
+ //testStartVM(virtualBox);
+
+ /* Release the VirtualBox object. */
+ virtualBox->Release();
+ virtualBoxClient->Release();
+ }
+ else
+ printf("Error creating VirtualBox instance! rc=%#lx\n", rc);
+ }
+
+ CoUninitialize();
+ return 0;
+}
+
diff --git a/src/VBox/Main/testcase/tstVBoxAPIXPCOM.cpp b/src/VBox/Main/testcase/tstVBoxAPIXPCOM.cpp
new file mode 100644
index 00000000..aebb6407
--- /dev/null
+++ b/src/VBox/Main/testcase/tstVBoxAPIXPCOM.cpp
@@ -0,0 +1,684 @@
+/* $Id: tstVBoxAPIXPCOM.cpp $ */
+/** @file
+ *
+ * tstVBoxAPIXPCOM - sample program to illustrate the VirtualBox
+ * XPCOM API for machine management.
+ * It only uses standard C/C++ and XPCOM semantics,
+ * no additional VBox classes/macros/helpers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * PURPOSE OF THIS SAMPLE PROGRAM
+ * ------------------------------
+ *
+ * This sample program is intended to demonstrate the minimal code necessary
+ * to use VirtualBox XPCOM API for learning puroses only. The program uses
+ * pure XPCOM and doesn't have any extra dependencies to let you better
+ * understand what is going on when a client talks to the VirtualBox core
+ * using the XPCOM framework.
+ *
+ * However, if you want to write a real application, it is highly recommended
+ * to use our MS COM XPCOM Glue library and helper C++ classes. This way, you
+ * will get at least the following benefits:
+ *
+ * a) better portability: both the MS COM (used on Windows) and XPCOM (used
+ * everywhere else) VirtualBox client application from the same source code
+ * (including common smart C++ templates for automatic interface pointer
+ * reference counter and string data management);
+ * b) simpler XPCOM initialization and shutdown (only a single method call
+ * that does everything right).
+ *
+ * Currently, there is no separate sample program that uses the VirtualBox MS
+ * COM XPCOM Glue library. Please refer to the sources of stock VirtualBox
+ * applications such as the VirtualBox GUI frontend or the VBoxManage command
+ * line frontend.
+ *
+ *
+ * RUNNING THIS SAMPLE PROGRAM
+ * ---------------------------
+ *
+ * This sample program needs to know where the VirtualBox core files reside
+ * and where to search for VirtualBox shared libraries. Therefore, you need to
+ * use the following (or similar) command to execute it:
+ *
+ * $ env VBOX_XPCOM_HOME=../../.. LD_LIBRARY_PATH=../../.. ./tstVBoxAPIXPCOM
+ *
+ * The above command assumes that VBoxRT.so, VBoxXPCOM.so and others reside in
+ * the directory ../../..
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <iconv.h>
+
+/*
+ * Include the XPCOM headers
+ */
+#include <nsMemory.h>
+#include <nsString.h>
+#include <nsIServiceManager.h>
+#include <nsEventQueueUtils.h>
+
+#include <nsIExceptionService.h>
+
+/*
+ * VirtualBox XPCOM interface. This header is generated
+ * from IDL which in turn is generated from a custom XML format.
+ */
+#include "VirtualBox_XPCOM.h"
+
+/*
+ * Prototypes
+ */
+
+char *nsIDToString(nsID *guid);
+void printErrorInfo();
+
+
+/**
+ * Display all registered VMs on the screen with some information about each
+ *
+ * @param virtualBox VirtualBox instance object.
+ */
+void listVMs(IVirtualBox *virtualBox)
+{
+ nsresult rc;
+
+ printf("----------------------------------------------------\n");
+ printf("VM List:\n\n");
+
+ /*
+ * Get the list of all registered VMs
+ */
+ IMachine **machines = NULL;
+ PRUint32 cMachines = 0;
+
+ rc = virtualBox->GetMachines(&cMachines, &machines);
+ if (NS_SUCCEEDED(rc))
+ {
+ /*
+ * Iterate through the collection
+ */
+ for (PRUint32 i = 0; i < cMachines; ++ i)
+ {
+ IMachine *machine = machines[i];
+ if (machine)
+ {
+ PRBool isAccessible = PR_FALSE;
+ machine->GetAccessible(&isAccessible);
+
+ if (isAccessible)
+ {
+ nsXPIDLString machineName;
+ machine->GetName(getter_Copies(machineName));
+ char *machineNameAscii = ToNewCString(machineName);
+ printf("\tName: %s\n", machineNameAscii);
+ free(machineNameAscii);
+ }
+ else
+ {
+ printf("\tName: <inaccessible>\n");
+ }
+
+ nsXPIDLString iid;
+ machine->GetId(getter_Copies(iid));
+ const char *uuidString = ToNewCString(iid);
+ printf("\tUUID: %s\n", uuidString);
+ free((void*)uuidString);
+
+ if (isAccessible)
+ {
+ nsXPIDLString configFile;
+ machine->GetSettingsFilePath(getter_Copies(configFile));
+ char *configFileAscii = ToNewCString(configFile);
+ printf("\tConfig file: %s\n", configFileAscii);
+ free(configFileAscii);
+
+ PRUint32 memorySize;
+ machine->GetMemorySize(&memorySize);
+ printf("\tMemory size: %uMB\n", memorySize);
+
+ nsXPIDLString typeId;
+ machine->GetOSTypeId(getter_Copies(typeId));
+ IGuestOSType *osType = nsnull;
+ virtualBox->GetGuestOSType(typeId.get(), &osType);
+ nsXPIDLString osName;
+ osType->GetDescription(getter_Copies(osName));
+ char *osNameAscii = ToNewCString(osName);
+ printf("\tGuest OS: %s\n\n", osNameAscii);
+ free(osNameAscii);
+ osType->Release();
+ }
+
+ /* don't forget to release the objects in the array... */
+ machine->Release();
+ }
+ }
+ nsMemory::Free(machines);
+ }
+ printf("----------------------------------------------------\n\n");
+}
+
+/**
+ * Create a sample VM
+ *
+ * @param virtualBox VirtualBox instance object.
+ */
+void createVM(IVirtualBox *virtualBox)
+{
+ nsresult rc;
+ /*
+ * First create a unnamed new VM. It will be unconfigured and not be saved
+ * in the configuration until we explicitely choose to do so.
+ */
+ nsCOMPtr<IMachine> machine;
+ rc = virtualBox->CreateMachine(NULL, /* settings file */
+ NS_LITERAL_STRING("A brand new name").get(),
+ 0, nsnull, /* groups (safearray)*/
+ nsnull, /* ostype */
+ nsnull, /* create flags */
+ nsnull, /* cipher */
+ nsnull, /* password id */
+ nsnull, /* password */
+ getter_AddRefs(machine));
+ if (NS_FAILED(rc))
+ {
+ printf("Error: could not create machine! rc=%#x\n", rc);
+ return;
+ }
+
+ /*
+ * Set some properties
+ */
+ /* alternative to illustrate the use of string classes */
+ rc = machine->SetName(NS_ConvertUTF8toUTF16("A new name").get());
+ rc = machine->SetMemorySize(128);
+
+ /*
+ * Now a more advanced property -- the guest OS type. This is
+ * an object by itself which has to be found first. Note that we
+ * use the ID of the guest OS type here which is an internal
+ * representation (you can find that by configuring the OS type of
+ * a machine in the GUI and then looking at the <Guest ostype=""/>
+ * setting in the XML file. It is also possible to get the OS type from
+ * its description (win2k would be "Windows 2000") by getting the
+ * guest OS type collection and enumerating it.
+ */
+ nsCOMPtr<IGuestOSType> osType;
+ rc = virtualBox->GetGuestOSType(NS_LITERAL_STRING("Windows2000").get(),
+ getter_AddRefs(osType));
+ if (NS_FAILED(rc))
+ {
+ printf("Error: could not find guest OS type! rc=%#x\n", rc);
+ }
+ else
+ {
+ machine->SetOSTypeId(NS_LITERAL_STRING("Windows2000").get());
+ }
+
+ /*
+ * Register the VM. Note that this call also saves the VM config
+ * to disk. It is also possible to save the VM settings but not
+ * register the VM.
+ *
+ * Also note that due to current VirtualBox limitations, the machine
+ * must be registered *before* we can attach hard disks to it.
+ */
+ rc = virtualBox->RegisterMachine(machine);
+ if (NS_FAILED(rc))
+ {
+ printf("Error: could not register machine! rc=%#x\n", rc);
+ printErrorInfo();
+ return;
+ }
+
+ nsCOMPtr<IMachine> origMachine = machine;
+
+ /*
+ * In order to manipulate the registered machine, we must open a session
+ * for that machine. Do it now.
+ */
+ nsCOMPtr<ISession> session;
+ nsCOMPtr<IMachine> sessionMachine;
+ {
+ nsCOMPtr<nsIComponentManager> manager;
+ rc = NS_GetComponentManager(getter_AddRefs(manager));
+ if (NS_FAILED(rc))
+ {
+ printf("Error: could not get component manager! rc=%#x\n", rc);
+ return;
+ }
+ rc = manager->CreateInstanceByContractID(NS_SESSION_CONTRACTID,
+ nsnull,
+ NS_GET_IID(ISession),
+ getter_AddRefs(session));
+ if (NS_FAILED(rc))
+ {
+ printf("Error, could not instantiate session object! rc=%#x\n", rc);
+ return;
+ }
+
+ rc = machine->LockMachine(session, LockType_Write);
+ if (NS_FAILED(rc))
+ {
+ printf("Error, could not lock the machine for the session! rc=%#x\n", rc);
+ return;
+ }
+
+ /*
+ * After the machine is registered, the initial machine object becomes
+ * immutable. In order to get a mutable machine object, we must query
+ * it from the opened session object.
+ */
+ rc = session->GetMachine(getter_AddRefs(sessionMachine));
+ if (NS_FAILED(rc))
+ {
+ printf("Error, could not get machine session! rc=%#x\n", rc);
+ return;
+ }
+ }
+
+ /*
+ * Create a virtual harddisk
+ */
+ nsCOMPtr<IMedium> hardDisk = 0;
+ rc = virtualBox->CreateMedium(NS_LITERAL_STRING("VDI").get(),
+ NS_LITERAL_STRING("/tmp/TestHardDisk.vdi").get(),
+ AccessMode_ReadWrite, DeviceType_HardDisk,
+ getter_AddRefs(hardDisk));
+ if (NS_FAILED(rc))
+ {
+ printf("Failed creating a hard disk object! rc=%#x\n", rc);
+ }
+ else
+ {
+ /*
+ * We have only created an object so far. No on disk representation exists
+ * because none of its properties has been set so far. Let's continue creating
+ * a dynamically expanding image.
+ */
+ nsCOMPtr<IProgress> progress;
+ MediumVariant_T mediumVariants[] =
+ { MediumVariant_Standard };
+ rc = hardDisk->CreateBaseStorage(100 * 1024 * 1024, // size in bytes
+ sizeof(mediumVariants) / sizeof(mediumVariants[0]), mediumVariants,
+ getter_AddRefs(progress)); // optional progress object
+ if (NS_FAILED(rc))
+ {
+ printf("Failed creating hard disk image! rc=%#x\n", rc);
+ }
+ else
+ {
+ /*
+ * Creating the image is done in the background because it can take quite
+ * some time (at least fixed size images). We have to wait for its completion.
+ * Here we wait forever (timeout -1) which is potentially dangerous.
+ */
+ rc = progress->WaitForCompletion(-1);
+ PRInt32 resultCode;
+ progress->GetResultCode(&resultCode);
+ if (NS_FAILED(rc) || NS_FAILED(resultCode))
+ {
+ printf("Error: could not create hard disk! rc=%#x\n",
+ NS_FAILED(rc) ? rc : resultCode);
+ }
+ else
+ {
+ /*
+ * Now that it's created, we can assign it to the VM.
+ */
+ rc = sessionMachine->AttachDevice(
+ NS_LITERAL_STRING("IDE Controller").get(), // controller identifier
+ 0, // channel number on the controller
+ 0, // device number on the controller
+ DeviceType_HardDisk,
+ hardDisk);
+ if (NS_FAILED(rc))
+ {
+ printf("Error: could not attach hard disk! rc=%#x\n", rc);
+ }
+ }
+ }
+ }
+
+ /*
+ * It's got a hard disk but that one is new and thus not bootable. Make it
+ * boot from an ISO file. This requires some processing. First the ISO file
+ * has to be registered and then mounted to the VM's DVD drive and selected
+ * as the boot device.
+ */
+ nsCOMPtr<IMedium> dvdImage;
+ rc = virtualBox->OpenMedium(NS_LITERAL_STRING("/home/vbox/isos/winnt4ger.iso").get(),
+ DeviceType_DVD,
+ AccessMode_ReadOnly,
+ false /* fForceNewUuid */,
+ getter_AddRefs(dvdImage));
+ if (NS_FAILED(rc))
+ printf("Error: could not open CD image! rc=%#x\n", rc);
+ else
+ {
+ /*
+ * Now assign it to our VM
+ */
+ rc = sessionMachine->MountMedium(
+ NS_LITERAL_STRING("IDE Controller").get(), // controller identifier
+ 2, // channel number on the controller
+ 0, // device number on the controller
+ dvdImage,
+ PR_FALSE); // aForce
+ if (NS_FAILED(rc))
+ {
+ printf("Error: could not mount ISO image! rc=%#x\n", rc);
+ }
+ else
+ {
+ /*
+ * Last step: tell the VM to boot from the CD.
+ */
+ rc = sessionMachine->SetBootOrder(1, DeviceType::DVD);
+ if (NS_FAILED(rc))
+ {
+ printf("Could not set boot device! rc=%#x\n", rc);
+ }
+ }
+ }
+
+ /*
+ * Save all changes we've just made.
+ */
+ rc = sessionMachine->SaveSettings();
+ if (NS_FAILED(rc))
+ printf("Could not save machine settings! rc=%#x\n", rc);
+
+ /*
+ * It is always important to close the open session when it becomes not
+ * necessary any more.
+ */
+ session->UnlockMachine();
+
+ IMedium **aMedia;
+ PRUint32 cMedia;
+ rc = machine->Unregister((CleanupMode_T)CleanupMode_DetachAllReturnHardDisksOnly,
+ &cMedia, &aMedia);
+ if (NS_FAILED(rc))
+ printf("Unregistering the machine failed! rc=%#x\n", rc);
+ else
+ {
+ nsCOMPtr<IProgress> pProgress;
+ rc = machine->DeleteConfig(cMedia, aMedia, getter_AddRefs(pProgress));
+ if (NS_FAILED(rc))
+ printf("Deleting of machine failed! rc=%#x\n", rc);
+ else
+ {
+ rc = pProgress->WaitForCompletion(-1);
+ PRInt32 resultCode;
+ pProgress->GetResultCode(&resultCode);
+ if (NS_FAILED(rc) || NS_FAILED(resultCode))
+ printf("Failed to delete the machine! rc=%#x\n",
+ NS_FAILED(rc) ? rc : resultCode);
+ }
+
+ /* Release the media array: */
+ for (PRUint32 i = 0; i < cMedia; i++)
+ if (aMedia[i])
+ aMedia[i]->Release();
+ nsMemory::Free(aMedia);
+ }
+}
+
+// main
+///////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char **argv)
+{
+ /*
+ * Check that PRUnichar is equal in size to what compiler composes L""
+ * strings from; otherwise NS_LITERAL_STRING macros won't work correctly
+ * and we will get a meaningless SIGSEGV. This, of course, must be checked
+ * at compile time in xpcom/string/nsTDependentString.h, but XPCOM lacks
+ * compile-time assert macros and I'm not going to add them now.
+ */
+ if (sizeof(PRUnichar) != sizeof(wchar_t))
+ {
+ printf("Error: sizeof(PRUnichar) {%lu} != sizeof(wchar_t) {%lu}!\n"
+ "Probably, you forgot the -fshort-wchar compiler option.\n",
+ (unsigned long) sizeof(PRUnichar),
+ (unsigned long) sizeof(wchar_t));
+ return -1;
+ }
+
+#if 1 /* Please ignore this! It is very very crude. */
+# ifdef RTPATH_APP_PRIVATE_ARCH
+ if (!getenv("VBOX_XPCOM_HOME"))
+ setenv("VBOX_XPCOM_HOME", RTPATH_APP_PRIVATE_ARCH, 1);
+# else
+ char szTmp[8192];
+ if (!getenv("VBOX_XPCOM_HOME"))
+ {
+ strcpy(szTmp, argv[0]);
+ *strrchr(szTmp, '/') = '\0';
+ strcat(szTmp, "/..");
+ fprintf(stderr, "tstVBoxAPIXPCOM: VBOX_XPCOM_HOME is not set, using '%s' instead\n", szTmp);
+ setenv("VBOX_XPCOM_HOME", szTmp, 1);
+ }
+# endif
+#endif
+ (void)argc; (void)argv;
+
+ nsresult rc;
+
+ /*
+ * This is the standard XPCOM init procedure.
+ * What we do is just follow the required steps to get an instance
+ * of our main interface, which is IVirtualBox.
+ *
+ * Note that we scope all nsCOMPtr variables in order to have all XPCOM
+ * objects automatically released before we call NS_ShutdownXPCOM at the
+ * end. This is an XPCOM requirement.
+ */
+ {
+ nsCOMPtr<nsIServiceManager> serviceManager;
+ rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), nsnull, nsnull);
+ if (NS_FAILED(rc))
+ {
+ printf("Error: XPCOM could not be initialized! rc=%#x\n", rc);
+ return -1;
+ }
+
+#if 0
+ /*
+ * Register our components. This step is only necessary if this executable
+ * implements XPCOM components itself which is not the case for this
+ * simple example.
+ */
+ nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(serviceManager);
+ if (!registrar)
+ {
+ printf("Error: could not query nsIComponentRegistrar interface!\n");
+ return -1;
+ }
+ registrar->AutoRegister(nsnull);
+#endif
+
+ /*
+ * Make sure the main event queue is created. This event queue is
+ * responsible for dispatching incoming XPCOM IPC messages. The main
+ * thread should run this event queue's loop during lengthy non-XPCOM
+ * operations to ensure messages from the VirtualBox server and other
+ * XPCOM IPC clients are processed. This use case doesn't perform such
+ * operations so it doesn't run the event loop.
+ */
+ nsCOMPtr<nsIEventQueue> eventQ;
+ rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
+ if (NS_FAILED(rc))
+ {
+ printf("Error: could not get main event queue! rc=%#x\n", rc);
+ return -1;
+ }
+
+ /*
+ * Now XPCOM is ready and we can start to do real work.
+ * IVirtualBox is the root interface of VirtualBox and will be
+ * retrieved from the XPCOM component manager. We use the
+ * XPCOM provided smart pointer nsCOMPtr for all objects because
+ * that's very convenient and removes the need deal with reference
+ * counting and freeing.
+ */
+ nsCOMPtr<nsIComponentManager> manager;
+ rc = NS_GetComponentManager(getter_AddRefs(manager));
+ if (NS_FAILED(rc))
+ {
+ printf("Error: could not get component manager! rc=%#x\n", rc);
+ return -1;
+ }
+
+ nsCOMPtr<IVirtualBox> virtualBox;
+ rc = manager->CreateInstanceByContractID(NS_VIRTUALBOX_CONTRACTID,
+ nsnull,
+ NS_GET_IID(IVirtualBox),
+ getter_AddRefs(virtualBox));
+ if (NS_FAILED(rc))
+ {
+ printf("Error, could not instantiate VirtualBox object! rc=%#x\n", rc);
+ return -1;
+ }
+ printf("VirtualBox object created\n");
+
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+
+
+ listVMs(virtualBox);
+
+ createVM(virtualBox);
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+
+ /* this is enough to free the IVirtualBox instance -- smart pointers rule! */
+ virtualBox = nsnull;
+
+ /*
+ * Process events that might have queued up in the XPCOM event
+ * queue. If we don't process them, the server might hang.
+ */
+ eventQ->ProcessPendingEvents();
+ }
+
+ /*
+ * Perform the standard XPCOM shutdown procedure.
+ */
+ NS_ShutdownXPCOM(nsnull);
+ printf("Done!\n");
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+//// Helpers
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Helper function to convert an nsID into a human readable string
+ *
+ * @returns result string, allocated. Has to be freed using free()
+ * @param guid Pointer to nsID that will be converted.
+ */
+char *nsIDToString(nsID *guid)
+{
+ char *res = (char*)malloc(39);
+
+ if (res != NULL)
+ {
+ snprintf(res, 39, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+ guid->m0, (PRUint32)guid->m1, (PRUint32)guid->m2,
+ (PRUint32)guid->m3[0], (PRUint32)guid->m3[1], (PRUint32)guid->m3[2],
+ (PRUint32)guid->m3[3], (PRUint32)guid->m3[4], (PRUint32)guid->m3[5],
+ (PRUint32)guid->m3[6], (PRUint32)guid->m3[7]);
+ }
+ return res;
+}
+
+/**
+ * Helper function to print XPCOM exception information set on the current
+ * thread after a failed XPCOM method call. This function will also print
+ * extended VirtualBox error info if it is available.
+ */
+void printErrorInfo()
+{
+ nsresult rc;
+
+ nsCOMPtr<nsIExceptionService> es;
+ es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
+ if (NS_SUCCEEDED(rc))
+ {
+ nsCOMPtr<nsIExceptionManager> em;
+ rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
+ if (NS_SUCCEEDED(rc))
+ {
+ nsCOMPtr<nsIException> ex;
+ rc = em->GetCurrentException(getter_AddRefs(ex));
+ if (NS_SUCCEEDED(rc) && ex)
+ {
+ nsCOMPtr<IVirtualBoxErrorInfo> info;
+ info = do_QueryInterface(ex, &rc);
+ if (NS_SUCCEEDED(rc) && info)
+ {
+ /* got extended error info */
+ printf("Extended error info (IVirtualBoxErrorInfo):\n");
+ PRInt32 resultCode = NS_OK;
+ info->GetResultCode(&resultCode);
+ printf(" resultCode=%08X\n", resultCode);
+ nsXPIDLString component;
+ info->GetComponent(getter_Copies(component));
+ printf(" component=%s\n", NS_ConvertUTF16toUTF8(component).get());
+ nsXPIDLString text;
+ info->GetText(getter_Copies(text));
+ printf(" text=%s\n", NS_ConvertUTF16toUTF8(text).get());
+ }
+ else
+ {
+ /* got basic error info */
+ printf("Basic error info (nsIException):\n");
+ nsresult resultCode = NS_OK;
+ ex->GetResult(&resultCode);
+ printf(" resultCode=%08X\n", resultCode);
+ nsXPIDLCString message;
+ ex->GetMessage(getter_Copies(message));
+ printf(" message=%s\n", message.get());
+ }
+
+ /* reset the exception to NULL to indicate we've processed it */
+ em->SetCurrentException(NULL);
+
+ rc = NS_OK;
+ }
+ }
+ }
+}
diff --git a/src/VBox/Main/testcase/tstVBoxCrypto.cpp b/src/VBox/Main/testcase/tstVBoxCrypto.cpp
new file mode 100644
index 00000000..f6216845
--- /dev/null
+++ b/src/VBox/Main/testcase/tstVBoxCrypto.cpp
@@ -0,0 +1,429 @@
+/* $Id: tstVBoxCrypto.cpp $ */
+/** @file
+ * tstVBoxCrypto - Testcase for the cryptographic support module.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/VBoxCryptoIf.h>
+#include <VBox/err.h>
+
+#include <iprt/file.h>
+#include <iprt/test.h>
+#include <iprt/ldr.h>
+#include <iprt/mem.h>
+#include <iprt/memsafer.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest;
+static const uint8_t g_abDek[64] = { 0x42 };
+static const char g_szPassword[] = "testtesttest";
+static const char g_szPasswordWrong[] = "testtest";
+
+static const char *g_aCiphers[] =
+{
+ "AES-XTS128-PLAIN64",
+ "AES-GCM128",
+ "AES-CTR128",
+
+ "AES-XTS256-PLAIN64",
+ "AES-GCM256",
+ "AES-CTR256"
+};
+
+#define CHECK_STR(str1, str2) do { if (strcmp(str1, str2)) { RTTestIFailed("line %u: '%s' != '%s' (*)", __LINE__, str1, str2); } } while (0)
+#define CHECK_BYTES(bytes1, bytes2, size) do { if (memcmp(bytes1, bytes2, size)) { RTTestIFailed("line %u: '%s' != '%s' (*)", __LINE__, #bytes1, bytes2); } } while (0)
+
+
+/**
+ * Creates a new cryptographic context and returns the encoded string version on success.
+ *
+ * @returns VBox status code.
+ * @param pCryptoIf Pointer to the cryptographic interface.
+ * @param pszCipher The cipher to use.
+ * @param pszPassword The password to use.
+ * @param ppszCtx Where to store the pointer to the context on success.
+ */
+static int tstCryptoCtxCreate(PCVBOXCRYPTOIF pCryptoIf, const char *pszCipher, const char *pszPassword, char **ppszCtx)
+{
+ VBOXCRYPTOCTX hCryptoCtx;
+
+ int rc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, pszPassword, &hCryptoCtx);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, ppszCtx);
+ int rc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
+ AssertReleaseRC(rc2);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Writes data to the given file until the given size is reached.
+ *
+ * @returns VBox status code.
+ * @param hVfsFile The file handle to write to.
+ * @param cbWrite Number of bytes to write.
+ */
+static int tstCryptoVfsWrite(RTVFSFILE hVfsFile, size_t cbWrite)
+{
+ RTTestISub("Writing to encrypted file");
+
+ int rc = VINF_SUCCESS;
+ size_t cbBufLeft = _128K;
+ void *pv = RTMemTmpAllocZ(cbBufLeft);
+ if (pv)
+ {
+ size_t cbLeft = cbWrite;
+ uint32_t cCounter = 0;
+ uint8_t *pb = (uint8_t *)pv;
+
+ /* Fill the counter buffer. */
+ uint32_t *pu32 = (uint32_t *)pv;
+ for (uint32_t i = 0; i < cbBufLeft / sizeof(uint32_t); i++)
+ *pu32++ = cCounter++;
+
+
+ for (;;)
+ {
+ size_t cbThisWrite = RTRandU64Ex(1, RT_MIN(cbBufLeft, cbLeft));
+ rc = RTVfsFileWrite(hVfsFile, pb, cbThisWrite, NULL /*pcbWritten*/);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("Writing to file failed with %Rrc (cbLeft=%zu, cbBufLeft=%zu, cbThisWrite=%zu)",
+ rc, cbLeft, cbBufLeft, cbThisWrite);
+ break;
+ }
+
+ cbLeft -= cbThisWrite;
+ cbBufLeft -= cbThisWrite;
+ pb += cbThisWrite;
+
+ if (!cbBufLeft)
+ {
+ /* Fill the counter buffer again. */
+ pu32 = (uint32_t *)pv;
+ pb = (uint8_t *)pv;
+ cbBufLeft = _128K;
+ for (uint32_t i = 0; i < cbBufLeft / sizeof(uint32_t); i++)
+ *pu32++ = cCounter++;
+ }
+
+ if (!cbLeft)
+ break;
+ }
+
+ RTMemTmpFree(pv);
+ }
+ else
+ {
+ RTTestIFailed("Allocating write buffer failed - out of memory");
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTTestISubDone();
+ return rc;
+}
+
+
+/**
+ * Writes data to the given file until the given size is reached.
+ *
+ * @returns VBox status code.
+ * @param hVfsFile The file handle to write to.
+ * @param cbFile Size of the file payload in bytes.
+ */
+static int tstCryptoVfsReadAndVerify(RTVFSFILE hVfsFile, size_t cbFile)
+{
+ RTTestISub("Reading from encrypted file and verifying data");
+
+ int rc = VINF_SUCCESS;
+ void *pv = RTMemTmpAllocZ(_128K);
+ if (pv)
+ {
+ size_t cbLeft = cbFile;
+ uint32_t cCounter = 0;
+
+ for (;;)
+ {
+ /* Read the data in multiple calls. */
+ size_t cbBufLeft = RT_MIN(cbLeft, _128K);
+ uint8_t *pb = (uint8_t *)pv;
+
+ while (cbBufLeft)
+ {
+ size_t cbThisRead = RTRandU64Ex(1, RT_MIN(cbBufLeft, cbLeft));
+ rc = RTVfsFileRead(hVfsFile, pb, cbThisRead, NULL /*pcbWritten*/);
+ if (RT_FAILURE(rc))
+ {
+ RTTestIFailed("Reading from file failed with %Rrc (cbLeft=%zu, cbBufLeft=%zu, cbThisRead=%zu)",
+ rc, cbLeft, cbBufLeft, cbThisRead);
+ break;
+ }
+
+ cbBufLeft -= cbThisRead;
+ pb += cbThisRead;
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+
+ /* Verify the read data. */
+ size_t cbInBuffer = RT_MIN(cbLeft, _128K);
+ Assert(!(cbInBuffer % sizeof(uint32_t)));
+ uint32_t *pu32 = (uint32_t *)pv;
+
+ for (uint32_t i = 0; i < cbInBuffer / sizeof(uint32_t); i++)
+ {
+ if (*pu32 != cCounter)
+ {
+ RTTestIFailed("Reading from file resulted in corrupted data (expected '%#x' got '%#x')",
+ cCounter, *pu32);
+ break;
+ }
+
+ pu32++;
+ cCounter++;
+ }
+
+ cbLeft -= RT_MIN(cbLeft, _128K);
+ if (!cbLeft)
+ break;
+ }
+
+ RTMemTmpFree(pv);
+ }
+ else
+ {
+ RTTestIFailed("Allocating read buffer failed - out of memory");
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTTestISubDone();
+ return rc;
+}
+
+
+/**
+ * Testing some basics of the encrypted file VFS code.
+ *
+ * @param pCryptoIf Pointer to the callback table.
+ */
+static void tstCryptoVfsBasics(PCVBOXCRYPTOIF pCryptoIf)
+{
+ RTTestISub("Encrypted file - Basics");
+
+ RTTestDisableAssertions(g_hTest);
+
+ char *pszCtx = NULL;
+ int rc = tstCryptoCtxCreate(pCryptoIf, g_aCiphers[4], g_szPassword, &pszCtx);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create the memory file to write to. */
+ RTVFSFILE hVfsFile;
+ rc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, 0 /*cbEstimate*/, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSFILE hVfsFileEnc;
+
+ RTTestISub("Creating encrypted file");
+
+ rc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszCtx, g_szPassword, &hVfsFileEnc);
+ if (RT_SUCCESS(rc))
+ {
+ RTTestISubDone();
+
+ size_t cbFile = RT_ALIGN_Z(RTRandU32Ex(_1K, 10 * _1M), sizeof(uint32_t)); /* Align to full counter field size. */
+ rc = tstCryptoVfsWrite(hVfsFileEnc, cbFile);
+ RTVfsFileRelease(hVfsFileEnc); /* Close file. */
+ if (RT_SUCCESS(rc))
+ {
+ /* Reopen for reading. */
+ RTTestISub("Open encrypted file");
+
+ /* Reset the memory file offset. */
+ RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
+
+ rc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszCtx, g_szPassword, &hVfsFileEnc);
+ if (RT_SUCCESS(rc))
+ {
+ RTTestISubDone();
+
+ RTTestISub("Query encrypted file size");
+ uint64_t cbFileRd;
+ rc = RTVfsFileQuerySize(hVfsFileEnc, &cbFileRd);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbFile != cbFileRd)
+ RTTestIFailed("Unexpected file size, got %#llx expected %#zx", cbFileRd, cbFile);
+
+ RTTestISubDone();
+ tstCryptoVfsReadAndVerify(hVfsFileEnc, cbFile);
+ }
+ else
+ RTTestIFailed("Querying encrypted file size failed %Rrc", rc);
+
+ RTVfsFileRelease(hVfsFileEnc); /* Close file. */
+ }
+ else
+ RTTestIFailed("Opening encrypted file for reading failed with %Rrc", rc);
+
+ }
+ /* Error set on failure. */
+ }
+ else
+ RTTestIFailed("Creating encrypted file handle failed with %Rrc", rc);
+
+ RTVfsFileRelease(hVfsFile);
+ }
+ else
+ RTTestIFailed("Creating a new encrypted file failed with %Rrc", rc);
+
+ RTMemFree(pszCtx);
+ }
+ else
+ RTTestIFailed("Creating a new encrypted context failed with %Rrc", rc);
+
+ RTTestRestoreAssertions(g_hTest);
+ RTTestISubDone();
+}
+
+
+/**
+ * Testing some basics of the crypto keystore code.
+ *
+ * @param pCryptoIf Pointer to the callback table.
+ */
+static void tstCryptoKeyStoreBasics(PCVBOXCRYPTOIF pCryptoIf)
+{
+ RTTestISub("Crypto Keystore - Basics");
+
+ RTTestDisableAssertions(g_hTest);
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aCiphers); i++)
+ {
+ RTTestISubF("Creating a new keystore for cipher '%s'", g_aCiphers[i]);
+
+ char *pszKeystoreEnc = NULL; /**< The encoded keystore. */
+ int rc = pCryptoIf->pfnCryptoKeyStoreCreate(g_szPassword, &g_abDek[0], sizeof(g_abDek),
+ g_aCiphers[i], &pszKeystoreEnc);
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t *pbKey = NULL;
+ size_t cbKey = 0;
+ char *pszCipher = NULL;
+
+ RTTestSub(g_hTest, "Trying to unlock DEK with wrong password");
+ rc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(pszKeystoreEnc, g_szPasswordWrong,
+ &pbKey, &cbKey, &pszCipher);
+ RTTESTI_CHECK_RC(rc, VERR_VD_PASSWORD_INCORRECT);
+
+ RTTestSub(g_hTest, "Trying to unlock DEK with correct password");
+ rc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(pszKeystoreEnc, g_szPassword,
+ &pbKey, &cbKey, &pszCipher);
+ RTTESTI_CHECK_RC_OK(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RTTESTI_CHECK(cbKey == sizeof(g_abDek));
+ CHECK_STR(pszCipher, g_aCiphers[i]);
+ CHECK_BYTES(pbKey, &g_abDek[0], sizeof(g_abDek));
+
+ RTMemSaferFree(pbKey, cbKey);
+ }
+
+ RTMemFree(pszKeystoreEnc);
+ }
+ else
+ RTTestIFailed("Creating a new keystore failed with %Rrc", rc);
+ }
+
+ RTTestRestoreAssertions(g_hTest);
+}
+
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Initialization.
+ */
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstVBoxCrypto", &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ RTTestBanner(g_hTest);
+
+ RTTestSub(g_hTest, "Loading the cryptographic support module");
+ const char *pszModCrypto = NULL;
+ if (argc == 2)
+ {
+ /* The module to load is given on the command line. */
+ pszModCrypto = argv[1];
+ }
+ else
+ {
+ /* Try find it in the extension pack. */
+ /** @todo */
+ RTTestSkipped(g_hTest, "Getting the module from the extension pack is not implemented yet, skipping testcase");
+ }
+
+ if (pszModCrypto)
+ {
+ RTLDRMOD hLdrModCrypto = NIL_RTLDRMOD;
+ int rc = RTLdrLoad(pszModCrypto, &hLdrModCrypto);
+ if (RT_SUCCESS(rc))
+ {
+ PFNVBOXCRYPTOENTRY pfnCryptoEntry = NULL;
+ rc = RTLdrGetSymbol(hLdrModCrypto, VBOX_CRYPTO_MOD_ENTRY_POINT, (void **)&pfnCryptoEntry);
+ if (RT_SUCCESS(rc))
+ {
+ PCVBOXCRYPTOIF pCryptoIf = NULL;
+ rc = pfnCryptoEntry(&pCryptoIf);
+ if (RT_SUCCESS(rc))
+ {
+ /* Loading succeeded, now we can start real testing. */
+ tstCryptoKeyStoreBasics(pCryptoIf);
+ tstCryptoVfsBasics(pCryptoIf);
+ }
+ else
+ RTTestIFailed("Calling '%s' failed with %Rrc", VBOX_CRYPTO_MOD_ENTRY_POINT, rc);
+ }
+ else
+ RTTestIFailed("Failed to resolve entry point '%s' with %Rrc", VBOX_CRYPTO_MOD_ENTRY_POINT, rc);
+ }
+ else
+ RTTestIFailed("Failed to load the crypto module '%s' with %Rrc", pszModCrypto, rc);
+ }
+
+ return RTTestSummaryAndDestroy(g_hTest);
+}
diff --git a/src/VBox/Main/testcase/tstVBoxMultipleVM.cpp b/src/VBox/Main/testcase/tstVBoxMultipleVM.cpp
new file mode 100644
index 00000000..b59d64fb
--- /dev/null
+++ b/src/VBox/Main/testcase/tstVBoxMultipleVM.cpp
@@ -0,0 +1,617 @@
+/** @file
+ * tstVBoxMultipleVM - load test for ClientWatcher.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/array.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <VBox/com/VirtualBox.h>
+#include <iprt/stream.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#include <VBox/sup.h>
+
+#include <vector>
+#include <algorithm>
+
+#include <iprt/test.h>
+#include <iprt/time.h>
+#include <iprt/rand.h>
+#include <iprt/getopt.h>
+
+using namespace com;
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/* Arguments of test thread */
+struct TestThreadArgs
+{
+ /** number of machines that should be run simultaneousely */
+ uint32_t machinesPackSize;
+ /** percents of VM Stop operation what should be called
+ * without session unlocking */
+ uint32_t percentsUnlok;
+ /** How much time in milliseconds test will be executed */
+ uint64_t cMsExecutionTime;
+ /** How much machines create for the test */
+ uint32_t numberMachines;
+};
+
+
+/*********************************************************************************************************************************
+* Global Variables & defs *
+*********************************************************************************************************************************/
+static RTTEST g_hTest;
+#ifdef RT_ARCH_AMD64
+typedef std::vector<Bstr> TMachinesList;
+static volatile bool g_RunTest = true;
+static RTSEMEVENT g_PingEevent;
+static volatile uint64_t g_Counter = 0;
+static TestThreadArgs g_Args;
+
+
+/** Worker for TST_COM_EXPR(). */
+static HRESULT tstComExpr(HRESULT hrc, const char *pszOperation, int iLine)
+{
+ if (FAILED(hrc))
+ {
+ RTTestFailed(g_hTest, "%s failed on line %u with hrc=%Rhrc\n", pszOperation, iLine, hrc);
+ }
+ return hrc;
+}
+
+
+#define CHECK_ERROR_L(iface, method) \
+ do { \
+ hrc = iface->method; \
+ if (FAILED(hrc)) \
+ RTPrintf("warning: %s->%s failed on line %u with hrc=%Rhrc\n", #iface, #method, __LINE__, hrc);\
+ } while (0)
+
+
+/** Macro that executes the given expression and report any failure.
+ * The expression must return a HRESULT. */
+#define TST_COM_EXPR(expr) tstComExpr(expr, #expr, __LINE__)
+
+
+static int tstStartVM(IVirtualBox *pVBox, ISession *pSession, Bstr machineID, bool fSkipUnlock)
+{
+ HRESULT hrc;
+ ComPtr<IProgress> progress;
+ ComPtr<IMachine> machine;
+ Bstr machineName;
+
+ hrc = TST_COM_EXPR(pVBox->FindMachine(machineID.raw(), machine.asOutParam()));
+ if(SUCCEEDED(hrc))
+ hrc = TST_COM_EXPR(machine->COMGETTER(Name)(machineName.asOutParam()));
+ if(SUCCEEDED(hrc))
+ {
+ hrc = machine->LaunchVMProcess(pSession, Bstr("headless").raw(),
+ ComSafeArrayNullInParam(), progress.asOutParam());
+ }
+ if (SUCCEEDED(hrc) && !progress.isNull())
+ {
+ CHECK_ERROR_L(progress, WaitForCompletion(-1));
+ if (SUCCEEDED(hrc))
+ {
+ BOOL completed = true;
+ CHECK_ERROR_L(progress, COMGETTER(Completed)(&completed));
+ if (SUCCEEDED(hrc))
+ {
+ Assert(completed);
+ LONG iRc;
+ CHECK_ERROR_L(progress, COMGETTER(ResultCode)(&iRc));
+ if (SUCCEEDED(hrc))
+ {
+ if (FAILED(iRc))
+ {
+ ProgressErrorInfo info(progress);
+ RTPrintf("Start VM '%ls' failed. Warning: %ls.\n", machineName.raw(), info.getText().raw());
+ }
+ else
+ RTPrintf("VM '%ls' started.\n", machineName.raw());
+ }
+ }
+ }
+ if (!fSkipUnlock)
+ pSession->UnlockMachine();
+ else
+ RTPrintf("Session unlock skipped.\n");
+ }
+ return hrc;
+}
+
+
+static int tstStopVM(IVirtualBox* pVBox, ISession* pSession, Bstr machineID, bool fSkipUnlock)
+{
+ ComPtr<IMachine> machine;
+ HRESULT hrc = TST_COM_EXPR(pVBox->FindMachine(machineID.raw(), machine.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ Bstr machineName;
+ hrc = TST_COM_EXPR(machine->COMGETTER(Name)(machineName.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ MachineState_T machineState;
+ hrc = TST_COM_EXPR(machine->COMGETTER(State)(&machineState));
+ // check that machine is in running state
+ if ( SUCCEEDED(hrc)
+ && ( machineState == MachineState_Running
+ || machineState == MachineState_Paused))
+ {
+ ComPtr<IConsole> console;
+ ComPtr<IProgress> progress;
+
+ hrc = TST_COM_EXPR(machine->LockMachine(pSession, LockType_Shared));
+ if(SUCCEEDED(hrc))
+ TST_COM_EXPR(pSession->COMGETTER(Console)(console.asOutParam()));
+ if(SUCCEEDED(hrc))
+ hrc = console->PowerDown(progress.asOutParam());
+ if (SUCCEEDED(hrc) && !progress.isNull())
+ {
+ //RTPrintf("Stopping VM %ls...\n", machineName.raw());
+ CHECK_ERROR_L(progress, WaitForCompletion(-1));
+ if (SUCCEEDED(hrc))
+ {
+ BOOL completed = true;
+ CHECK_ERROR_L(progress, COMGETTER(Completed)(&completed));
+ if (SUCCEEDED(hrc))
+ {
+ //ASSERT(completed);
+ LONG iRc;
+ CHECK_ERROR_L(progress, COMGETTER(ResultCode)(&iRc));
+ if (SUCCEEDED(hrc))
+ {
+ if (FAILED(iRc))
+ {
+ ProgressErrorInfo info(progress);
+ RTPrintf("Stop VM %ls failed. Warning: %ls.\n", machineName.raw(), info.getText().raw());
+ hrc = iRc;
+ }
+ else
+ {
+ RTPrintf("VM '%ls' stopped.\n", machineName.raw());
+ }
+ }
+ }
+ }
+ if (!fSkipUnlock)
+ pSession->UnlockMachine();
+ else
+ RTPrintf("Session unlock skipped.\n");
+ }
+ }
+ }
+ }
+ return hrc;
+}
+
+
+/**
+ * Get random @a maxCount machines from list of existing VMs.
+ *
+ * @note Can return less then maxCount machines.
+ */
+static int tstGetMachinesList(IVirtualBox *pVBox, uint32_t maxCount, TMachinesList &listToFill)
+{
+ com::SafeIfaceArray<IMachine> machines;
+ HRESULT hrc = TST_COM_EXPR(pVBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines)));
+ if (SUCCEEDED(hrc))
+ {
+
+ size_t cMachines = RT_MIN(machines.size(), maxCount);
+ for (size_t i = 0; i < cMachines; ++i)
+ {
+ // choose random index of machine
+ uint32_t idx = RTRandU32Ex(0, (uint32_t)machines.size() - 1);
+ if (machines[idx])
+ {
+ Bstr bstrId;
+ Bstr machineName;
+ CHECK_ERROR_L(machines[idx], COMGETTER(Id)(bstrId.asOutParam()));
+ if (SUCCEEDED(hrc))
+ CHECK_ERROR_L(machines[idx], COMGETTER(Name)(machineName.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ if (Utf8Str(machineName).startsWith("umtvm"))
+ listToFill.push_back(bstrId);
+ }
+ }
+ }
+
+ // remove duplicates from the vector
+ std::sort(listToFill.begin(), listToFill.end());
+ listToFill.erase(std::unique(listToFill.begin(), listToFill.end()), listToFill.end());
+ RTPrintf("Filled pack of %d from %d machines.\n", listToFill.size(), machines.size());
+ }
+
+ return hrc;
+}
+
+
+static int tstMachinesPack(IVirtualBox *pVBox, uint32_t maxPackSize, uint32_t percentage)
+{
+ HRESULT hrc = S_OK;
+ TMachinesList machinesList;
+ bool alwaysUnlock = false;
+ uint64_t percN = 0;
+
+ // choose and fill pack of machines for test
+ tstGetMachinesList(pVBox, maxPackSize, machinesList);
+
+ RTPrintf("Start test.\n");
+ // screw up counter
+ g_Counter = UINT64_MAX - machinesList.size() <= g_Counter ? 0 : g_Counter;
+ if (percentage > 0)
+ percN = 100 / percentage;
+ else
+ alwaysUnlock = true;
+
+ // start all machines in pack
+ for (TMachinesList::iterator it = machinesList.begin();
+ it != machinesList.end() && g_RunTest;
+ ++it)
+ {
+ ComPtr<ISession> session;
+ hrc = session.createInprocObject(CLSID_Session);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = tstStartVM(pVBox, session, *it, !(alwaysUnlock || g_Counter++ % percN));
+ }
+ RTSemEventSignal(g_PingEevent);
+ RTThreadSleep(100);
+ }
+ // stop all machines in the pack
+ for (TMachinesList::iterator it = machinesList.begin();
+ it != machinesList.end() && g_RunTest;
+ ++it)
+ {
+ ComPtr<ISession> session;
+ hrc = session.createInprocObject(CLSID_Session);
+ if (SUCCEEDED(hrc))
+ {
+ // stop machines, skip session unlock of given % of machines
+ hrc = tstStopVM(pVBox, session, *it, !(alwaysUnlock || g_Counter++ % percN));
+ }
+ RTSemEventSignal(g_PingEevent);
+ RTThreadSleep(100);
+ }
+ return hrc;
+}
+
+
+static Bstr tstMakeMachineName(int i)
+{
+ char szMachineName[32];
+ RTStrPrintf(szMachineName, sizeof(szMachineName), "umtvm%d", i);
+ return Bstr(szMachineName);
+}
+
+
+static int tstCreateMachines(IVirtualBox *pVBox)
+{
+ HRESULT hrc = S_OK;
+ // create machines for the test
+ for (uint32_t i = 0; i < g_Args.numberMachines; i++)
+ {
+ ComPtr<IMachine> ptrMachine;
+ com::SafeArray<BSTR> groups;
+
+ Bstr machineName(tstMakeMachineName(i));
+ /* Default VM settings */
+ CHECK_ERROR_L(pVBox, CreateMachine(NULL, /* Settings */
+ machineName.raw(), /* Name */
+ ComSafeArrayAsInParam(groups), /* Groups */
+ NULL, /* OS Type */
+ NULL, /** Cipher */
+ NULL, /** Password id */
+ NULL, /** Password */
+ NULL, /* Create flags */
+ ptrMachine.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ CHECK_ERROR_L(pVBox, RegisterMachine(ptrMachine));
+ RTPrintf("Machine '%ls' created\n", machineName.raw());
+ }
+
+ RTSemEventSignal(g_PingEevent);
+ RTThreadSleep(100);
+ }
+ return hrc;
+}
+
+
+static int tstClean(IVirtualBox *pVBox, IVirtualBoxClient *pClient)
+{
+ RT_NOREF(pClient);
+ HRESULT hrc = S_OK;
+
+ // stop all machines created for the test
+ for (uint32_t i = 0; i < g_Args.numberMachines; i++)
+ {
+ ComPtr<IMachine> machine;
+ ComPtr<IProgress> progress;
+ ComPtr<ISession> session;
+ SafeIfaceArray<IMedium> media;
+
+ Bstr machineName(tstMakeMachineName(i));
+
+ /* Delete created VM and its files */
+ CHECK_ERROR_L(pVBox, FindMachine(machineName.raw(), machine.asOutParam()));
+
+ // try to stop it again if it was not stopped
+ if (SUCCEEDED(hrc))
+ {
+ MachineState_T machineState;
+ CHECK_ERROR_L(machine, COMGETTER(State)(&machineState));
+ if ( SUCCEEDED(hrc)
+ && ( machineState == MachineState_Running
+ || machineState == MachineState_Paused) )
+ {
+ hrc = session.createInprocObject(CLSID_Session);
+ if (SUCCEEDED(hrc))
+ tstStopVM(pVBox, session, machineName, FALSE);
+ }
+ }
+
+ if (SUCCEEDED(hrc))
+ CHECK_ERROR_L(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(media)));
+ if (SUCCEEDED(hrc))
+ CHECK_ERROR_L(machine, DeleteConfig(ComSafeArrayAsInParam(media), progress.asOutParam()));
+ if (SUCCEEDED(hrc))
+ CHECK_ERROR_L(progress, WaitForCompletion(-1));
+ if (SUCCEEDED(hrc))
+ RTPrintf("Machine '%ls' deleted.\n", machineName.raw());
+ }
+ return hrc;
+}
+
+
+static DECLCALLBACK(int) tstThreadRun(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf);
+ TestThreadArgs* args = (TestThreadArgs*)pvUser;
+ Assert(args != NULL);
+ uint32_t maxPackSize = args->machinesPackSize;
+ uint32_t percentage = args->percentsUnlok;
+
+ HRESULT hrc = com::Initialize();
+ if (SUCCEEDED(hrc))
+ {
+ ComPtr<IVirtualBoxClient> ptrVBoxClient;
+ ComPtr<IVirtualBox> ptrVBox;
+
+ hrc = TST_COM_EXPR(ptrVBoxClient.createInprocObject(CLSID_VirtualBoxClient));
+ if (SUCCEEDED(hrc))
+ hrc = TST_COM_EXPR(ptrVBoxClient->COMGETTER(VirtualBox)(ptrVBox.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ RTPrintf("Creating machines...\n");
+ tstCreateMachines(ptrVBox);
+
+ while (g_RunTest)
+ {
+ hrc = tstMachinesPack(ptrVBox, maxPackSize, percentage);
+ }
+
+ RTPrintf("Deleting machines...\n");
+ tstClean(ptrVBox, ptrVBoxClient);
+ }
+
+ g_RunTest = false;
+ RTSemEventSignal(g_PingEevent);
+ RTThreadSleep(100);
+
+ ptrVBox = NULL;
+ ptrVBoxClient = NULL;
+ com::Shutdown();
+ }
+ return hrc;
+}
+
+
+static int ParseArguments(int argc, char **argv, TestThreadArgs *pArgs)
+{
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--packsize", 'p', RTGETOPT_REQ_UINT32 }, // number of machines to start together
+ { "--lock", 's', RTGETOPT_REQ_UINT32 }, // percentage of VM sessions closed without Unlok
+ { "--time", 't', RTGETOPT_REQ_UINT64 }, // required time of load test execution, in seconds
+ { "--machines" , 'u', RTGETOPT_REQ_UINT32 }
+ };
+ int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
+ AssertRCReturn(rc, rc);
+ AssertPtr(pArgs);
+
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'p':
+ if (ValueUnion.u32 == 0)
+ {
+ RTPrintf("--packsize should be more then zero\n");
+ return VERR_INVALID_PARAMETER;
+ }
+ if (ValueUnion.u32 > 16000)
+ {
+ RTPrintf("maximum --packsize value is 16000.\n"
+ "That means can use no more then 16000 machines for the test.\n");
+ return VERR_INVALID_PARAMETER;
+ }
+ pArgs->machinesPackSize = ValueUnion.u32;
+ break;
+
+ case 's':
+ if (ValueUnion.u32 > 100)
+ {
+ RTPrintf("maximum --lock value is 100.\n"
+ "That means 100 percent of sessions should be closed without unlock.\n");
+ return VERR_INVALID_PARAMETER;
+ }
+ pArgs->percentsUnlok = ValueUnion.u32;
+ break;
+
+ case 't':
+ pArgs->cMsExecutionTime = ValueUnion.u64 * 1000;
+ break;
+
+ case 'u':
+ if (ValueUnion.u32 > 16000)
+ {
+ RTPrintf("maximum --machines value is 16000.\n"
+ "That means can make no more then 16000 machines for the test.\n");
+ return VERR_INVALID_PARAMETER;
+ }
+ if (ValueUnion.u32 < pArgs->machinesPackSize)
+ {
+ RTPrintf("--machines value should be larger then --packsize value.\n");
+ return VERR_INVALID_PARAMETER;
+ }
+ pArgs->numberMachines = ValueUnion.u32;
+ break;
+
+ default:
+ RTGetOptPrintError(rc, &ValueUnion);
+ return rc;
+ }
+ }
+ return rc;
+}
+
+#endif /* RT_ARCH_AMD64 */
+
+
+/**
+ *
+ * Examples:
+ * - tstVBoxClientWatcherLoad --packsize 500 --lock 10 --time 14400 --machines 4000
+ * It will create 4000 VMs with names "utmvm0"..."utmvm3999". It will start
+ * 500 random VMs together, stop them, without closing their session with
+ * probability 10%, will repeat this over 4 hours. After test it will
+ * delete all "utmvm..." machines.
+ *
+ * - tstVBoxClientWatcherLoad --packsize 1 --lock 30 --time 3600 --machines 1000
+ * It will create 1000 VMs with names "utmvm0"..."utmvm999". It will start
+ * random VM - stop them, without closing their session with probability
+ * 30%, will repeat this over 30 minutes. After test it will delete all
+ * "utmvm..." machines.
+ */
+int main(int argc, char **argv)
+{
+ RT_NOREF(argc, argv);
+ RTEXITCODE rcExit = RTTestInitAndCreate("tstVBoxMultipleVM", &g_hTest);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ SUPR3Init(NULL);
+ com::Initialize();
+ RTTestBanner(g_hTest);
+
+#ifndef RT_ARCH_AMD64
+ /*
+ * Linux OOM killer when running many VMs on a 32-bit host.
+ */
+ return RTTestSkipAndDestroy(g_hTest, "The test can only run reliably on 64-bit hosts.");
+#else /* RT_ARCH_AMD64 */
+
+ RTPrintf("Initializing ...\n");
+ int rc = RTSemEventCreate(&g_PingEevent);
+ AssertRC(rc);
+
+ g_Args.machinesPackSize = 100;
+ g_Args.percentsUnlok = 10;
+ g_Args.cMsExecutionTime = 3*RT_MS_1MIN;
+ g_Args.numberMachines = 200;
+
+ /*
+ * Skip this test for the time being. Saw crashes on several test boxes but no time
+ * to debug.
+ */
+ if (argc == 1)
+ return RTTestSkipAndDestroy(g_hTest, "Test crashes sometimes.\n");
+
+ rc = ParseArguments(argc, argv, &g_Args);
+ if (RT_FAILURE(rc))
+ return RTTestSkipAndDestroy(g_hTest, "Invalid arguments.\n");
+
+ RTPrintf("Arguments packSize = %d, percentUnlok = %d, time = %lld.\n",
+ g_Args.machinesPackSize, g_Args.percentsUnlok, g_Args.cMsExecutionTime);
+
+ RTTHREAD hThread;
+ rc = RTThreadCreate(&hThread, tstThreadRun, (void *)&g_Args,
+ 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "tstThreadRun");
+ if (RT_SUCCESS(rc))
+ {
+ AssertRC(rc);
+
+ uint64_t msStart = RTTimeMilliTS();
+ while (RTTimeMilliTS() - msStart < g_Args.cMsExecutionTime && g_RunTest)
+ {
+ // check that test thread didn't hang and call us periodically
+ // allowed 30 seconds for operation - msStart or stop VM
+ rc = RTSemEventWait(g_PingEevent, 3 * 60 * 1000);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_TIMEOUT)
+ {
+ RTTestFailed(g_hTest, "Timeout. Deadlock?\n");
+ com::Shutdown();
+ return RTTestSummaryAndDestroy(g_hTest);
+ }
+ AssertRC(rc);
+ }
+ }
+
+ RTPrintf("Finishing...\n");
+
+ // finish test thread
+ g_RunTest = false;
+ // wait it for finish
+ RTThreadWait(hThread, RT_INDEFINITE_WAIT, &rc);
+ }
+ RTSemEventDestroy(g_PingEevent);
+
+ com::Shutdown();
+ if (RT_FAILURE(rc))
+ RTTestFailed(g_hTest, "Test failed.\n");
+ else
+ RTTestPassed(g_hTest, "Test finished.\n");
+ return RTTestSummaryAndDestroy(g_hTest);
+#endif /* RT_ARCH_AMD64 */
+}
+