summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/ValidationKit/tests
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/ValidationKit/tests')
-rw-r--r--src/VBox/ValidationKit/tests/Makefile.kmk49
-rw-r--r--src/VBox/ValidationKit/tests/__init__.py30
-rw-r--r--src/VBox/ValidationKit/tests/additions/Makefile.kmk43
-rwxr-xr-xsrc/VBox/ValidationKit/tests/additions/tdAddBasic1.py340
-rwxr-xr-xsrc/VBox/ValidationKit/tests/additions/tdAddGuestCtrl.py3753
-rw-r--r--src/VBox/ValidationKit/tests/api/Makefile.kmk50
-rw-r--r--src/VBox/ValidationKit/tests/api/__init__.py30
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdApi1.py88
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ovabin0 -> 9216 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ovabin0 -> 77312 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ovabin0 -> 77312 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ovabin0 -> 9728 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ovabin0 -> 9728 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ovabin0 -> 116224 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ovabin0 -> 116224 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem74
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ovabin0 -> 112640 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ovabin0 -> 112640 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ovabin0 -> 218624 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ovabin0 -> 218624 bytes
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem134
-rw-r--r--src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ovabin0 -> 78336 bytes
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdAppliance1.py206
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdMoveMedium1.py209
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdMoveVM1.py734
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdPython1.py210
-rwxr-xr-xsrc/VBox/ValidationKit/tests/api/tdTreeDepth1.py151
-rw-r--r--src/VBox/ValidationKit/tests/audio/Makefile.kup0
-rwxr-xr-xsrc/VBox/ValidationKit/tests/audio/tdGuestHostTimings.py230
-rw-r--r--src/VBox/ValidationKit/tests/autostart/Makefile.kmk41
-rwxr-xr-xsrc/VBox/ValidationKit/tests/autostart/tdAutostart1.py699
-rw-r--r--src/VBox/ValidationKit/tests/benchmarks/Makefile.kmk41
-rwxr-xr-xsrc/VBox/ValidationKit/tests/benchmarks/tdBenchmark1.py112
-rw-r--r--src/VBox/ValidationKit/tests/cpu/Makefile.kmk41
-rwxr-xr-xsrc/VBox/ValidationKit/tests/cpu/tdCpuPae1.py254
-rw-r--r--src/VBox/ValidationKit/tests/installation/Makefile.kmk41
-rwxr-xr-xsrc/VBox/ValidationKit/tests/installation/tdGuestOsInstOs2.py248
-rwxr-xr-xsrc/VBox/ValidationKit/tests/installation/tdGuestOsInstTest1.py429
-rw-r--r--src/VBox/ValidationKit/tests/network/Makefile.kmk41
-rwxr-xr-xsrc/VBox/ValidationKit/tests/network/tdNetBenchmark1.py623
-rw-r--r--src/VBox/ValidationKit/tests/selftests/Makefile.kmk44
-rwxr-xr-xsrc/VBox/ValidationKit/tests/selftests/tdSelfTest1.py47
-rwxr-xr-xsrc/VBox/ValidationKit/tests/selftests/tdSelfTest2.py121
-rwxr-xr-xsrc/VBox/ValidationKit/tests/selftests/tdSelfTest3.py115
-rwxr-xr-xsrc/VBox/ValidationKit/tests/selftests/tdSelfTest4.py115
-rw-r--r--src/VBox/ValidationKit/tests/serial/Makefile.kmk42
-rwxr-xr-xsrc/VBox/ValidationKit/tests/serial/loopback.py247
-rwxr-xr-xsrc/VBox/ValidationKit/tests/serial/tdSerial1.py337
-rwxr-xr-xsrc/VBox/ValidationKit/tests/shutdown/tdGuestOsShutdown1.py357
-rw-r--r--src/VBox/ValidationKit/tests/smoketests/Makefile.kmk42
-rwxr-xr-xsrc/VBox/ValidationKit/tests/smoketests/tdExoticOrAncient1.py113
-rwxr-xr-xsrc/VBox/ValidationKit/tests/smoketests/tdSmokeTest1.py163
-rw-r--r--src/VBox/ValidationKit/tests/storage/Makefile.kmk45
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/remoteexecutor.py277
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/storagecfg.py608
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py1350
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/tdStorageSnapshotMerging1.py423
-rwxr-xr-xsrc/VBox/ValidationKit/tests/storage/tdStorageStress1.py517
-rw-r--r--src/VBox/ValidationKit/tests/teleportation/Makefile.kmk41
-rwxr-xr-xsrc/VBox/ValidationKit/tests/teleportation/tdTeleportLocal1.py953
-rw-r--r--src/VBox/ValidationKit/tests/unittests/Makefile.kmk41
-rwxr-xr-xsrc/VBox/ValidationKit/tests/unittests/tdUnitTest1.py774
-rw-r--r--src/VBox/ValidationKit/tests/usb/Makefile.kmk43
-rwxr-xr-xsrc/VBox/ValidationKit/tests/usb/tdUsb1.py580
-rwxr-xr-xsrc/VBox/ValidationKit/tests/usb/tst-utsgadget.py144
-rwxr-xr-xsrc/VBox/ValidationKit/tests/usb/usbgadget.py1470
66 files changed, 17910 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/tests/Makefile.kmk b/src/VBox/ValidationKit/tests/Makefile.kmk
new file mode 100644
index 00000000..f7b4a584
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/Makefile.kmk
@@ -0,0 +1,49 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# Include sub-makefiles.
+#
+include $(PATH_SUB_CURRENT)/additions/Makefile.kmk
+include $(PATH_SUB_CURRENT)/api/Makefile.kmk
+include $(PATH_SUB_CURRENT)/autostart/Makefile.kmk
+include $(PATH_SUB_CURRENT)/benchmarks/Makefile.kmk
+include $(PATH_SUB_CURRENT)/cpu/Makefile.kmk
+include $(PATH_SUB_CURRENT)/network/Makefile.kmk
+include $(PATH_SUB_CURRENT)/selftests/Makefile.kmk
+include $(PATH_SUB_CURRENT)/smoketests/Makefile.kmk
+include $(PATH_SUB_CURRENT)/storage/Makefile.kmk
+include $(PATH_SUB_CURRENT)/teleportation/Makefile.kmk
+include $(PATH_SUB_CURRENT)/unittests/Makefile.kmk
+include $(PATH_SUB_CURRENT)/installation/Makefile.kmk
+include $(PATH_SUB_CURRENT)/usb/Makefile.kmk
+include $(PATH_SUB_CURRENT)/serial/Makefile.kmk
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/ValidationKit/tests/__init__.py b/src/VBox/ValidationKit/tests/__init__.py
new file mode 100644
index 00000000..c981b648
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/__init__.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Just to make python 2.x happy.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
diff --git a/src/VBox/ValidationKit/tests/additions/Makefile.kmk b/src/VBox/ValidationKit/tests/additions/Makefile.kmk
new file mode 100644
index 00000000..36c309c4
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/additions/Makefile.kmk
@@ -0,0 +1,43 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Additions Tests.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsAdditions
+ValidationKitTestsAdditions_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsAdditions_INST = $(INST_VALIDATIONKIT)tests/additions/
+ValidationKitTestsAdditions_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdAddBasic1.py \
+ $(PATH_SUB_CURRENT)/tdAddGuestCtrl.py
+
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsAdditions_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/additions/tdAddBasic1.py b/src/VBox/ValidationKit/tests/additions/tdAddBasic1.py
new file mode 100755
index 00000000..b23ed97e
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/additions/tdAddBasic1.py
@@ -0,0 +1,340 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdAddBasic1.py $
+
+"""
+VirtualBox Validation Kit - Additions Basics #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+# Sub test driver imports.
+sys.path.append(os.path.dirname(os.path.abspath(__file__))); # For sub-test drivers.
+from tdAddGuestCtrl import SubTstDrvAddGuestCtrl;
+
+
+class tdAddBasic1(vbox.TestDriver): # pylint: disable=R0902
+ """
+ Additions Basics #1.
+ """
+ ## @todo
+ # - More of the settings stuff can be and need to be generalized!
+ #
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
+ self.asTestsDef = ['guestprops', 'stdguestprops', 'guestcontrol'];
+ self.asTests = self.asTestsDef;
+ self.asRsrcs = None
+
+ self.addSubTestDriver(SubTstDrvAddGuestCtrl(self));
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdAddBasic1 Options:');
+ reporter.log(' --tests <s1[:s2[:]]>');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
+ reporter.log(' --quick');
+ reporter.log(' Same as --virt-modes hwvirt --cpu-counts 1.');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ if asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of tests');
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ raise base.InvalidOption('The "--tests" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestsDef)));
+
+ elif asArgs[iArg] == '--quick':
+ self.parseOption(['--virt-modes', 'hwvirt'], 0);
+ self.parseOption(['--cpu-counts', '1'], 0);
+
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def getResourceSet(self):
+ if self.asRsrcs is None:
+ self.asRsrcs = []
+ for oSubTstDrv in self.aoSubTstDrvs:
+ self.asRsrcs.extend(oSubTstDrv.asRsrcs)
+ self.asRsrcs.extend(self.oTestVmSet.getResourceSet())
+ return self.asRsrcs
+
+ def actionConfig(self):
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+ sGaIso = self.getGuestAdditionsIso();
+ return self.oTestVmSet.actionConfig(self, eNic0AttachType = eNic0AttachType, sDvdImage = sGaIso);
+
+ def actionExecute(self):
+ return self.oTestVmSet.actionExecute(self, self.testOneCfg);
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def testOneCfg(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru the tests.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ fRc = False;
+
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = True, \
+ sFileCdWait = 'AUTORUN.INF');
+ if oSession is not None:
+ self.addTask(oTxsSession);
+ # Do the testing.
+ reporter.testStart('Install');
+ fRc, oTxsSession = self.testInstallAdditions(oSession, oTxsSession, oTestVm);
+ reporter.testDone();
+ fSkip = not fRc;
+
+ reporter.testStart('Guest Properties');
+ if not fSkip:
+ fRc = self.testGuestProperties(oSession, oTxsSession, oTestVm) and fRc;
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Guest Control');
+ if not fSkip:
+ (fRc2, oTxsSession) = self.aoSubTstDrvs[0].testIt(oTestVm, oSession, oTxsSession);
+ fRc = fRc2 and fRc;
+ reporter.testDone(fSkip);
+
+ ## @todo Save and restore test.
+
+ ## @todo Reset tests.
+
+ ## @todo Final test: Uninstallation.
+
+ # Cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+ return fRc;
+
+ def testInstallAdditions(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests installing the guest additions
+ """
+ if oTestVm.isWindows():
+ (fRc, oTxsSession) = self.testWindowsInstallAdditions(oSession, oTxsSession, oTestVm);
+ else:
+ reporter.error('Guest Additions installation not implemented for %s yet! (%s)' % \
+ (oTestVm.sKind, oTestVm.sVmName));
+ fRc = False;
+
+ #
+ # Verify installation of Guest Additions using commmon bits.
+ #
+ if fRc is True:
+ #
+ # Wait for the GAs to come up.
+ #
+
+ ## @todo need to signed up for a OnAdditionsStateChanged and wait runlevel to
+ # at least reach Userland.
+
+ #
+ # Check if the additions are operational.
+ #
+ try: oGuest = oSession.o.console.guest;
+ except:
+ reporter.errorXcpt('Getting IGuest failed.');
+ return (False, oTxsSession);
+
+ # Check the additionsVersion attribute. It must not be empty.
+ reporter.testStart('IGuest::additionsVersion');
+ fRc = self.testIGuest_additionsVersion(oGuest);
+ reporter.testDone();
+
+ reporter.testStart('IGuest::additionsRunLevel');
+ self.testIGuest_additionsRunLevel(oGuest, oTestVm);
+ reporter.testDone();
+
+ ## @todo test IAdditionsFacilities.
+
+ return (fRc, oTxsSession);
+
+ def testWindowsInstallAdditions(self, oSession, oTxsSession, oTestVm):
+ """
+ Installs the Windows guest additions using the test execution service.
+ Since this involves rebooting the guest, we will have to create a new TXS session.
+ """
+ asLogFile = [];
+
+ fHaveSetupApiDevLog = False;
+
+ # Delete relevant log files.
+ if oTestVm.sKind in ('WindowsNT4',):
+ sWinDir = 'C:/WinNT/';
+ else:
+ sWinDir = 'C:/Windows/';
+ asLogFile = [sWinDir+'setupapi.log', sWinDir+'setupact.log', sWinDir+'setuperr.log'];
+
+ # Apply The SetupAPI logging level so that we also get the (most verbose) setupapi.dev.log file.
+ ## @todo !!! HACK ALERT !!! Add the value directly into the testing source image. Later.
+ fHaveSetupApiDevLog = self.txsRunTest(oTxsSession, 'Enabling setupapi.dev.log', 30 * 1000, \
+ 'c:\\Windows\\System32\\reg.exe', ('c:\\Windows\\System32\\reg.exe', \
+ 'add', '"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Setup"', '/v', 'LogLevel', '/t', 'REG_DWORD', \
+ '/d', '0xFF'));
+
+ # On some guests the files in question still can be locked by the OS, so ignore deletion
+ # errors from the guest side (e.g. sharing violations) and just continue.
+ for sFile in asLogFile:
+ self.txsRmFile(oSession, oTxsSession, sFile, 10 * 1000, fIgnoreErrors = True);
+
+ # Install the public signing key.
+ if oTestVm.sKind not in ('WindowsNT4', 'Windows2000', 'WindowsXP', 'Windows2003'):
+ ## TODO
+ pass;
+
+ #
+ # The actual install.
+ # Enable installing the optional auto-logon modules (VBoxGINA/VBoxCredProv) + (Direct)3D support.
+ # Also tell the installer to produce the appropriate log files.
+ #
+ fRc = self.txsRunTest(oTxsSession, 'VBoxWindowsAdditions.exe', 5 * 60 * 1000, \
+ '${CDROM}/VBoxWindowsAdditions.exe', ('${CDROM}/VBoxWindowsAdditions.exe', '/S', '/l', '/with_autologon'));
+ ## @todo For testing the installation (D)3D stuff ('/with_d3d') we need to boot up in safe mode.
+
+ #
+ # Reboot the VM and reconnect the TXS session.
+ #
+ if fRc is True:
+ (fRc, oTxsSession) = self.txsRebootAndReconnectViaTcp(oSession, oTxsSession, cMsTimeout = 3 * 60000);
+
+ if fRc is True:
+ # Add the Windows Guest Additions installer files to the files we want to download
+ # from the guest.
+ sGuestAddsDir = 'C:/Program Files/Oracle/VirtualBox Guest Additions/';
+ asLogFile.append(sGuestAddsDir + 'install.log');
+ # Note: There won't be a install_ui.log because of the silent installation.
+ asLogFile.append(sGuestAddsDir + 'install_drivers.log');
+ asLogFile.append('C:/Windows/setupapi.log');
+
+ # Note: setupapi.dev.log only is available since Windows 2000.
+ if fHaveSetupApiDevLog:
+ asLogFile.append('C:/Windows/setupapi.dev.log');
+
+ #
+ # Download log files.
+ # Ignore errors as all files above might not be present (or in different locations)
+ # on different Windows guests.
+ #
+ self.txsDownloadFiles(oSession, oTxsSession, asLogFile, fIgnoreErrors = True);
+
+ return (fRc, oTxsSession);
+
+ def testIGuest_additionsVersion(self, oGuest):
+ """
+ Returns False if no version string could be obtained, otherwise True
+ even though errors are logged.
+ """
+ try:
+ sVer = oGuest.additionsVersion;
+ except:
+ reporter.errorXcpt('Getting the additions version failed.');
+ return False;
+ reporter.log('IGuest::additionsVersion="%s"' % (sVer,));
+
+ if sVer.strip() == '':
+ reporter.error('IGuest::additionsVersion is empty.');
+ return False;
+
+ if sVer != sVer.strip():
+ reporter.error('IGuest::additionsVersion is contains spaces: "%s".' % (sVer,));
+
+ asBits = sVer.split('.');
+ if len(asBits) < 3:
+ reporter.error('IGuest::additionsVersion does not contain at least tree dot separated fields: "%s" (%d).'
+ % (sVer, len(asBits)));
+
+ ## @todo verify the format.
+ return True;
+
+ def testIGuest_additionsRunLevel(self, oGuest, oTestVm):
+ """
+ Do run level tests.
+ """
+ if oTestVm.isLoggedOntoDesktop():
+ eExpectedRunLevel = vboxcon.AdditionsRunLevelType_Desktop;
+ else:
+ eExpectedRunLevel = vboxcon.AdditionsRunLevelType_Userland;
+
+ ## @todo Insert wait for the desired run level.
+ try:
+ iLevel = oGuest.additionsRunLevel;
+ except:
+ reporter.errorXcpt('Getting the additions run level failed.');
+ return False;
+ reporter.log('IGuest::additionsRunLevel=%s' % (iLevel,));
+
+ if iLevel != eExpectedRunLevel:
+ pass; ## @todo We really need that wait!!
+ #reporter.error('Expected runlevel %d, found %d instead' % (eExpectedRunLevel, iLevel));
+ return True;
+
+
+ def testGuestProperties(self, oSession, oTxsSession, oTestVm):
+ """
+ Test guest properties.
+ """
+ _ = oSession; _ = oTxsSession; _ = oTestVm;
+ return True;
+
+if __name__ == '__main__':
+ sys.exit(tdAddBasic1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/additions/tdAddGuestCtrl.py b/src/VBox/ValidationKit/tests/additions/tdAddGuestCtrl.py
new file mode 100755
index 00000000..7f27e7da
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/additions/tdAddGuestCtrl.py
@@ -0,0 +1,3753 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# pylint: disable=C0302
+
+"""
+VirtualBox Validation Kit - Guest Control Tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+# Disable bitching about too many arguments per function.
+# pylint: disable=R0913
+
+# Disable bitching about semicolons at the end of lines.
+# pylint: disable=W0301
+
+## @todo Convert map() usage to a cleaner alternative Python now offers.
+# pylint: disable=W0141
+
+## @todo Convert the context/test classes into named tuples. Not in the mood right now, so
+# disabling it.
+# pylint: disable=R0903
+
+# Standard Python imports.
+from array import array
+import errno
+import os
+import random
+import string # pylint: disable=W0402
+import struct
+import sys
+import time
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+from testdriver import vboxwrappers;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int # pylint: disable=W0622,C0103
+
+
+class GuestStream(bytearray):
+ """
+ Class for handling a guest process input/output stream.
+ """
+ def appendStream(self, stream, convertTo='<b'):
+ """
+ Appends and converts a byte sequence to this object;
+ handy for displaying a guest stream.
+ """
+ self.extend(struct.pack(convertTo, stream));
+
+class tdCtxTest(object):
+ """
+ Provides the actual test environment. Should be kept
+ as generic as possible.
+ """
+ def __init__(self, oSession, oTxsSession, oTestVm): # pylint: disable=W0613
+ ## The desired Main API result.
+ self.fRc = False;
+ ## IGuest reference.
+ self.oGuest = oSession.o.console.guest;
+ # Rest not used (yet).
+
+class tdCtxCreds(object):
+ """
+ Provides credentials to pass to the guest.
+ """
+ def __init__(self, sUser = None, sPassword = None, sDomain = None, oTestVm = None):
+ # If no user is specified, select the default user and
+ # password for the given test VM.
+ if sUser is None:
+ assert sPassword is None;
+ assert sDomain is None;
+ assert oTestVm is not None;
+
+ ## @todo fix this so all VMs have several usable test users with the same passwords (or none).
+ sUser = 'test';
+ sPassword = 'password';
+ if oTestVm.isWindows():
+ #sPassword = ''; # stupid config mistake.
+ sPassword = 'password';
+ sUser = 'Administrator';
+ sDomain = '';
+
+ self.sUser = sUser;
+ self.sPassword = sPassword if sPassword is not None else '';
+ self.sDomain = sDomain if sDomain is not None else '';
+
+class tdTestGuestCtrlBase(object):
+ """
+ Base class for all guest control tests.
+ Note: This test ASSUMES that working Guest Additions
+ were installed and running on the guest to be tested.
+ """
+ def __init__(self):
+ self.oTest = None;
+ self.oCreds = None;
+ self.timeoutMS = 30 * 1000; # 30s timeout
+ ## IGuestSession reference or None.
+ self.oGuestSession = None;
+
+ def setEnvironment(self, oSession, oTxsSession, oTestVm):
+ """
+ Sets the test environment required for this test.
+ """
+ self.oTest = tdCtxTest(oSession, oTxsSession, oTestVm);
+ return self.oTest;
+
+ def createSession(self, sName):
+ """
+ Creates (opens) a guest session.
+ Returns (True, IGuestSession) on success or (False, None) on failure.
+ """
+ if self.oGuestSession is None:
+ if sName is None:
+ sName = "<untitled>";
+ try:
+ reporter.log('Creating session "%s" ...' % (sName,));
+ self.oGuestSession = self.oTest.oGuest.createSession(self.oCreds.sUser,
+ self.oCreds.sPassword,
+ self.oCreds.sDomain,
+ sName);
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('Creating a guest session "%s" failed; sUser="%s", pw="%s", sDomain="%s":'
+ % (sName, self.oCreds.sUser, self.oCreds.sPassword, self.oCreds.sDomain));
+ return (False, None);
+
+ try:
+ reporter.log('Waiting for session "%s" to start within %dms...' % (sName, self.timeoutMS));
+ fWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start ];
+ waitResult = self.oGuestSession.waitForArray(fWaitFor, self.timeoutMS);
+ #
+ # Be nice to Guest Additions < 4.3: They don't support session handling and
+ # therefore return WaitFlagNotSupported.
+ #
+ if waitResult != vboxcon.GuestSessionWaitResult_Start \
+ and waitResult != vboxcon.GuestSessionWaitResult_WaitFlagNotSupported:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.log('Session did not start successfully, returned wait result: %d' \
+ % (waitResult,));
+ return (False, None);
+ reporter.log('Session "%s" successfully started' % (sName,));
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('Waiting for guest session "%s" (usr=%s;pw=%s;dom=%s) to start failed:'
+ % (sName, self.oCreds.sUser, self.oCreds.sPassword, self.oCreds.sDomain,));
+ return (False, None);
+ else:
+ reporter.log('Warning: Session already set; this is probably not what you want');
+ return (True, self.oGuestSession);
+
+ def setSession(self, oGuestSession):
+ """
+ Sets the current guest session and closes
+ an old one if necessary.
+ """
+ if self.oGuestSession is not None:
+ self.closeSession();
+ self.oGuestSession = oGuestSession;
+ return self.oGuestSession;
+
+ def closeSession(self):
+ """
+ Closes the guest session.
+ """
+ if self.oGuestSession is not None:
+ sName = self.oGuestSession.name;
+ try:
+ reporter.log('Closing session "%s" ...' % (sName,));
+ self.oGuestSession.close();
+ self.oGuestSession = None;
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('Closing guest session "%s" failed:' % (sName,));
+ return False;
+ return True;
+
+class tdTestCopyFrom(tdTestGuestCtrlBase):
+ """
+ Test for copying files from the guest to the host.
+ """
+ def __init__(self, sSrc = "", sDst = "", sUser = "", sPassword = "", aFlags = None):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sSrc = sSrc;
+ self.sDst = sDst;
+ self.aFlags = aFlags;
+
+class tdTestCopyTo(tdTestGuestCtrlBase):
+ """
+ Test for copying files from the host to the guest.
+ """
+ def __init__(self, sSrc = "", sDst = "", sUser = "", sPassword = "", aFlags = None):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sSrc = sSrc;
+ self.sDst = sDst;
+ self.aFlags = aFlags;
+
+class tdTestDirCreate(tdTestGuestCtrlBase):
+ """
+ Test for directoryCreate call.
+ """
+ def __init__(self, sDirectory = "", sUser = "", sPassword = "", fMode = 0, aFlags = None):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sDirectory = sDirectory;
+ self.fMode = fMode;
+ self.aFlags = aFlags;
+
+class tdTestDirCreateTemp(tdTestGuestCtrlBase):
+ """
+ Test for the directoryCreateTemp call.
+ """
+ def __init__(self, sDirectory = "", sTemplate = "", sUser = "", sPassword = "", fMode = 0, fSecure = False):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sDirectory = sDirectory;
+ self.sTemplate = sTemplate;
+ self.fMode = fMode;
+ self.fSecure = fSecure;
+
+class tdTestDirOpen(tdTestGuestCtrlBase):
+ """
+ Test for the directoryOpen call.
+ """
+ def __init__(self, sDirectory = "", sUser = "", sPassword = "",
+ sFilter = "", aFlags = None):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sDirectory = sDirectory;
+ self.sFilter = sFilter;
+ self.aFlags = aFlags or [];
+
+class tdTestDirRead(tdTestDirOpen):
+ """
+ Test for the opening, reading and closing a certain directory.
+ """
+ def __init__(self, sDirectory = "", sUser = "", sPassword = "",
+ sFilter = "", aFlags = None):
+ tdTestDirOpen.__init__(self, sDirectory, sUser, sPassword, sFilter, aFlags);
+
+class tdTestExec(tdTestGuestCtrlBase):
+ """
+ Specifies exactly one guest control execution test.
+ Has a default timeout of 5 minutes (for safety).
+ """
+ def __init__(self, sCmd = "", aArgs = None, aEnv = None, \
+ aFlags = None, timeoutMS = 5 * 60 * 1000, \
+ sUser = "", sPassword = "", sDomain = "", \
+ fWaitForExit = True):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain);
+ self.sCmd = sCmd;
+ self.aArgs = aArgs if aArgs is not None else [sCmd,];
+ self.aEnv = aEnv;
+ self.aFlags = aFlags or [];
+ self.timeoutMS = timeoutMS;
+ self.fWaitForExit = fWaitForExit;
+ self.uExitStatus = 0;
+ self.iExitCode = 0;
+ self.cbStdOut = 0;
+ self.cbStdErr = 0;
+ self.sBuf = '';
+
+class tdTestFileExists(tdTestGuestCtrlBase):
+ """
+ Test for the file exists API call (fileExists).
+ """
+ def __init__(self, sFile = "", sUser = "", sPassword = ""):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sFile = sFile;
+
+class tdTestFileRemove(tdTestGuestCtrlBase):
+ """
+ Test querying guest file information.
+ """
+ def __init__(self, sFile = "", sUser = "", sPassword = ""):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sFile = sFile;
+
+class tdTestFileStat(tdTestGuestCtrlBase):
+ """
+ Test querying guest file information.
+ """
+ def __init__(self, sFile = "", sUser = "", sPassword = "", cbSize = 0, eFileType = 0):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sFile = sFile;
+ self.cbSize = cbSize;
+ self.eFileType = eFileType;
+
+class tdTestFileIO(tdTestGuestCtrlBase):
+ """
+ Test for the IGuestFile object.
+ """
+ def __init__(self, sFile = "", sUser = "", sPassword = ""):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sFile = sFile;
+
+class tdTestFileQuerySize(tdTestGuestCtrlBase):
+ """
+ Test for the file size query API call (fileQuerySize).
+ """
+ def __init__(self, sFile = "", sUser = "", sPassword = ""):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sFile = sFile;
+
+class tdTestFileReadWrite(tdTestGuestCtrlBase):
+ """
+ Tests reading from guest files.
+ """
+ def __init__(self, sFile = "", sUser = "", sPassword = "",
+ sOpenMode = "r", sDisposition = "",
+ sSharingMode = "",
+ lCreationMode = 0, cbOffset = 0, cbToReadWrite = 0,
+ aBuf = None):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain = "");
+ self.sFile = sFile;
+ self.sOpenMode = sOpenMode;
+ self.sDisposition = sDisposition;
+ self.sSharingMode = sSharingMode;
+ self.lCreationMode = lCreationMode;
+ self.cbOffset = cbOffset;
+ self.cbToReadWrite = cbToReadWrite;
+ self.aBuf = aBuf;
+
+ def getOpenAction(self):
+ """ Converts string disposition to open action enum. """
+ if self.sDisposition == 'oe': return vboxcon.FileOpenAction_OpenExisting;
+ if self.sDisposition == 'oc': return vboxcon.FileOpenAction_OpenOrCreate;
+ if self.sDisposition == 'ce': return vboxcon.FileOpenAction_CreateNew;
+ if self.sDisposition == 'ca': return vboxcon.FileOpenAction_CreateOrReplace;
+ if self.sDisposition == 'ot': return vboxcon.FileOpenAction_OpenExistingTruncated;
+ if self.sDisposition == 'oa': return vboxcon.FileOpenAction_AppendOrCreate;
+ raise base.GenError(self.sDisposition);
+
+ def getAccessMode(self):
+ """ Converts open mode to access mode enum. """
+ if self.sOpenMode == 'r': return vboxcon.FileOpenMode_ReadOnly;
+ if self.sOpenMode == 'w': return vboxcon.FileOpenMode_WriteOnly;
+ if self.sOpenMode == 'w+': return vboxcon.FileOpenMode_ReadWrite;
+ if self.sOpenMode == 'r+': return vboxcon.FileOpenMode_ReadWrite;
+ raise base.GenError(self.sOpenMode);
+
+ def getSharingMode(self):
+ """ Converts the sharing mode. """
+ return vboxcon.FileSharingMode_All;
+
+class tdTestSession(tdTestGuestCtrlBase):
+ """
+ Test the guest session handling.
+ """
+ def __init__(self, sUser = "", sPassword = "", sDomain = "", \
+ sSessionName = ""):
+ tdTestGuestCtrlBase.__init__(self);
+ self.sSessionName = sSessionName;
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain);
+
+ def getSessionCount(self, oVBoxMgr):
+ """
+ Helper for returning the number of currently
+ opened guest sessions of a VM.
+ """
+ if self.oTest.oGuest is None:
+ return 0;
+ aoSession = oVBoxMgr.getArray(self.oTest.oGuest, 'sessions')
+ return len(aoSession);
+
+class tdTestSessionEx(tdTestGuestCtrlBase):
+ """
+ Test the guest session.
+ """
+ def __init__(self, aoSteps = None, enmUser = None):
+ tdTestGuestCtrlBase.__init__(self);
+ assert enmUser is None; # For later.
+ self.enmUser = enmUser;
+ self.aoSteps = aoSteps if aoSteps is not None else [];
+
+ def execute(self, oTstDrv, oVmSession, oTxsSession, oTestVm, sMsgPrefix):
+ """
+ Executes the test.
+ """
+ #
+ # Create a session.
+ #
+ assert self.enmUser is None; # For later.
+ self.oCreds = tdCtxCreds(oTestVm = oTestVm);
+ self.setEnvironment(oVmSession, oTxsSession, oTestVm);
+ reporter.log2('%s: %s steps' % (sMsgPrefix, len(self.aoSteps),));
+ fRc, oCurSession = self.createSession(sMsgPrefix);
+ if fRc is True:
+ #
+ # Execute the tests.
+ #
+ try:
+ fRc = self.executeSteps(oTstDrv, oCurSession, sMsgPrefix);
+ except:
+ reporter.errorXcpt('%s: Unexpected exception executing test steps' % (sMsgPrefix,));
+ fRc = False;
+
+ fRc2 = self.closeSession();
+ if fRc2 is False:
+ reporter.error('%s: Session could not be closed' % (sMsgPrefix,));
+ fRc = False;
+ else:
+ reporter.error('%s: Session creation failed' % (sMsgPrefix,));
+ fRc = False;
+ return fRc;
+
+ def executeSteps(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes just the steps.
+ Returns True on success, False on test failure.
+ """
+ fRc = True;
+ for (i, oStep) in enumerate(self.aoSteps):
+ fRc2 = oStep.execute(oTstDrv, oGstCtrlSession, sMsgPrefix + ', step #%d' % i);
+ if fRc2 is True:
+ pass;
+ elif fRc2 is None:
+ reporter.log('skipping remaining %d steps' % (len(self.aoSteps) - i - 1,));
+ break;
+ else:
+ fRc = False;
+ return fRc;
+
+ @staticmethod
+ def executeListTestSessions(aoTests, oTstDrv, oVmSession, oTxsSession, oTestVm, sMsgPrefix):
+ """
+ Works thru a list of tdTestSessionEx object.
+ """
+ fRc = True;
+ for (i, oCurTest) in enumerate(aoTests):
+ try:
+ fRc2 = oCurTest.execute(oTstDrv, oVmSession, oTxsSession, oTestVm, '%s, test %#d' % (sMsgPrefix, i,));
+ if fRc2 is not True:
+ fRc = False;
+ except:
+ reporter.errorXcpt('Unexpected exception executing test #%d' % (i,));
+ fRc = False;
+
+ return (fRc, oTxsSession);
+
+
+class tdSessionStepBase(object):
+ """
+ Base class for the guest control session test steps.
+ """
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the test step.
+
+ Returns True on success.
+ Returns False on failure (must be reported as error).
+ Returns None if to skip the remaining steps.
+ """
+ reporter.error('%s: Missing execute implementation: %s' % (sMsgPrefix, self,));
+ _ = oTstDrv;
+ _ = oGstCtrlSession;
+ return False;
+
+
+class tdStepRequireMinimumApiVer(tdSessionStepBase):
+ """
+ Special test step which will cause executeSteps to skip the remaining step
+ if the VBox API is too old:
+ """
+ def __init__(self, fpMinApiVer):
+ self.fpMinApiVer = fpMinApiVer;
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """ Returns None if API version is too old, otherwise True. """
+ if oTstDrv.fpApiVer >= self.fpMinApiVer:
+ return True;
+ _ = oGstCtrlSession;
+ _ = sMsgPrefix;
+ return None; # Special return value. Don't use elsewhere.
+
+
+#
+# Scheduling Environment Changes with the Guest Control Session.
+#
+
+class tdStepSessionSetEnv(tdSessionStepBase):
+ """
+ Guest session environment: schedule putenv
+ """
+ def __init__(self, sVar, sValue, hrcExpected = 0):
+ self.sVar = sVar;
+ self.sValue = sValue;
+ self.hrcExpected = hrcExpected;
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the step.
+ Returns True on success, False on test failure.
+ """
+ reporter.log2('tdStepSessionSetEnv: sVar=%s sValue=%s hrcExpected=%#x' % (self.sVar, self.sValue, self.hrcExpected,));
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ oGstCtrlSession.environmentScheduleSet(self.sVar, self.sValue);
+ else:
+ oGstCtrlSession.environmentSet(self.sVar, self.sValue);
+ except vbox.ComException as oXcpt:
+ # Is this an expected failure?
+ if vbox.ComError.equal(oXcpt, self.hrcExpected):
+ return True;
+ reporter.errorXcpt('%s: Expected hrc=%#x (%s) got %#x (%s) instead (setenv %s=%s)'
+ % (sMsgPrefix, self.hrcExpected, vbox.ComError.toString(self.hrcExpected),
+ vbox.ComError.getXcptResult(oXcpt),
+ vbox.ComError.toString(vbox.ComError.getXcptResult(oXcpt)),
+ self.sVar, self.sValue,));
+ return False;
+ except:
+ reporter.errorXcpt('%s: Unexpected exception in tdStepSessionSetEnv::execute (%s=%s)'
+ % (sMsgPrefix, self.sVar, self.sValue,));
+ return False;
+
+ # Should we succeed?
+ if self.hrcExpected != 0:
+ reporter.error('%s: Expected hrcExpected=%#x, got S_OK (putenv %s=%s)'
+ % (sMsgPrefix, self.hrcExpected, self.sVar, self.sValue,));
+ return False;
+ return True;
+
+class tdStepSessionUnsetEnv(tdSessionStepBase):
+ """
+ Guest session environment: schedule unset.
+ """
+ def __init__(self, sVar, hrcExpected = 0):
+ self.sVar = sVar;
+ self.hrcExpected = hrcExpected;
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the step.
+ Returns True on success, False on test failure.
+ """
+ reporter.log2('tdStepSessionUnsetEnv: sVar=%s hrcExpected=%#x' % (self.sVar, self.hrcExpected,));
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ oGstCtrlSession.environmentScheduleUnset(self.sVar);
+ else:
+ oGstCtrlSession.environmentUnset(self.sVar);
+ except vbox.ComException as oXcpt:
+ # Is this an expected failure?
+ if vbox.ComError.equal(oXcpt, self.hrcExpected):
+ return True;
+ reporter.errorXcpt('%s: Expected hrc=%#x (%s) got %#x (%s) instead (unsetenv %s)'
+ % (sMsgPrefix, self.hrcExpected, vbox.ComError.toString(self.hrcExpected),
+ vbox.ComError.getXcptResult(oXcpt),
+ vbox.ComError.toString(vbox.ComError.getXcptResult(oXcpt)),
+ self.sVar,));
+ return False;
+ except:
+ reporter.errorXcpt('%s: Unexpected exception in tdStepSessionUnsetEnv::execute (%s)'
+ % (sMsgPrefix, self.sVar,));
+ return False;
+
+ # Should we succeed?
+ if self.hrcExpected != 0:
+ reporter.error('%s: Expected hrcExpected=%#x, got S_OK (unsetenv %s)'
+ % (sMsgPrefix, self.hrcExpected, self.sVar,));
+ return False;
+ return True;
+
+class tdStepSessionBulkEnv(tdSessionStepBase):
+ """
+ Guest session environment: Bulk environment changes.
+ """
+ def __init__(self, asEnv = None, hrcExpected = 0):
+ self.asEnv = asEnv if asEnv is not None else [];
+ self.hrcExpected = hrcExpected;
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the step.
+ Returns True on success, False on test failure.
+ """
+ reporter.log2('tdStepSessionBulkEnv: asEnv=%s hrcExpected=%#x' % (self.asEnv, self.hrcExpected,));
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ oTstDrv.oVBoxMgr.setArray(oGstCtrlSession, 'environmentChanges', self.asEnv);
+ else:
+ oTstDrv.oVBoxMgr.setArray(oGstCtrlSession, 'environment', self.asEnv);
+ except vbox.ComException as oXcpt:
+ # Is this an expected failure?
+ if vbox.ComError.equal(oXcpt, self.hrcExpected):
+ return True;
+ reporter.errorXcpt('%s: Expected hrc=%#x (%s) got %#x (%s) instead (asEnv=%s)'
+ % (sMsgPrefix, self.hrcExpected, vbox.ComError.toString(self.hrcExpected),
+ vbox.ComError.getXcptResult(oXcpt),
+ vbox.ComError.toString(vbox.ComError.getXcptResult(oXcpt)),
+ self.asEnv,));
+ return False;
+ except:
+ reporter.errorXcpt('%s: Unexpected exception writing the environmentChanges property (asEnv=%s).'
+ % (sMsgPrefix, self.asEnv));
+ return False;
+ return True;
+
+class tdStepSessionClearEnv(tdStepSessionBulkEnv):
+ """
+ Guest session environment: clears the scheduled environment changes.
+ """
+ def __init__(self):
+ tdStepSessionBulkEnv.__init__(self);
+
+
+class tdStepSessionCheckEnv(tdSessionStepBase):
+ """
+ Check the currently scheduled environment changes of a guest control session.
+ """
+ def __init__(self, asEnv = None):
+ self.asEnv = asEnv if asEnv is not None else [];
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Executes the step.
+ Returns True on success, False on test failure.
+ """
+ reporter.log2('tdStepSessionCheckEnv: asEnv=%s' % (self.asEnv,));
+
+ #
+ # Get the environment change list.
+ #
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ asCurEnv = oTstDrv.oVBoxMgr.getArray(oGstCtrlSession, 'environmentChanges');
+ else:
+ asCurEnv = oTstDrv.oVBoxMgr.getArray(oGstCtrlSession, 'environment');
+ except:
+ reporter.errorXcpt('%s: Unexpected exception reading the environmentChanges property.' % (sMsgPrefix,));
+ return False;
+
+ #
+ # Compare it with the expected one by trying to remove each expected value
+ # and the list anything unexpected.
+ #
+ fRc = True;
+ asCopy = list(asCurEnv); # just in case asCurEnv is immutable
+ for sExpected in self.asEnv:
+ try:
+ asCopy.remove(sExpected);
+ except:
+ reporter.error('%s: Expected "%s" to be in the resulting environment' % (sMsgPrefix, sExpected,));
+ fRc = False;
+ for sUnexpected in asCopy:
+ reporter.error('%s: Unexpected "%s" in the resulting environment' % (sMsgPrefix, sUnexpected,));
+ fRc = False;
+
+ if fRc is not True:
+ reporter.log2('%s: Current environment: %s' % (sMsgPrefix, asCurEnv));
+ return fRc;
+
+
+#
+# File system object statistics (i.e. stat()).
+#
+
+class tdStepStat(tdSessionStepBase):
+ """
+ Stats a file system object.
+ """
+ def __init__(self, sPath, hrcExpected = 0, fFound = True, fFollowLinks = True, enmType = None):
+ self.sPath = sPath;
+ self.hrcExpected = hrcExpected;
+ self.fFound = fFound;
+ self.fFollowLinks = fFollowLinks;
+ self.enmType = enmType if enmType is not None else vboxcon.FsObjType_File;
+ self.cbExactSize = None;
+ self.cbMinSize = None;
+
+ def execute(self, oTstDrv, oGstCtrlSession, sMsgPrefix):
+ """
+ Execute the test step.
+ """
+ reporter.log2('tdStepStat: sPath=%s enmType=%s hrcExpected=%s fFound=%s fFollowLinks=%s'
+ % (self.sPath, self.enmType, self.hrcExpected, self.fFound, self.fFollowLinks,));
+
+ # Don't execute non-file tests on older VBox version.
+ if oTstDrv.fpApiVer >= 5.0 or self.enmType == vboxcon.FsObjType_File or not self.fFound:
+ #
+ # Call the API.
+ #
+ try:
+ if oTstDrv.fpApiVer >= 5.0:
+ oFsInfo = oGstCtrlSession.fsObjQueryInfo(self.sPath, self.fFollowLinks);
+ else:
+ oFsInfo = oGstCtrlSession.fileQueryInfo(self.sPath);
+ except vbox.ComException as oXcpt:
+ ## @todo: The error reporting in the API just plain sucks! Most of the errors are
+ ## VBOX_E_IPRT_ERROR and there seems to be no way to distinguish between
+ ## non-existing files/path and a lot of other errors. Fix API and test!
+ if not self.fFound:
+ return True;
+ if vbox.ComError.equal(oXcpt, self.hrcExpected): # Is this an expected failure?
+ return True;
+ return reporter.errorXcpt('%s: Unexpected exception for exiting path "%s" (enmType=%s, hrcExpected=%s):'
+ % (sMsgPrefix, self.sPath, self.enmType, self.hrcExpected,));
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception in tdStepStat::execute (%s)'
+ % (sMsgPrefix, self.sPath,));
+ if oFsInfo is None:
+ return reporter.error('%s: "%s" got None instead of IFsObjInfo instance!' % (sMsgPrefix, self.sPath,));
+
+ #
+ # Check type expectations.
+ #
+ try:
+ enmType = oFsInfo.type;
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception in reading "IFsObjInfo::type"' % (sMsgPrefix,));
+ if enmType != self.enmType:
+ return reporter.error('%s: "%s" has type %s, expected %s'
+ % (sMsgPrefix, self.sPath, enmType, self.enmType));
+
+ #
+ # Check size expectations.
+ # Note! This is unicode string here on windows, for some reason.
+ # long long mapping perhaps?
+ #
+ try:
+ cbObject = long(oFsInfo.objectSize);
+ except:
+ return reporter.errorXcpt('%s: Unexpected exception in reading "IFsObjInfo::objectSize"'
+ % (sMsgPrefix,));
+ if self.cbExactSize is not None \
+ and cbObject != self.cbExactSize:
+ return reporter.error('%s: "%s" has size %s bytes, expected %s bytes'
+ % (sMsgPrefix, self.sPath, cbObject, self.cbExactSize));
+ if self.cbMinSize is not None \
+ and cbObject < self.cbMinSize:
+ return reporter.error('%s: "%s" has size %s bytes, expected as least %s bytes'
+ % (sMsgPrefix, self.sPath, cbObject, self.cbMinSize));
+ return True;
+
+class tdStepStatDir(tdStepStat):
+ """ Checks for an existing directory. """
+ def __init__(self, sDirPath):
+ tdStepStat.__init__(self, sPath = sDirPath, enmType = vboxcon.FsObjType_Directory);
+
+class tdStepStatFile(tdStepStat):
+ """ Checks for an existing file """
+ def __init__(self, sFilePath):
+ tdStepStat.__init__(self, sPath = sFilePath, enmType = vboxcon.FsObjType_File);
+
+class tdStepStatFileSize(tdStepStat):
+ """ Checks for an existing file of a given expected size.. """
+ def __init__(self, sFilePath, cbExactSize = 0):
+ tdStepStat.__init__(self, sPath = sFilePath, enmType = vboxcon.FsObjType_File);
+ self.cbExactSize = cbExactSize;
+
+class tdStepStatFileNotFound(tdStepStat):
+ """ Checks for an existing directory. """
+ def __init__(self, sPath):
+ tdStepStat.__init__(self, sPath = sPath, fFound = False);
+
+class tdStepStatPathNotFound(tdStepStat):
+ """ Checks for an existing directory. """
+ def __init__(self, sPath):
+ tdStepStat.__init__(self, sPath = sPath, fFound = False);
+
+
+#
+#
+#
+
+class tdTestSessionFileRefs(tdTestGuestCtrlBase):
+ """
+ Tests session file (IGuestFile) reference counting.
+ """
+ def __init__(self, cRefs = 0):
+ tdTestGuestCtrlBase.__init__(self);
+ self.cRefs = cRefs;
+
+class tdTestSessionDirRefs(tdTestGuestCtrlBase):
+ """
+ Tests session directory (IGuestDirectory) reference counting.
+ """
+ def __init__(self, cRefs = 0):
+ tdTestGuestCtrlBase.__init__(self);
+ self.cRefs = cRefs;
+
+class tdTestSessionProcRefs(tdTestGuestCtrlBase):
+ """
+ Tests session process (IGuestProcess) reference counting.
+ """
+ def __init__(self, cRefs = 0):
+ tdTestGuestCtrlBase.__init__(self);
+ self.cRefs = cRefs;
+
+class tdTestUpdateAdditions(tdTestGuestCtrlBase):
+ """
+ Test updating the Guest Additions inside the guest.
+ """
+ def __init__(self, sSrc = "", aArgs = None, aFlags = None,
+ sUser = "", sPassword = "", sDomain = ""):
+ tdTestGuestCtrlBase.__init__(self);
+ self.oCreds = tdCtxCreds(sUser, sPassword, sDomain);
+ self.sSrc = sSrc;
+ self.aArgs = aArgs;
+ self.aFlags = aFlags;
+
+class tdTestResult(object):
+ """
+ Base class for test results.
+ """
+ def __init__(self, fRc = False):
+ ## The overall test result.
+ self.fRc = fRc;
+
+class tdTestResultDirRead(tdTestResult):
+ """
+ Test result for reading guest directories.
+ """
+ def __init__(self, fRc = False,
+ numFiles = 0, numDirs = 0):
+ tdTestResult.__init__(self, fRc = fRc);
+ self.numFiles = numFiles;
+ self.numDirs = numDirs;
+
+class tdTestResultExec(tdTestResult):
+ """
+ Holds a guest process execution test result,
+ including the exit code, status + aFlags.
+ """
+ def __init__(self, fRc = False, \
+ uExitStatus = 500, iExitCode = 0, \
+ sBuf = None, cbBuf = 0, \
+ cbStdOut = 0, cbStdErr = 0):
+ tdTestResult.__init__(self);
+ ## The overall test result.
+ self.fRc = fRc;
+ ## Process exit stuff.
+ self.uExitStatus = uExitStatus;
+ self.iExitCode = iExitCode;
+ ## Desired buffer length returned back from stdout/stderr.
+ self.cbBuf = cbBuf;
+ ## Desired buffer result from stdout/stderr. Use with caution!
+ self.sBuf = sBuf;
+ self.cbStdOut = cbStdOut;
+ self.cbStdErr = cbStdErr;
+
+class tdTestResultFileStat(tdTestResult):
+ """
+ Test result for stat'ing guest files.
+ """
+ def __init__(self, fRc = False,
+ cbSize = 0, eFileType = 0):
+ tdTestResult.__init__(self, fRc = fRc);
+ self.cbSize = cbSize;
+ self.eFileType = eFileType;
+ ## @todo Add more information.
+
+class tdTestResultFileReadWrite(tdTestResult):
+ """
+ Test result for reading + writing guest directories.
+ """
+ def __init__(self, fRc = False,
+ cbProcessed = 0, cbOffset = 0, aBuf = None):
+ tdTestResult.__init__(self, fRc = fRc);
+ self.cbProcessed = cbProcessed;
+ self.cbOffset = cbOffset;
+ self.aBuf = aBuf;
+
+class tdTestResultSession(tdTestResult):
+ """
+ Test result for guest session counts.
+ """
+ def __init__(self, fRc = False, cNumSessions = 0):
+ tdTestResult.__init__(self, fRc = fRc);
+ self.cNumSessions = cNumSessions;
+
+class SubTstDrvAddGuestCtrl(base.SubTestDriverBase):
+ """
+ Sub-test driver for executing guest control (VBoxService, IGuest) tests.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, 'add-guest-ctrl', oTstDrv);
+
+ ## @todo base.TestBase.
+ self.asTestsDef = \
+ [
+ 'session_basic', 'session_env', 'session_file_ref', 'session_dir_ref', 'session_proc_ref',
+ 'exec_basic', 'exec_errorlevel', 'exec_timeout',
+ 'dir_create', 'dir_create_temp', 'dir_read',
+ 'file_remove', 'file_stat', 'file_read', 'file_write',
+ 'copy_to', 'copy_from',
+ 'update_additions'
+ ];
+ self.asTests = self.asTestsDef;
+ self.asRsrcs = ['5.3/guestctrl/50mb_rnd.dat', ];
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ if asArgs[iArg] == '--add-guest-ctrl-tests':
+ iArg += 1;
+ if asArgs[iArg] == 'all': # Nice for debugging scripts.
+ self.asTests = self.asTestsDef;
+ return iArg + 1;
+
+ iNext = self.oTstDrv.requireMoreArgs(1, asArgs, iArg);
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ raise base.InvalidOption('The "--add-guest-ctrl-tests" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestsDef)));
+ return iNext;
+ return iArg;
+
+ def showUsage(self):
+ base.SubTestDriverBase.showUsage(self);
+ reporter.log(' --add-guest-ctrl-tests <s1[:s2[:]]>');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
+ return True;
+
+ def testIt(self, oTestVm, oSession, oTxsSession):
+ """
+ Executes the test.
+
+ Returns fRc, oTxsSession. The latter may have changed.
+ """
+ reporter.log("Active tests: %s" % (self.asTests,));
+
+ fRc = True;
+
+ # Do the testing.
+ reporter.testStart('Session Basics');
+ fSkip = 'session_basic' not in self.asTests;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlSession(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Session Environment');
+ fSkip = 'session_env' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlSessionEnvironment(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Session File References');
+ fSkip = 'session_file_ref' not in self.asTests;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlSessionFileRefs(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ ## @todo Implement this.
+ #reporter.testStart('Session Directory References');
+ #fSkip = 'session_dir_ref' not in self.asTests;
+ #if fSkip is False:
+ # fRc, oTxsSession = self.testGuestCtrlSessionDirRefs(oSession, oTxsSession, oTestVm);
+ #reporter.testDone(fSkip);
+
+ reporter.testStart('Session Process References');
+ fSkip = 'session_proc_ref' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlSessionProcRefs(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Execution');
+ fSkip = 'exec_basic' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlExec(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Execution Error Levels');
+ fSkip = 'exec_errorlevel' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlExecErrorLevel(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Execution Timeouts');
+ fSkip = 'exec_timeout' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlExecTimeout(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Creating directories');
+ fSkip = 'dir_create' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlDirCreate(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Creating temporary directories');
+ fSkip = 'dir_create_temp' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlDirCreateTemp(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Reading directories');
+ fSkip = 'dir_read' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlDirRead(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Copy to guest');
+ fSkip = 'copy_to' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlCopyTo(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Copy from guest');
+ fSkip = 'copy_from' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlCopyFrom(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Removing files');
+ fSkip = 'file_remove' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlFileRemove(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ reporter.testStart('Querying file information (stat)');
+ fSkip = 'file_stat' not in self.asTests or fRc is False;
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlFileStat(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ # FIXME: Failing tests.
+ # reporter.testStart('File read');
+ # fSkip = 'file_read' not in self.asTests or fRc is False;
+ # if fSkip is False:
+ # fRc, oTxsSession = self.testGuestCtrlFileRead(oSession, oTxsSession, oTestVm);
+ # reporter.testDone(fSkip);
+
+ # reporter.testStart('File write');
+ # fSkip = 'file_write' not in self.asTests or fRc is False;
+ # if fSkip is False:
+ # fRc, oTxsSession = self.testGuestCtrlFileWrite(oSession, oTxsSession, oTestVm);
+ # reporter.testDone(fSkip);
+
+ reporter.testStart('Updating Guest Additions');
+ fSkip = 'update_additions' not in self.asTests or fRc is False;
+ # Skip test for updating Guest Additions if we run on a too old (Windows) guest.
+ fSkip = oTestVm.sKind in ('WindowsNT4', 'Windows2000', 'WindowsXP', 'Windows2003');
+ if fSkip is False:
+ fRc, oTxsSession = self.testGuestCtrlUpdateAdditions(oSession, oTxsSession, oTestVm);
+ reporter.testDone(fSkip);
+
+ return (fRc, oTxsSession);
+
+ def gctrlCopyFileFrom(self, oGuestSession, sSrc, sDst, aFlags):
+ """
+ Helper function to copy a single file from the guest to the host.
+ """
+ fRc = True; # Be optimistic.
+ try:
+ reporter.log2('Copying guest file "%s" to host "%s"' % (sSrc, sDst));
+ if self.oTstDrv.fpApiVer >= 5.0:
+ curProgress = oGuestSession.fileCopyFromGuest(sSrc, sDst, aFlags);
+ else:
+ curProgress = oGuestSession.copyFrom(sSrc, sDst, aFlags);
+ if curProgress is not None:
+ oProgress = vboxwrappers.ProgressWrapper(curProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, "gctrlFileCopyFrom");
+ try:
+ oProgress.wait();
+ if not oProgress.isSuccess():
+ oProgress.logResult(fIgnoreErrors = True);
+ fRc = False;
+ except:
+ reporter.logXcpt('Waiting exception for sSrc="%s", sDst="%s":' % (sSrc, sDst));
+ fRc = False;
+ else:
+ reporter.error('No progress object returned');
+ fRc = False;
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('Copy from exception for sSrc="%s", sDst="%s":' % (sSrc, sDst));
+ fRc = False;
+
+ return fRc;
+
+ def gctrlCopyFileTo(self, oGuestSession, sSrc, sDst, aFlags):
+ """
+ Helper function to copy a single file from host to the guest.
+ """
+ fRc = True; # Be optimistic.
+ try:
+ reporter.log2('Copying host file "%s" to guest "%s" (flags %s)' % (sSrc, sDst, aFlags));
+ if self.oTstDrv.fpApiVer >= 5.0:
+ curProgress = oGuestSession.fileCopyToGuest(sSrc, sDst, aFlags);
+ else:
+ curProgress = oGuestSession.copyTo(sSrc, sDst, aFlags);
+ if curProgress is not None:
+ oProgress = vboxwrappers.ProgressWrapper(curProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, "gctrlCopyFileTo");
+ try:
+ oProgress.wait();
+ if not oProgress.isSuccess():
+ oProgress.logResult(fIgnoreErrors = True);
+ fRc = False;
+ except:
+ reporter.logXcpt('Wait exception for sSrc="%s", sDst="%s":' % (sSrc, sDst));
+ fRc = False;
+ else:
+ reporter.error('No progress object returned');
+ fRc = False;
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('Copy to exception for sSrc="%s", sDst="%s":' % (sSrc, sDst));
+ fRc = False;
+
+ return fRc;
+
+ def gctrlCreateDir(self, oTest, oRes, oGuestSession):
+ """
+ Helper function to create a guest directory specified in
+ the current test.
+ """
+ fRc = True; # Be optimistic.
+ reporter.log2('Creating directory "%s"' % (oTest.sDirectory,));
+
+ try:
+ oGuestSession.directoryCreate(oTest.sDirectory, \
+ oTest.fMode, oTest.aFlags);
+ if self.oTstDrv.fpApiVer >= 5.0:
+ fDirExists = oGuestSession.directoryExists(oTest.sDirectory, False);
+ else:
+ fDirExists = oGuestSession.directoryExists(oTest.sDirectory);
+ if fDirExists is False \
+ and oRes.fRc is True:
+ # Directory does not exist but we want it to.
+ fRc = False;
+ except:
+ reporter.logXcpt('Directory create exception for directory "%s":' % (oTest.sDirectory,));
+ if oRes.fRc is True:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ fRc = False;
+ # Directory creation failed, which was the expected result.
+
+ return fRc;
+
+ def gctrlReadDir(self, oTest, oRes, oGuestSession, subDir = ''): # pylint: disable=R0914
+ """
+ Helper function to read a guest directory specified in
+ the current test.
+ """
+ sDir = oTest.sDirectory;
+ sFilter = oTest.sFilter;
+ aFlags = oTest.aFlags;
+
+ fRc = True; # Be optimistic.
+ cDirs = 0; # Number of directories read.
+ cFiles = 0; # Number of files read.
+
+ try:
+ sCurDir = os.path.join(sDir, subDir);
+ #reporter.log2('Directory="%s", filter="%s", aFlags="%s"' % (sCurDir, sFilter, aFlags));
+ oCurDir = oGuestSession.directoryOpen(sCurDir, sFilter, aFlags);
+ while fRc:
+ try:
+ oFsObjInfo = oCurDir.read();
+ if oFsObjInfo.name == "." \
+ or oFsObjInfo.name == "..":
+ #reporter.log2('\tSkipping "%s"' % oFsObjInfo.name);
+ continue; # Skip "." and ".." entries.
+ if oFsObjInfo.type is vboxcon.FsObjType_Directory:
+ #reporter.log2('\tDirectory "%s"' % oFsObjInfo.name);
+ cDirs += 1;
+ sSubDir = oFsObjInfo.name;
+ if subDir != "":
+ sSubDir = os.path.join(subDir, oFsObjInfo.name);
+ fRc, cSubDirs, cSubFiles = self.gctrlReadDir(oTest, oRes, oGuestSession, sSubDir);
+ cDirs += cSubDirs;
+ cFiles += cSubFiles;
+ elif oFsObjInfo.type is vboxcon.FsObjType_File:
+ #reporter.log2('\tFile "%s"' % oFsObjInfo.name);
+ cFiles += 1;
+ elif oFsObjInfo.type is vboxcon.FsObjType_Symlink:
+ #reporter.log2('\tSymlink "%s" -- not tested yet' % oFsObjInfo.name);
+ pass;
+ else:
+ reporter.error('\tDirectory "%s" contains invalid directory entry "%s" (type %d)' % \
+ (sCurDir, oFsObjInfo.name, oFsObjInfo.type));
+ fRc = False;
+ except Exception as oXcpt:
+ # No necessarily an error -- could be VBOX_E_OBJECT_NOT_FOUND. See reference.
+ if vbox.ComError.equal(oXcpt, vbox.ComError.VBOX_E_OBJECT_NOT_FOUND):
+ #reporter.log2('\tNo more directory entries for "%s"' % (sCurDir,));
+ break
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('\tDirectory open exception for directory="%s":' % (sCurDir,));
+ fRc = False;
+ break;
+ oCurDir.close();
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('\tDirectory open exception for directory="%s":' % (sCurDir,));
+ fRc = False;
+
+ return (fRc, cDirs, cFiles);
+
+ def gctrlExecDoTest(self, i, oTest, oRes, oGuestSession):
+ """
+ Wrapper function around gctrlExecute to provide more sanity checking
+ when needed in actual execution tests.
+ """
+ reporter.log('Testing #%d, cmd="%s" ...' % (i, oTest.sCmd));
+ fRc = self.gctrlExecute(oTest, oGuestSession);
+ if fRc is oRes.fRc:
+ if fRc is True:
+ # Compare exit status / code on successful process execution.
+ if oTest.uExitStatus != oRes.uExitStatus \
+ or oTest.iExitCode != oRes.iExitCode:
+ reporter.error('Test #%d failed: Got exit status + code %d,%d, expected %d,%d'
+ % (i, oTest.uExitStatus, oTest.iExitCode, oRes.uExitStatus, oRes.iExitCode));
+ return False;
+ if fRc is True:
+ # Compare test / result buffers on successful process execution.
+ if oTest.sBuf is not None \
+ and oRes.sBuf is not None:
+ if bytes(oTest.sBuf) != bytes(oRes.sBuf):
+ reporter.error('Test #%d failed: Got buffer\n%s (%d bytes), expected\n%s (%d bytes)'
+ % (i, map(hex, map(ord, oTest.sBuf)), len(oTest.sBuf), \
+ map(hex, map(ord, oRes.sBuf)), len(oRes.sBuf)));
+ return False;
+ else:
+ reporter.log2('Test #%d passed: Buffers match (%d bytes)' % (i, len(oRes.sBuf)));
+ elif oRes.sBuf is not None \
+ and oRes.sBuf:
+ reporter.error('Test #%d failed: Got no buffer data, expected\n%s (%dbytes)' %
+ (i, map(hex, map(ord, oRes.sBuf)), len(oRes.sBuf)));
+ return False;
+ elif oRes.cbStdOut > 0 \
+ and oRes.cbStdOut != oTest.cbStdOut:
+ reporter.error('Test #%d failed: Got %d stdout data, expected %d'
+ % (i, oTest.cbStdOut, oRes.cbStdOut));
+ return False;
+ else:
+ reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc, oRes.fRc));
+ return False;
+ return True;
+
+ def gctrlExecute(self, oTest, oGuestSession):
+ """
+ Helper function to execute a program on a guest, specified in
+ the current test.
+ """
+ fRc = True; # Be optimistic.
+
+ ## @todo Compare execution timeouts!
+ #tsStart = base.timestampMilli();
+
+ reporter.log2('Using session user=%s, sDomain=%s, name=%s, timeout=%d' \
+ % (oGuestSession.user, oGuestSession.domain, \
+ oGuestSession.name, oGuestSession.timeout));
+ reporter.log2('Executing sCmd=%s, aFlags=%s, timeoutMS=%d, aArgs=%s, aEnv=%s' \
+ % (oTest.sCmd, oTest.aFlags, oTest.timeoutMS, \
+ oTest.aArgs, oTest.aEnv));
+ try:
+ curProc = oGuestSession.processCreate(oTest.sCmd,
+ oTest.aArgs if self.oTstDrv.fpApiVer >= 5.0 else oTest.aArgs[1:],
+ oTest.aEnv, oTest.aFlags, oTest.timeoutMS);
+ if curProc is not None:
+ reporter.log2('Process start requested, waiting for start (%dms) ...' % (oTest.timeoutMS,));
+ fWaitFor = [ vboxcon.ProcessWaitForFlag_Start ];
+ waitResult = curProc.waitForArray(fWaitFor, oTest.timeoutMS);
+ reporter.log2('Wait result returned: %d, current process status is: %d' % (waitResult, curProc.status));
+
+ if curProc.status == vboxcon.ProcessStatus_Started:
+ fWaitFor = [ vboxcon.ProcessWaitForFlag_Terminate ];
+ if vboxcon.ProcessCreateFlag_WaitForStdOut in oTest.aFlags:
+ fWaitFor.append(vboxcon.ProcessWaitForFlag_StdOut);
+ if vboxcon.ProcessCreateFlag_WaitForStdErr in oTest.aFlags:
+ fWaitFor.append(vboxcon.ProcessWaitForFlag_StdErr);
+ ## @todo Add vboxcon.ProcessWaitForFlag_StdIn.
+ reporter.log2('Process (PID %d) started, waiting for termination (%dms), waitFlags=%s ...' \
+ % (curProc.PID, oTest.timeoutMS, fWaitFor));
+ while True:
+ waitResult = curProc.waitForArray(fWaitFor, oTest.timeoutMS);
+ reporter.log2('Wait returned: %d' % (waitResult,));
+ try:
+ # Try stdout.
+ if waitResult == vboxcon.ProcessWaitResult_StdOut \
+ or waitResult == vboxcon.ProcessWaitResult_WaitFlagNotSupported:
+ reporter.log2('Reading stdout ...');
+ abBuf = curProc.Read(1, 64 * 1024, oTest.timeoutMS);
+ if abBuf:
+ reporter.log2('Process (PID %d) got %d bytes of stdout data' % (curProc.PID, len(abBuf)));
+ oTest.cbStdOut += len(abBuf);
+ oTest.sBuf = abBuf; # Appending does *not* work atm, so just assign it. No time now.
+ # Try stderr.
+ if waitResult == vboxcon.ProcessWaitResult_StdErr \
+ or waitResult == vboxcon.ProcessWaitResult_WaitFlagNotSupported:
+ reporter.log2('Reading stderr ...');
+ abBuf = curProc.Read(2, 64 * 1024, oTest.timeoutMS);
+ if abBuf:
+ reporter.log2('Process (PID %d) got %d bytes of stderr data' % (curProc.PID, len(abBuf)));
+ oTest.cbStdErr += len(abBuf);
+ oTest.sBuf = abBuf; # Appending does *not* work atm, so just assign it. No time now.
+ # Use stdin.
+ if waitResult == vboxcon.ProcessWaitResult_StdIn \
+ or waitResult == vboxcon.ProcessWaitResult_WaitFlagNotSupported:
+ pass; #reporter.log2('Process (PID %d) needs stdin data' % (curProc.pid,));
+ # Termination or error?
+ if waitResult == vboxcon.ProcessWaitResult_Terminate \
+ or waitResult == vboxcon.ProcessWaitResult_Error \
+ or waitResult == vboxcon.ProcessWaitResult_Timeout:
+ reporter.log2('Process (PID %d) reported terminate/error/timeout: %d, status: %d' \
+ % (curProc.PID, waitResult, curProc.status));
+ break;
+ except:
+ # Just skip reads which returned nothing.
+ pass;
+ reporter.log2('Final process status (PID %d) is: %d' % (curProc.PID, curProc.status));
+ reporter.log2('Process (PID %d) %d stdout, %d stderr' % (curProc.PID, oTest.cbStdOut, oTest.cbStdErr));
+ oTest.uExitStatus = curProc.status;
+ oTest.iExitCode = curProc.exitCode;
+ reporter.log2('Process (PID %d) has exit code: %d' % (curProc.PID, oTest.iExitCode));
+ except KeyboardInterrupt:
+ reporter.error('Process (PID %d) execution interrupted' % (curProc.PID,));
+ if curProc is not None:
+ curProc.close();
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('Execution exception for command "%s":' % (oTest.sCmd,));
+ fRc = False;
+
+ return fRc;
+
+ def testGuestCtrlSessionEnvironment(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests the guest session environment changes.
+ """
+ aoTests = [
+ # Check basic operations.
+ tdTestSessionEx([ # Initial environment is empty.
+ tdStepSessionCheckEnv(),
+ # Check clearing empty env.
+ tdStepSessionClearEnv(),
+ tdStepSessionCheckEnv(),
+ # Check set.
+ tdStepSessionSetEnv('FOO', 'BAR'),
+ tdStepSessionCheckEnv(['FOO=BAR',]),
+ tdStepRequireMinimumApiVer(5.0), # 4.3 can't cope with the remainder.
+ tdStepSessionClearEnv(),
+ tdStepSessionCheckEnv(),
+ # Check unset.
+ tdStepSessionUnsetEnv('BAR'),
+ tdStepSessionCheckEnv(['BAR']),
+ tdStepSessionClearEnv(),
+ tdStepSessionCheckEnv(),
+ # Set + unset.
+ tdStepSessionSetEnv('FOO', 'BAR'),
+ tdStepSessionCheckEnv(['FOO=BAR',]),
+ tdStepSessionUnsetEnv('FOO'),
+ tdStepSessionCheckEnv(['FOO']),
+ # Bulk environment changes (via attrib) (shall replace existing 'FOO').
+ tdStepSessionBulkEnv( ['PATH=/bin:/usr/bin', 'TMPDIR=/var/tmp', 'USER=root']),
+ tdStepSessionCheckEnv(['PATH=/bin:/usr/bin', 'TMPDIR=/var/tmp', 'USER=root']),
+ ]),
+ tdTestSessionEx([ # Check that setting the same value several times works.
+ tdStepSessionSetEnv('FOO','BAR'),
+ tdStepSessionCheckEnv([ 'FOO=BAR',]),
+ tdStepSessionSetEnv('FOO','BAR2'),
+ tdStepSessionCheckEnv([ 'FOO=BAR2',]),
+ tdStepSessionSetEnv('FOO','BAR3'),
+ tdStepSessionCheckEnv([ 'FOO=BAR3',]),
+ tdStepRequireMinimumApiVer(5.0), # 4.3 can't cope with the remainder.
+ # Add a little unsetting to the mix.
+ tdStepSessionSetEnv('BAR', 'BEAR'),
+ tdStepSessionCheckEnv([ 'FOO=BAR3', 'BAR=BEAR',]),
+ tdStepSessionUnsetEnv('FOO'),
+ tdStepSessionCheckEnv([ 'FOO', 'BAR=BEAR',]),
+ tdStepSessionSetEnv('FOO','BAR4'),
+ tdStepSessionCheckEnv([ 'FOO=BAR4', 'BAR=BEAR',]),
+ # The environment is case sensitive.
+ tdStepSessionSetEnv('foo','BAR5'),
+ tdStepSessionCheckEnv([ 'FOO=BAR4', 'BAR=BEAR', 'foo=BAR5']),
+ tdStepSessionUnsetEnv('foo'),
+ tdStepSessionCheckEnv([ 'FOO=BAR4', 'BAR=BEAR', 'foo']),
+ ]),
+ tdTestSessionEx([ # Bulk settings merges stuff, last entry standing.
+ tdStepSessionBulkEnv(['FOO=bar', 'foo=bar', 'FOO=doofus', 'TMPDIR=/tmp', 'foo=bar2']),
+ tdStepSessionCheckEnv(['FOO=doofus', 'TMPDIR=/tmp', 'foo=bar2']),
+ tdStepRequireMinimumApiVer(5.0), # 4.3 is buggy!
+ tdStepSessionBulkEnv(['2=1+1', 'FOO=doofus2', ]),
+ tdStepSessionCheckEnv(['2=1+1', 'FOO=doofus2' ]),
+ ]),
+ # Invalid variable names.
+ tdTestSessionEx([
+ tdStepSessionSetEnv('', 'FOO', vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepRequireMinimumApiVer(5.0), # 4.3 is too relaxed checking input!
+ tdStepSessionSetEnv('=', '===', vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionSetEnv('FOO=', 'BAR', vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionSetEnv('=FOO', 'BAR', vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepRequireMinimumApiVer(5.0), # 4.3 is buggy and too relaxed!
+ tdStepSessionBulkEnv(['', 'foo=bar'], vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionBulkEnv(['=', 'foo=bar'], vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ tdStepSessionBulkEnv(['=FOO', 'foo=bar'], vbox.ComError.E_INVALIDARG),
+ tdStepSessionCheckEnv(),
+ ]),
+ # A bit more weird keys/values.
+ tdTestSessionEx([ tdStepSessionSetEnv('$$$', ''),
+ tdStepSessionCheckEnv([ '$$$=',]), ]),
+ tdTestSessionEx([ tdStepSessionSetEnv('$$$', '%%%'),
+ tdStepSessionCheckEnv([ '$$$=%%%',]),
+ ]),
+ tdTestSessionEx([ tdStepRequireMinimumApiVer(5.0), # 4.3 is buggy!
+ tdStepSessionSetEnv(u'ß$%ß&', ''),
+ tdStepSessionCheckEnv([ u'ß$%ß&=',]),
+ ]),
+ # Misc stuff.
+ tdTestSessionEx([ tdStepSessionSetEnv('FOO', ''),
+ tdStepSessionCheckEnv(['FOO=',]),
+ ]),
+ tdTestSessionEx([ tdStepSessionSetEnv('FOO', 'BAR'),
+ tdStepSessionCheckEnv(['FOO=BAR',])
+ ],),
+ tdTestSessionEx([ tdStepSessionSetEnv('FOO', 'BAR'),
+ tdStepSessionSetEnv('BAR', 'BAZ'),
+ tdStepSessionCheckEnv([ 'FOO=BAR', 'BAR=BAZ',]),
+ ]),
+ ];
+ return tdTestSessionEx.executeListTestSessions(aoTests, self.oTstDrv, oSession, oTxsSession, oTestVm, 'SessionEnv');
+
+ def testGuestCtrlSession(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests the guest session handling.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ aaTests = [
+ # Invalid parameters.
+ [ tdTestSession(),
+ tdTestResultSession(fRc = False) ],
+ [ tdTestSession(sUser = ''),
+ tdTestResultSession(fRc = False) ],
+ [ tdTestSession(sPassword = 'bar'),
+ tdTestResultSession(fRc = False) ],
+ [ tdTestSession(sDomain = 'boo'),
+ tdTestResultSession(fRc = False) ],
+ [ tdTestSession(sPassword = 'bar', sDomain = 'boo'),
+ tdTestResultSession(fRc = False) ],
+ # User account without a passwort - forbidden.
+ [ tdTestSession(sUser = sUser),
+ tdTestResultSession(fRc = False) ],
+ # Wrong credentials.
+ # Note: On Guest Additions < 4.3 this always succeeds because these don't
+ # support creating dedicated sessions. Instead, guest process creation
+ # then will fail. See note below.
+ [ tdTestSession(sUser = 'foo', sPassword = 'bar', sDomain = 'boo'),
+ tdTestResultSession(fRc = False) ],
+ # Correct credentials.
+ [ tdTestSession(sUser = sUser, sPassword = sPassword),
+ tdTestResultSession(fRc = True, cNumSessions = 1) ]
+ ];
+
+ # Parameters.
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestSession, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ reporter.log('Testing #%d, user="%s", sPassword="%s", sDomain="%s" ...' \
+ % (i, curTest.oCreds.sUser, curTest.oCreds.sPassword, curTest.oCreds.sDomain));
+ curGuestSessionName = 'testGuestCtrlSession: Test #%d' % (i);
+ fRc2, curGuestSession = curTest.createSession(curGuestSessionName);
+ # See note about < 4.3 Guest Additions above.
+ if curGuestSession is not None \
+ and curGuestSession.protocolVersion >= 2 \
+ and fRc2 is not curRes.fRc:
+ reporter.error('Test #%d failed: Session creation failed: Got %s, expected %s' \
+ % (i, fRc2, curRes.fRc));
+ fRc = False;
+ if fRc2:
+ # On Guest Additions < 4.3 getSessionCount() always will return 1, so skip the
+ # check then.
+ if curGuestSession.protocolVersion >= 2:
+ curSessionCount = curTest.getSessionCount(self.oTstDrv.oVBoxMgr);
+ if curSessionCount is not curRes.cNumSessions:
+ reporter.error('Test #%d failed: Session count does not match: Got %d, expected %d' \
+ % (i, curSessionCount, curRes.cNumSessions));
+ fRc = False;
+ break;
+ if curGuestSession is not None \
+ and curGuestSession.name != curGuestSessionName:
+ reporter.error('Test #%d failed: Session name does not match: Got "%s", expected "%s"' \
+ % (i, curGuestSession.name, curGuestSessionName));
+ fRc = False;
+ break;
+ fRc2 = curTest.closeSession();
+ if fRc2 is False:
+ reporter.error('Test #%d failed: Session could not be closed' % (i,));
+ fRc = False;
+ break;
+
+ if fRc is False:
+ return (False, oTxsSession);
+
+ # Multiple sessions.
+ iMaxGuestSessions = 31; # Maximum number of concurrent guest session allowed.
+ # Actually, this is 32, but we don't test session 0.
+ multiSession = {};
+ reporter.log2('Opening multiple guest tsessions at once ...');
+ for i in range(iMaxGuestSessions + 1):
+ multiSession[i] = tdTestSession(sUser = sUser, sPassword = sPassword, sSessionName = 'MultiSession #%d' % (i,));
+ multiSession[i].setEnvironment(oSession, oTxsSession, oTestVm);
+ curSessionCount = multiSession[i].getSessionCount(self.oTstDrv.oVBoxMgr);
+ reporter.log2('MultiSession test #%d count is %d' % (i, curSessionCount));
+ if curSessionCount is not i:
+ reporter.error('MultiSession count #%d must be %d, got %d' % (i, i, curSessionCount));
+ fRc = False;
+ break;
+ fRc2, _ = multiSession[i].createSession('MultiSession #%d' % (i,));
+ if fRc2 is not True:
+ if i < iMaxGuestSessions:
+ reporter.error('MultiSession #%d test failed' % (i,));
+ fRc = False;
+ else:
+ reporter.log('MultiSession #%d exceeded concurrent guest session count, good' % (i,));
+ break;
+
+ curSessionCount = multiSession[i].getSessionCount(self.oTstDrv.oVBoxMgr);
+ if curSessionCount is not iMaxGuestSessions:
+ reporter.error('Final MultiSession count must be %d, got %d'
+ % (iMaxGuestSessions, curSessionCount));
+ return (False, oTxsSession);
+
+ reporter.log2('Closing MultiSessions ...');
+ iLastSession = iMaxGuestSessions - 1;
+ for i in range(iLastSession): # Close all but the last opened session.
+ fRc2 = multiSession[i].closeSession();
+ reporter.log2('MultiSession #%d count is %d' % (i, multiSession[i].getSessionCount(self.oTstDrv.oVBoxMgr),));
+ if fRc2 is False:
+ reporter.error('Closing MultiSession #%d failed' % (i,));
+ fRc = False;
+ break;
+ curSessionCount = multiSession[i].getSessionCount(self.oTstDrv.oVBoxMgr);
+ if curSessionCount is not 1:
+ reporter.error('Final MultiSession count #2 must be 1, got %d' % (curSessionCount,));
+ fRc = False;
+
+ try:
+ # r=bird: multiSession[0].oGuestSession is None! Why don't you just use 'assert' or 'if' to check
+ # the functioning of the __testcase__?
+
+ # Make sure that accessing the first opened guest session does not work anymore because we just removed (closed) it.
+ curSessionName = multiSession[0].oGuestSession.name;
+ reporter.error('Accessing first removed MultiSession should not be possible, got name="%s"' % (curSessionName,));
+ fRc = False;
+ except:
+ reporter.logXcpt('Could not access first removed MultiSession object, good:');
+
+ try:
+ # Try Accessing last opened session which did not get removed yet.
+ curSessionName = multiSession[iLastSession].oGuestSession.name;
+ reporter.log('Accessing last standing MultiSession worked, got name="%s"' % (curSessionName,));
+ multiSession[iLastSession].closeSession();
+ curSessionCount = multiSession[i].getSessionCount(self.oTstDrv.oVBoxMgr);
+ if curSessionCount is not 0:
+ reporter.error('Final MultiSession count #3 must be 0, got %d' % (curSessionCount,));
+ fRc = False;
+ except:
+ reporter.logXcpt('Could not access last standing MultiSession object:');
+ fRc = False;
+
+ ## @todo Test session timeouts.
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlSessionFileRefs(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests the guest session file reference handling.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ sPassword = "password";
+ sDomain = "";
+ sFile = "C:\\windows\\system32\\kernel32.dll";
+
+ # Number of stale guest files to create.
+ cStaleFiles = 10;
+
+ fRc = True;
+ try:
+ oGuest = oSession.o.console.guest;
+ oGuestSession = oGuest.createSession(sUser, sPassword, sDomain, \
+ "testGuestCtrlSessionFileRefs");
+ fWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start ];
+ waitResult = oGuestSession.waitForArray(fWaitFor, 30 * 1000);
+ #
+ # Be nice to Guest Additions < 4.3: They don't support session handling and
+ # therefore return WaitFlagNotSupported.
+ #
+ if waitResult != vboxcon.GuestSessionWaitResult_Start \
+ and waitResult != vboxcon.GuestSessionWaitResult_WaitFlagNotSupported:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.log('Session did not start successfully, returned wait result: %d' \
+ % (waitResult));
+ return (False, oTxsSession);
+ reporter.log('Session successfully started');
+
+ #
+ # Open guest files and "forget" them (stale entries).
+ # For them we don't have any references anymore intentionally.
+ #
+ reporter.log2('Opening stale files');
+ for i in range(0, cStaleFiles):
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oGuestSession.fileOpen(sFile, vboxcon.FileAccessMode_ReadOnly, vboxcon.FileOpenAction_OpenExisting, 0);
+ else:
+ oGuestSession.fileOpen(sFile, "r", "oe", 0);
+ # Note: Use a timeout in the call above for not letting the stale processes
+ # hanging around forever. This can happen if the installed Guest Additions
+ # do not support terminating guest processes.
+ except:
+ reporter.errorXcpt('Opening stale file #%d failed:' % (i,));
+ fRc = False;
+ break;
+
+ if fRc:
+ cFiles = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'files'));
+ if cFiles != cStaleFiles:
+ reporter.error('Test failed: Got %d stale files, expected %d' % (cFiles, cStaleFiles));
+ fRc = False;
+
+ if fRc:
+ #
+ # Open non-stale files and close them again.
+ #
+ reporter.log2('Opening non-stale files');
+ aaFiles = [];
+ for i in range(0, cStaleFiles):
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oCurFile = oGuestSession.fileOpen(sFile, vboxcon.FileAccessMode_ReadOnly,
+ vboxcon.FileOpenAction_OpenExisting, 0);
+ else:
+ oCurFile = oGuestSession.fileOpen(sFile, "r", "oe", 0);
+ aaFiles.append(oCurFile);
+ except:
+ reporter.errorXcpt('Opening non-stale file #%d failed:' % (i,));
+ fRc = False;
+ break;
+ if fRc:
+ cFiles = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'files'));
+ if cFiles != cStaleFiles * 2:
+ reporter.error('Test failed: Got %d total files, expected %d' % (cFiles, cStaleFiles * 2));
+ fRc = False;
+ if fRc:
+ reporter.log2('Closing all non-stale files again ...');
+ for i in range(0, cStaleFiles):
+ try:
+ aaFiles[i].close();
+ except:
+ reporter.errorXcpt('Waiting for non-stale file #%d failed:' % (i,));
+ fRc = False;
+ break;
+ cFiles = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'files'));
+ # Here we count the stale files (that is, files we don't have a reference
+ # anymore for) and the opened and then closed non-stale files (that we still keep
+ # a reference in aaFiles[] for).
+ if cFiles != cStaleFiles:
+ reporter.error('Test failed: Got %d total files, expected %d' \
+ % (cFiles, cStaleFiles));
+ fRc = False;
+ if fRc:
+ #
+ # Check if all (referenced) non-stale files now are in "closed" state.
+ #
+ reporter.log2('Checking statuses of all non-stale files ...');
+ for i in range(0, cStaleFiles):
+ try:
+ curFilesStatus = aaFiles[i].status;
+ if curFilesStatus != vboxcon.FileStatus_Closed:
+ reporter.error('Test failed: Non-stale file #%d has status %d, expected %d' \
+ % (i, curFilesStatus, vboxcon.FileStatus_Closed));
+ fRc = False;
+ except:
+ reporter.errorXcpt('Checking status of file #%d failed:' % (i,));
+ fRc = False;
+ break;
+ if fRc:
+ reporter.log2('All non-stale files closed');
+ cFiles = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'files'));
+ reporter.log2('Final guest session file count: %d' % (cFiles,));
+ # Now try to close the session and see what happens.
+ reporter.log2('Closing guest session ...');
+ oGuestSession.close();
+ except:
+ reporter.errorXcpt('Testing for stale processes failed:');
+ fRc = False;
+
+ return (fRc, oTxsSession);
+
+ #def testGuestCtrlSessionDirRefs(self, oSession, oTxsSession, oTestVm):
+ # """
+ # Tests the guest session directory reference handling.
+ # """
+
+ # fRc = True;
+ # return (fRc, oTxsSession);
+
+ def testGuestCtrlSessionProcRefs(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests the guest session process reference handling.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ sPassword = "password";
+ sDomain = "";
+ sCmd = "C:\\windows\\system32\\cmd.exe";
+ aArgs = [sCmd,];
+
+ # Number of stale guest processes to create.
+ cStaleProcs = 10;
+
+ fRc = True;
+ try:
+ oGuest = oSession.o.console.guest;
+ oGuestSession = oGuest.createSession(sUser, sPassword, sDomain, \
+ "testGuestCtrlSessionProcRefs");
+ fWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start ];
+ waitResult = oGuestSession.waitForArray(fWaitFor, 30 * 1000);
+ #
+ # Be nice to Guest Additions < 4.3: They don't support session handling and
+ # therefore return WaitFlagNotSupported.
+ #
+ if waitResult != vboxcon.GuestSessionWaitResult_Start \
+ and waitResult != vboxcon.GuestSessionWaitResult_WaitFlagNotSupported:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.log('Session did not start successfully, returned wait result: %d' \
+ % (waitResult));
+ return (False, oTxsSession);
+ reporter.log('Session successfully started');
+
+ #
+ # Fire off forever-running processes and "forget" them (stale entries).
+ # For them we don't have any references anymore intentionally.
+ #
+ reporter.log2('Starting stale processes');
+ for i in range(0, cStaleProcs):
+ try:
+ oGuestSession.processCreate(sCmd,
+ aArgs if self.oTstDrv.fpApiVer >= 5.0 else aArgs[1:], [],
+ [ vboxcon.ProcessCreateFlag_WaitForStdOut ], \
+ 30 * 1000);
+ # Note: Use a timeout in the call above for not letting the stale processes
+ # hanging around forever. This can happen if the installed Guest Additions
+ # do not support terminating guest processes.
+ except:
+ reporter.logXcpt('Creating stale process #%d failed:' % (i,));
+ fRc = False;
+ break;
+
+ if fRc:
+ cProcs = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes'));
+ if cProcs != cStaleProcs:
+ reporter.error('Test failed: Got %d stale processes, expected %d' % (cProcs, cStaleProcs));
+ fRc = False;
+
+ if fRc:
+ #
+ # Fire off non-stale processes and wait for termination.
+ #
+ if oTestVm.isWindows():
+ aArgs = [ sCmd, '/C', 'dir', '/S', 'C:\\Windows\\system'];
+ reporter.log2('Starting non-stale processes');
+ aaProcs = [];
+ for i in range(0, cStaleProcs):
+ try:
+ oCurProc = oGuestSession.processCreate(sCmd, aArgs if self.oTstDrv.fpApiVer >= 5.0 else aArgs[1:],
+ [], [], 0); # Infinite timeout.
+ aaProcs.append(oCurProc);
+ except:
+ reporter.logXcpt('Creating non-stale process #%d failed:' % (i,));
+ fRc = False;
+ break;
+ if fRc:
+ reporter.log2('Waiting for non-stale processes to terminate');
+ for i in range(0, cStaleProcs):
+ try:
+ aaProcs[i].waitForArray([ vboxcon.ProcessWaitForFlag_Terminate ], 30 * 1000);
+ curProcStatus = aaProcs[i].status;
+ if aaProcs[i].status != vboxcon.ProcessStatus_TerminatedNormally:
+ reporter.error('Test failed: Waiting for non-stale processes #%d'
+ ' resulted in status %d, expected %d' \
+ % (i, curProcStatus, vboxcon.ProcessStatus_TerminatedNormally));
+ fRc = False;
+ except:
+ reporter.logXcpt('Waiting for non-stale process #%d failed:' % (i,));
+ fRc = False;
+ break;
+ cProcs = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes'));
+ # Here we count the stale processes (that is, processes we don't have a reference
+ # anymore for) and the started + terminated non-stale processes (that we still keep
+ # a reference in aaProcs[] for).
+ if cProcs != (cStaleProcs * 2):
+ reporter.error('Test failed: Got %d total processes, expected %d' \
+ % (cProcs, cStaleProcs));
+ fRc = False;
+ if fRc:
+ #
+ # Check if all (referenced) non-stale processes now are in "terminated" state.
+ #
+ for i in range(0, cStaleProcs):
+ curProcStatus = aaProcs[i].status;
+ if aaProcs[i].status != vboxcon.ProcessStatus_TerminatedNormally:
+ reporter.error('Test failed: Non-stale processes #%d has status %d, expected %d' \
+ % (i, curProcStatus, vboxcon.ProcessStatus_TerminatedNormally));
+ fRc = False;
+ if fRc:
+ reporter.log2('All non-stale processes terminated');
+
+ # Fire off blocking processes which are terminated via terminate().
+ if oTestVm.isWindows():
+ aArgs = [ sCmd, '/C', 'dir', '/S', 'C:\\Windows'];
+ reporter.log2('Starting blocking processes');
+ aaProcs = [];
+ for i in range(0, cStaleProcs):
+ try:
+ oCurProc = oGuestSession.processCreate(sCmd, aArgs if self.oTstDrv.fpApiVer >= 5.0 else aArgs[1:],
+ [], [], 30 * 1000);
+ # Note: Use a timeout in the call above for not letting the stale processes
+ # hanging around forever. This can happen if the installed Guest Additions
+ # do not support terminating guest processes.
+ aaProcs.append(oCurProc);
+ except:
+ reporter.logXcpt('Creating blocking process failed:');
+ fRc = False;
+ break;
+ if fRc:
+ reporter.log2('Terminating blocking processes');
+ for i in range(0, cStaleProcs):
+ try:
+ aaProcs[i].terminate();
+ except: # Termination might not be supported, just skip and log it.
+ reporter.logXcpt('Termination of blocking process failed, skipped:');
+ cProcs = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes'));
+ if cProcs != (cStaleProcs * 2): # Still should be 20 processes because we terminated the 10 newest ones.
+ reporter.error('Test failed: Got %d total processes, expected %d' % (cProcs, cStaleProcs * 2));
+ fRc = False;
+ cProcs = len(self.oTstDrv.oVBoxMgr.getArray(oGuestSession, 'processes'));
+ reporter.log2('Final guest session processes count: %d' % (cProcs,));
+ # Now try to close the session and see what happens.
+ reporter.log2('Closing guest session ...');
+ oGuestSession.close();
+ except:
+ reporter.logXcpt('Testing for stale processes failed:');
+ fRc = False;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlExec(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914,R0915
+ """
+ Tests the basic execution feature.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ if oTestVm.isWindows():
+ # Outputting stuff.
+ sImageOut = "C:\\windows\\system32\\cmd.exe";
+ else:
+ reporter.error('Implement me!'); ## @todo Implement non-Windows bits.
+ return (False, oTxsSession);
+
+ aaInvalid = [
+ # Invalid parameters.
+ [ tdTestExec(sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = False) ],
+ # Non-existent / invalid image.
+ [ tdTestExec(sCmd = "non-existent", sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = False) ],
+ [ tdTestExec(sCmd = "non-existent2", sUser = sUser, sPassword = sPassword, fWaitForExit = True),
+ tdTestResultExec(fRc = False) ],
+ # Use an invalid format string.
+ [ tdTestExec(sCmd = "%$%%%&", sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = False) ],
+ # More stuff.
+ [ tdTestExec(sCmd = u"ƒ‰‹ˆ÷‹¸", sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = False) ],
+ [ tdTestExec(sCmd = "???://!!!", sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = False) ],
+ [ tdTestExec(sCmd = "<>!\\", sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = False) ]
+ # Enable as soon as ERROR_BAD_DEVICE is implemented.
+ #[ tdTestExec(sCmd = "CON", sUser = sUser, sPassword = sPassword),
+ # tdTestResultExec(fRc = False) ]
+ ];
+
+ if oTestVm.isWindows():
+ sVBoxControl = "C:\\Program Files\\Oracle\\VirtualBox Guest Additions\\VBoxControl.exe";
+ aaExec = [
+ # Basic executon.
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', 'c:\\windows\\system32' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', 'c:\\windows\\system32\\kernel32.dll' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', 'c:\\windows\\system32\\nonexist.dll' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', '/wrongparam' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # Paths with spaces.
+ ## @todo Get path of installed Guest Additions. Later.
+ [ tdTestExec(sCmd = sVBoxControl, aArgs = [ sVBoxControl, 'version' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True) ],
+ # StdOut.
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', 'c:\\windows\\system32' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', 'stdout-non-existing' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # StdErr.
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', 'c:\\windows\\system32' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', 'stderr-non-existing' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # StdOut + StdErr.
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', 'c:\\windows\\system32' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True) ],
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir', '/S', 'stdouterr-non-existing' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 1) ]
+ # FIXME: Failing tests.
+ # Environment variables.
+ # [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'set', 'TEST_NONEXIST' ],
+ # sUser = sUser, sPassword = sPassword),
+ # tdTestResultExec(fRc = True, iExitCode = 1) ]
+ # [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'set', 'windir' ],
+ # sUser = sUser, sPassword = sPassword,
+ # aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, sBuf = 'windir=C:\\WINDOWS\r\n') ],
+ # [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'set', 'TEST_FOO' ],
+ # sUser = sUser, sPassword = sPassword,
+ # aEnv = [ 'TEST_FOO=BAR' ],
+ # aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, sBuf = 'TEST_FOO=BAR\r\n') ],
+ # [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'set', 'TEST_FOO' ],
+ # sUser = sUser, sPassword = sPassword,
+ # aEnv = [ 'TEST_FOO=BAR', 'TEST_BAZ=BAR' ],
+ # aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, sBuf = 'TEST_FOO=BAR\r\n') ]
+
+ ## @todo Create some files (or get files) we know the output size of to validate output length!
+ ## @todo Add task which gets killed at some random time while letting the guest output something.
+ ];
+
+ # Manual test, not executed automatically.
+ aaManual = [
+ [ tdTestExec(sCmd = sImageOut, aArgs = [ sImageOut, '/C', 'dir /S C:\\Windows' ],
+ sUser = sUser, sPassword = sPassword,
+ aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ tdTestResultExec(fRc = True, cbStdOut = 497917) ] ];
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ # Build up the final test array for the first batch.
+ aaTests = [];
+ aaTests.extend(aaInvalid);
+ if aaExec is not None:
+ aaTests.extend(aaExec);
+ fRc = True;
+
+ #
+ # Single execution stuff. Nice for debugging.
+ #
+ fManual = False;
+ if fManual:
+ curTest = aaTests[1][0]; # tdTestExec, use an index, later.
+ curRes = aaTests[1][1]; # tdTestResultExec
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlExec: Single test 1');
+ if fRc is False:
+ reporter.error('Single test failed: Could not create session');
+ else:
+ fRc = self.gctrlExecDoTest(0, curTest, curRes, curGuestSession);
+ curTest.closeSession();
+
+ curTest = aaTests[2][0]; # tdTestExec, use an index, later.
+ curRes = aaTests[2][1]; # tdTestResultExec
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlExec: Single test 2');
+ if fRc is False:
+ reporter.error('Single test failed: Could not create session');
+ else:
+ fRc = self.gctrlExecDoTest(0, curTest, curRes, curGuestSession);
+ curTest.closeSession();
+
+ curTest = aaTests[3][0]; # tdTestExec, use an index, later.
+ curRes = aaTests[3][1]; # tdTestResultExec
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlExec: Single test 3');
+ if fRc is False:
+ reporter.error('Single test failed: Could not create session');
+ else:
+ fRc = self.gctrlExecDoTest(0, curTest, curRes, curGuestSession);
+ curTest.closeSession();
+ return (fRc, oTxsSession);
+ else:
+ aaManual = aaManual; # Workaround for pylint #W0612.
+
+ if fRc is False:
+ return (fRc, oTxsSession);
+
+ #
+ # First batch: One session per guest process.
+ #
+ reporter.log('One session per guest process ...');
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResultExec
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlExec: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ fRc = self.gctrlExecDoTest(i, curTest, curRes, curGuestSession);
+ if fRc is False:
+ break;
+ fRc = curTest.closeSession();
+ if fRc is False:
+ break;
+
+ # No sessions left?
+ if fRc is True:
+ aSessions = self.oTstDrv.oVBoxMgr.getArray(oSession.o.console.guest, 'sessions');
+ cSessions = len(aSessions);
+ if cSessions is not 0:
+ reporter.error('Found %d stale session(s), expected 0:' % (cSessions,));
+ for (i, aSession) in enumerate(aSessions):
+ reporter.log('\tStale session #%d ("%s")' % (aSession.id, aSession.name));
+ fRc = False;
+
+ if fRc is False:
+ return (fRc, oTxsSession);
+
+ reporter.log('Now using one guest session for all tests ...');
+
+ #
+ # Second batch: One session for *all* guest processes.
+ #
+ oGuest = oSession.o.console.guest;
+ try:
+ reporter.log('Creating session for all tests ...');
+ curGuestSession = oGuest.createSession(sUser, sPassword, '', 'testGuestCtrlExec: One session for all tests');
+ try:
+ fWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start ];
+ waitResult = curGuestSession.waitForArray(fWaitFor, 30 * 1000);
+ if waitResult != vboxcon.GuestSessionWaitResult_Start \
+ and waitResult != vboxcon.GuestSessionWaitResult_WaitFlagNotSupported:
+ reporter.error('Session did not start successfully, returned wait result: %d' \
+ % (waitResult));
+ return (False, oTxsSession);
+ reporter.log('Session successfully started');
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('Waiting for guest session to start failed:');
+ return (False, oTxsSession);
+ # Note: Not waiting for the guest session to start here
+ # is intentional. This must be handled by the process execution
+ # call then.
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResultExec
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc = self.gctrlExecDoTest(i, curTest, curRes, curGuestSession);
+ if fRc is False:
+ break;
+ try:
+ reporter.log2('Closing guest session ...');
+ curGuestSession.close();
+ curGuestSession = None;
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('Closing guest session failed:');
+ fRc = False;
+ except:
+ reporter.logXcpt('Could not create one session:');
+
+ # No sessions left?
+ if fRc is True:
+ cSessions = len(self.oTstDrv.oVBoxMgr.getArray(oSession.o.console.guest, 'sessions'));
+ if cSessions is not 0:
+ reporter.error('Found %d stale session(s), expected 0' % (cSessions,));
+ fRc = False;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlExecErrorLevel(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests handling of error levels from started guest processes.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ if oTestVm.isWindows():
+ # Outputting stuff.
+ sImage = "C:\\windows\\system32\\cmd.exe";
+ else:
+ reporter.error('Implement me!'); ## @todo Implement non-Windows bits.
+ return (False, oTxsSession);
+
+ aaTests = [];
+ if oTestVm.isWindows():
+ aaTests.extend([
+ # Simple.
+ [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'wrongcommand' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'exit', '22' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 22) ],
+ [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'set', 'ERRORLEVEL=234' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 0) ],
+ [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'echo', '%WINDIR%' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 0) ],
+ [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'set', 'ERRORLEVEL=0' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 0) ],
+ [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\windows\\system32' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 0) ],
+ [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\windows\\system32\\kernel32.dll' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 0) ],
+ [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\nonexisting-file' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 1) ],
+ [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\nonexisting-dir\\' ],
+ sUser = sUser, sPassword = sPassword),
+ tdTestResultExec(fRc = True, iExitCode = 1) ]
+ # FIXME: Failing tests.
+ # With stdout.
+ # [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\windows\\system32' ],
+ # sUser = sUser, sPassword = sPassword, aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut ]),
+ # tdTestResultExec(fRc = True, iExitCode = 0) ],
+ # [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\nonexisting-file' ],
+ # sUser = sUser, sPassword = sPassword, aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut ]),
+ # tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\nonexisting-dir\\' ],
+ # sUser = sUser, sPassword = sPassword, aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut ]),
+ # tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # With stderr.
+ # [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\windows\\system32' ],
+ # sUser = sUser, sPassword = sPassword, aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, iExitCode = 0) ],
+ # [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\nonexisting-file' ],
+ # sUser = sUser, sPassword = sPassword, aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\nonexisting-dir\\' ],
+ # sUser = sUser, sPassword = sPassword, aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # With stdout/stderr.
+ # [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\windows\\system32' ],
+ # sUser = sUser, sPassword = sPassword,
+ # aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, iExitCode = 0) ],
+ # [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\nonexisting-file' ],
+ # sUser = sUser, sPassword = sPassword,
+ # aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, iExitCode = 1) ],
+ # [ tdTestExec(sCmd = sImage, aArgs = [ sImage, '/C', 'dir', 'c:\\nonexisting-dir\\' ],
+ # sUser = sUser, sPassword = sPassword,
+ # aFlags = [ vboxcon.ProcessCreateFlag_WaitForStdOut, vboxcon.ProcessCreateFlag_WaitForStdErr ]),
+ # tdTestResultExec(fRc = True, iExitCode = 1) ]
+ ## @todo Test stdin!
+ ]);
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlExecErrorLevel: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ fRc = self.gctrlExecDoTest(i, curTest, curRes, curGuestSession);
+ curTest.closeSession();
+ if fRc is False:
+ break;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlExecTimeout(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests handling of timeouts of started guest processes.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+ sDomain = "";
+
+ if oTestVm.isWindows():
+ # Outputting stuff.
+ sImage = "C:\\windows\\system32\\cmd.exe";
+ else:
+ reporter.error('Implement me!'); ## @todo Implement non-Windows bits.
+ return (False, oTxsSession);
+
+ fRc = True;
+ try:
+ oGuest = oSession.o.console.guest;
+ oGuestSession = oGuest.createSession(sUser, sPassword, sDomain, "testGuestCtrlExecTimeout");
+ oGuestSession.waitForArray([ vboxcon.GuestSessionWaitForFlag_Start ], 30 * 1000);
+ # Create a process which never terminates and should timeout when
+ # waiting for termination.
+ try:
+ curProc = oGuestSession.processCreate(sImage, [sImage,] if self.oTstDrv.fpApiVer >= 5.0 else [], \
+ [], [], 30 * 1000);
+ reporter.log('Waiting for process 1 being started ...');
+ waitRes = curProc.waitForArray([ vboxcon.ProcessWaitForFlag_Start ], 30 * 1000);
+ if waitRes != vboxcon.ProcessWaitResult_Start:
+ reporter.error('Waiting for process 1 to start failed, got status %d');
+ fRc = False;
+ if fRc:
+ reporter.log('Waiting for process 1 to time out within 1ms ...');
+ waitRes = curProc.waitForArray([ vboxcon.ProcessWaitForFlag_Terminate ], 1);
+ if waitRes != vboxcon.ProcessWaitResult_Timeout:
+ reporter.error('Waiting for process 1 did not time out when it should (1)');
+ fRc = False;
+ else:
+ reporter.log('Waiting for process 1 timed out (1), good');
+ if fRc:
+ reporter.log('Waiting for process 1 to time out within 5000ms ...');
+ waitRes = curProc.waitForArray([ vboxcon.ProcessWaitForFlag_Terminate ], 5000);
+ if waitRes != vboxcon.ProcessWaitResult_Timeout:
+ reporter.error('Waiting for process 1 did not time out when it should, got wait result %d' % (waitRes,));
+ fRc = False;
+ else:
+ reporter.log('Waiting for process 1 timed out (5000), good');
+ ## @todo Add curProc.terminate() as soon as it's implemented.
+ except:
+ reporter.errorXcpt('Exception for process 1:');
+ fRc = False;
+ # Create a lengthly running guest process which will be killed by VBoxService on the
+ # guest because it ran out of execution time (5 seconds).
+ if fRc:
+ try:
+ curProc = oGuestSession.processCreate(sImage, [sImage,] if self.oTstDrv.fpApiVer >= 5.0 else [], \
+ [], [], 5 * 1000);
+ reporter.log('Waiting for process 2 being started ...');
+ waitRes = curProc.waitForArray([ vboxcon.ProcessWaitForFlag_Start ], 30 * 1000);
+ if waitRes != vboxcon.ProcessWaitResult_Start:
+ reporter.error('Waiting for process 1 to start failed, got status %d');
+ fRc = False;
+ if fRc:
+ reporter.log('Waiting for process 2 to get killed because it ran out of execution time ...');
+ waitRes = curProc.waitForArray([ vboxcon.ProcessWaitForFlag_Terminate ], 30 * 1000);
+ if waitRes != vboxcon.ProcessWaitResult_Timeout:
+ reporter.error('Waiting for process 2 did not time out when it should, got wait result %d' \
+ % (waitRes,));
+ fRc = False;
+ if fRc:
+ reporter.log('Waiting for process 2 indicated an error, good');
+ if curProc.status != vboxcon.ProcessStatus_TimedOutKilled:
+ reporter.error('Status of process 2 wrong; excepted %d, got %d' \
+ % (vboxcon.ProcessStatus_TimedOutKilled, curProc.status));
+ fRc = False;
+ else:
+ reporter.log('Status of process 2 correct (%d)' % (vboxcon.ProcessStatus_TimedOutKilled,));
+ ## @todo Add curProc.terminate() as soon as it's implemented.
+ except:
+ reporter.errorXcpt('Exception for process 2:');
+ fRc = False;
+ oGuestSession.close();
+ except:
+ reporter.errorXcpt('Could not handle session:');
+ fRc = False;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlDirCreate(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests creation of guest directories.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ if oTestVm.isWindows():
+ sScratch = "C:\\Temp\\vboxtest\\testGuestCtrlDirCreate\\";
+
+ aaTests = [];
+ if oTestVm.isWindows():
+ aaTests.extend([
+ # Invalid stuff.
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword, sDirectory = '' ),
+ tdTestResult(fRc = False) ],
+ # More unusual stuff.
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword, sDirectory = '..\\..\\' ),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword, sDirectory = '../../' ),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword, sDirectory = 'z:\\' ),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword, sDirectory = '\\\\uncrulez\\foo' ),
+ tdTestResult(fRc = False) ],
+ # Creating directories.
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword, sDirectory = sScratch ),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword, sDirectory = os.path.join(sScratch, 'foo\\bar\\baz'),
+ aFlags = [ vboxcon.DirectoryCreateFlag_Parents ] ),
+ tdTestResult(fRc = True) ],
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword, sDirectory = os.path.join(sScratch, 'foo\\bar\\baz'),
+ aFlags = [ vboxcon.DirectoryCreateFlag_Parents ] ),
+ tdTestResult(fRc = True) ],
+ # Long (+ random) stuff.
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword,
+ sDirectory = os.path.join(sScratch,
+ "".join(random.choice(string.ascii_lowercase) for i in range(32))) ),
+ tdTestResult(fRc = True) ],
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword,
+ sDirectory = os.path.join(sScratch,
+ "".join(random.choice(string.ascii_lowercase) for i in range(128))) ),
+ tdTestResult(fRc = True) ],
+ # Following two should fail on Windows (paths too long). Both should timeout.
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword,
+ sDirectory = os.path.join(sScratch,
+ "".join(random.choice(string.ascii_lowercase) for i in range(255))) ),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreate(sUser = sUser, sPassword = sPassword,
+ sDirectory = os.path.join(sScratch,
+ "".join(random.choice(string.ascii_lowercase) for i in range(1024)))
+ ),
+ tdTestResult(fRc = False) ]
+ ]);
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ reporter.log('Testing #%d, sDirectory="%s" ...' % (i, curTest.sDirectory));
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlDirCreate: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ fRc = self.gctrlCreateDir(curTest, curRes, curGuestSession);
+ curTest.closeSession();
+ if fRc is False:
+ reporter.error('Test #%d failed' % (i,));
+ fRc = False;
+ break;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlDirCreateTemp(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests creation of temporary directories.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ # if oTestVm.isWindows():
+ # sScratch = "C:\\Temp\\vboxtest\\testGuestCtrlDirCreateTemp\\";
+
+ aaTests = [];
+ if oTestVm.isWindows():
+ aaTests.extend([
+ # Invalid stuff.
+ [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sDirectory = ''),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sDirectory = 'C:\\Windows',
+ fMode = 1234),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = '',
+ sDirectory = 'C:\\Windows', fMode = 1234),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'xXx',
+ sDirectory = 'C:\\Windows', fMode = 0o700),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'xxx',
+ sDirectory = 'C:\\Windows', fMode = 0o700),
+ tdTestResult(fRc = False) ],
+ # More unusual stuff.
+ [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'foo',
+ sDirectory = 'z:\\'),
+ tdTestResult(fRc = False) ],
+ [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'foo',
+ sDirectory = '\\\\uncrulez\\foo'),
+ tdTestResult(fRc = False) ],
+ # Non-existing stuff.
+ [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'bar',
+ sDirectory = 'c:\\Apps\\nonexisting\\foo'),
+ tdTestResult(fRc = False) ],
+ # FIXME: Failing test. Non Windows path
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'bar',
+ # sDirectory = '/tmp/non/existing'),
+ # tdTestResult(fRc = False) ]
+ ]);
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ # FIXME: Failing tests.
+ # aaTests.extend([
+ # Non-secure variants.
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'X',
+ # sDirectory = sScratch),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'X',
+ # sDirectory = sScratch),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch,
+ # fMode = 0o700),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch,
+ # fMode = 0o700),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch,
+ # fMode = 0o755),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch,
+ # fMode = 0o755),
+ # tdTestResult(fRc = True) ],
+ # Secure variants.
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch, fSecure = True),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch, fSecure = True),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch, fSecure = True),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch, fSecure = True),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch,
+ # fSecure = True, fMode = 0o700),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch,
+ # fSecure = True, fMode = 0o700),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch,
+ # fSecure = True, fMode = 0o755),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = 'XXX',
+ # sDirectory = sScratch,
+ # fSecure = True, fMode = 0o755),
+ # tdTestResult(fRc = True) ],
+ # Random stuff.
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword,
+ # sTemplate = "XXX-".join(random.choice(string.ascii_lowercase) for i in range(32)),
+ # sDirectory = sScratch,
+ # fSecure = True, fMode = 0o755),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = "".join('X' for i in range(32)),
+ # sDirectory = sScratch,
+ # fSecure = True, fMode = 0o755),
+ # tdTestResult(fRc = True) ],
+ # [ tdTestDirCreateTemp(sUser = sUser, sPassword = sPassword, sTemplate = "".join('X' for i in range(128)),
+ # sDirectory = sScratch,
+ # fSecure = True, fMode = 0o755),
+ # tdTestResult(fRc = True) ]
+ # ]);
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ reporter.log('Testing #%d, sTemplate="%s", fMode=%#o, path="%s", secure="%s" ...' %
+ (i, curTest.sTemplate, curTest.fMode, curTest.sDirectory, curTest.fSecure));
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlDirCreateTemp: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ sDirTemp = "";
+ try:
+ sDirTemp = curGuestSession.directoryCreateTemp(curTest.sTemplate, curTest.fMode,
+ curTest.sDirectory, curTest.fSecure);
+ except:
+ if curRes.fRc is True:
+ reporter.errorXcpt('Creating temp directory "%s" failed:' % (curTest.sDirectory,));
+ fRc = False;
+ break;
+ else:
+ reporter.logXcpt('Creating temp directory "%s" failed expectedly, skipping:' % (curTest.sDirectory,));
+ curTest.closeSession();
+ if sDirTemp != "":
+ reporter.log2('Temporary directory is: %s' % (sDirTemp,));
+ if self.oTstDrv.fpApiVer >= 5.0:
+ fExists = curGuestSession.directoryExists(sDirTemp, False);
+ else:
+ fExists = curGuestSession.directoryExists(sDirTemp);
+ if fExists is False:
+ reporter.error('Test #%d failed: Temporary directory "%s" does not exists' % (i, sDirTemp));
+ fRc = False;
+ break;
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlDirRead(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests opening and reading (enumerating) guest directories.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ aaTests = [];
+ if oTestVm.isWindows():
+ aaTests.extend([
+ # Invalid stuff.
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = ''),
+ tdTestResultDirRead(fRc = False) ],
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = 'C:\\Windows', aFlags = [ 1234 ]),
+ tdTestResultDirRead(fRc = False) ],
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = 'C:\\Windows', sFilter = '*.foo'),
+ tdTestResultDirRead(fRc = False) ],
+ # More unusual stuff.
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = 'z:\\'),
+ tdTestResultDirRead(fRc = False) ],
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = '\\\\uncrulez\\foo'),
+ tdTestResultDirRead(fRc = False) ],
+ # Non-existing stuff.
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = 'c:\\Apps\\nonexisting'),
+ tdTestResultDirRead(fRc = False) ],
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = 'c:\\Apps\\testDirRead'),
+ tdTestResultDirRead(fRc = False) ]
+ ]);
+
+ if oTestVm.sVmName == 'tst-xppro':
+ aaTests.extend([
+ # Reading directories.
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = '../../Windows/Fonts'),
+ tdTestResultDirRead(fRc = True, numFiles = 191) ],
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = 'c:\\Windows\\Help'),
+ tdTestResultDirRead(fRc = True, numDirs = 13, numFiles = 569) ],
+ [ tdTestDirRead(sUser = sUser, sPassword = sPassword, sDirectory = 'c:\\Windows\\Web'),
+ tdTestResultDirRead(fRc = True, numDirs = 3, numFiles = 55) ]
+ ]);
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ reporter.log('Testing #%d, dir="%s" ...' % (i, curTest.sDirectory));
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlDirRead: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ (fRc2, cDirs, cFiles) = self.gctrlReadDir(curTest, curRes, curGuestSession);
+ curTest.closeSession();
+ reporter.log2('Test #%d: Returned %d directories, %d files total' % (i, cDirs, cFiles));
+ if fRc2 is curRes.fRc:
+ if fRc2 is True:
+ if curRes.numFiles != cFiles:
+ reporter.error('Test #%d failed: Got %d files, expected %d' % (i, cFiles, curRes.numFiles));
+ fRc = False;
+ break;
+ if curRes.numDirs != cDirs:
+ reporter.error('Test #%d failed: Got %d directories, expected %d' % (i, cDirs, curRes.numDirs));
+ fRc = False;
+ break;
+ else:
+ reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc2, curRes.fRc));
+ fRc = False;
+ break;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlFileRemove(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests removing guest files.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ aaTests = [];
+ if oTestVm.isWindows():
+ aaTests.extend([
+ # Invalid stuff.
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = ''),
+ tdTestResult(fRc = False) ],
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'C:\\Windows'),
+ tdTestResult(fRc = False) ],
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'C:\\Windows'),
+ tdTestResult(fRc = False) ],
+ # More unusual stuff.
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'z:\\'),
+ tdTestResult(fRc = False) ],
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = '\\\\uncrulez\\foo'),
+ tdTestResult(fRc = False) ],
+ # Non-existing stuff.
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'c:\\Apps\\nonexisting'),
+ tdTestResult(fRc = False) ],
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'c:\\Apps\\testFileRemove'),
+ tdTestResult(fRc = False) ],
+ # Try to delete system files.
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'c:\\pagefile.sys'),
+ tdTestResult(fRc = False) ],
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'c:\\Windows\\kernel32.sys'),
+ tdTestResult(fRc = False) ]
+ ]);
+
+ if oTestVm.sVmName == 'tst-xppro':
+ aaTests.extend([
+ # Try delete some unimportant media stuff.
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'c:\\Windows\\Media\\chimes.wav'),
+ tdTestResult(fRc = True) ],
+ # Second attempt should fail.
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'c:\\Windows\\Media\\chimes.wav'),
+ tdTestResult(fRc = False) ],
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'c:\\Windows\\Media\\chord.wav'),
+ tdTestResult(fRc = True) ],
+ [ tdTestFileRemove(sUser = sUser, sPassword = sPassword, sFile = 'c:\\Windows\\Media\\chord.wav'),
+ tdTestResult(fRc = False) ]
+ ]);
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ reporter.log('Testing #%d, file="%s" ...' % (i, curTest.sFile));
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlFileRemove: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ curGuestSession.fsObjRemove(curTest.sFile);
+ else:
+ curGuestSession.fileRemove(curTest.sFile);
+ except:
+ if curRes.fRc is True:
+ reporter.errorXcpt('Removing file "%s" failed:' % (curTest.sFile,));
+ fRc = False;
+ break;
+ else:
+ reporter.logXcpt('Removing file "%s" failed expectedly, skipping:' % (curTest.sFile,));
+ curTest.closeSession();
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlFileStat(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests querying file information through stat.
+ """
+
+ # Basic stuff, existing stuff.
+ aoTests = [
+ tdTestSessionEx([ tdStepStatDir('.'),
+ tdStepStatDir('..'),
+ ]),
+ ];
+ if oTestVm.isWindows():
+ aoTests += [ tdTestSessionEx([ tdStepStatDir('C:\\Windows'),
+ tdStepStatDir('C:\\Windows\\System32'),
+ tdStepStatDir('C:\\Windows\\System32\\'),
+ tdStepStatDir('C:\\Windows\\System32\\.'),
+ tdStepStatDir('C:\\Windows\\System32\\.\\'),
+ tdStepStatDir('C:\\Windows\\System32\\..'),
+ tdStepStatDir('C:\\Windows\\System32\\..\\'),
+ tdStepStatDir('C:\\Windows\\System32\\..\\\\'),
+ tdStepStatDir('C:\\Windows\\System32\\\\..\\\\'),
+ tdStepStatDir('C:/Windows/System32'),
+ tdStepStatDir('C:/Windows/System32/'),
+ tdStepStatDir('c:/winDowS/sYsTeM32/'),
+ tdStepStatDir('C:/Windows/System32/.'),
+ tdStepStatDir('C:/Windows/System32/./'),
+ tdStepStatDir('C:/Windows/System32/..'),
+ tdStepStatDir('C:/Windows/System32/../'),
+ tdStepStatDir('C:/Windows/System32/..//'),
+ tdStepStatDir('C:/Windows/System32//..//'),
+ tdStepStatFile('C:\\Windows\\System32\\kernel32.dll'),
+ tdStepStatFile('C:/Windows/System32/kernel32.dll')
+ ]) ];
+ elif oTestVm.isOS2():
+ aoTests += [ tdTestSessionEx([ tdStepStatDir('C:\\OS2'),
+ tdStepStatDir('C:\\OS2\\DLL'),
+ tdStepStatDir('C:\\OS2\\DLL\\'),
+ tdStepStatDir('C:/OS2/DLL'),
+ tdStepStatDir('c:/OS2/DLL'),
+ tdStepStatDir('c:/OS2/DLL/'),
+ tdStepStatFile('C:\\CONFIG.SYS'),
+ tdStepStatFile('C:\\OS2\\DLL\\DOSCALL1.DLL'),
+ ]) ];
+ else: # generic unix.
+ aoTests += [ tdTestSessionEx([ tdStepStatDir('/'),
+ tdStepStatDir('///'),
+ tdStepStatDir('/usr/bin/.'),
+ tdStepStatDir('/usr/bin/./'),
+ tdStepStatDir('/usr/bin/..'),
+ tdStepStatDir('/usr/bin/../'),
+ tdStepStatFile('/bin/ls'),
+ tdStepStatFile('/bin/cp'),
+ tdStepStatFile('/bin/date'),
+ ]) ];
+ # None existing stuff.
+ if oTestVm.isWindows() or oTestVm.isOS2():
+ aoTests += [ tdTestSessionEx([ tdStepStatFileNotFound('C:\\NoSuchFileOrDirectory', ),
+ tdStepStatPathNotFound('C:\\NoSuchDirectory\\'),
+ tdStepStatPathNotFound('C:/NoSuchDirectory/'),
+ tdStepStatPathNotFound('C:\\NoSuchDirectory\\.'),
+ tdStepStatPathNotFound('C:/NoSuchDirectory/.'),
+ tdStepStatPathNotFound('C:\\NoSuchDirectory\\NoSuchFileOrDirectory'),
+ tdStepStatPathNotFound('C:/NoSuchDirectory/NoSuchFileOrDirectory'),
+ tdStepStatPathNotFound('C:/NoSuchDirectory/NoSuchFileOrDirectory/'),
+ tdStepStatPathNotFound('N:\\'), # ASSUMES nothing mounted on N:!
+ tdStepStatPathNotFound('\\\\NoSuchUncServerName\\NoSuchShare'),
+ ]) ];
+ else: # generic unix.
+ aoTests += [ tdTestSessionEx([ tdStepStatFileNotFound('/NoSuchFileOrDirectory', ),
+ tdStepStatFileNotFound('/bin/NoSuchFileOrDirectory'),
+ tdStepStatPathNotFound('/NoSuchDirectory/'),
+ tdStepStatPathNotFound('/NoSuchDirectory/.'),
+ ]) ];
+ # Invalid parameter check.
+ aoTests += [ tdTestSessionEx([ tdStepStat('', vbox.ComError.E_INVALIDARG), ]), ];
+
+ # Some test VM specific tests.
+ if oTestVm.sVmName == 'tst-xppro':
+ aoTests += [ tdTestSessionEx([ tdStepStatFileSize('c:\\Windows\\system32\\kernel32.dll', 926720), ]) ];
+
+ #
+ # Execute the tests.
+ #
+ return tdTestSessionEx.executeListTestSessions(aoTests, self.oTstDrv, oSession, oTxsSession, oTestVm, 'FsStat');
+
+ def testGuestCtrlFileRead(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests reading from guest files.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ if oTxsSession.syncMkDir('${SCRATCH}/testGuestCtrlFileRead') is False:
+ reporter.error('Could not create scratch directory on guest');
+ return (False, oTxsSession);
+
+ aaTests = [];
+ aaTests.extend([
+ # Invalid stuff.
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, cbToReadWrite = 0),
+ tdTestResultFileReadWrite(fRc = False) ],
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = ''),
+ tdTestResultFileReadWrite(fRc = False) ],
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = 'non-existing.file'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ # Wrong open mode.
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = 'non-existing.file', \
+ sOpenMode = 'rt', sDisposition = 'oe'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = '\\\\uncrulez\\non-existing.file', \
+ sOpenMode = 'tr', sDisposition = 'oe'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = '../../non-existing.file', \
+ sOpenMode = 'wr', sDisposition = 'oe'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ # Wrong disposition.
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = 'non-existing.file', \
+ sOpenMode = 'r', sDisposition = 'e'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = '\\\\uncrulez\\non-existing.file', \
+ sOpenMode = 'r', sDisposition = 'o'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = '../../non-existing.file', \
+ sOpenMode = 'r', sDisposition = 'c'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ # Opening non-existing file when it should exist.
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = 'non-existing.file', \
+ sOpenMode = 'r', sDisposition = 'oe'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = '\\\\uncrulez\\non-existing.file', \
+ sOpenMode = 'r', sDisposition = 'oe'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = '../../non-existing.file', \
+ sOpenMode = 'r', sDisposition = 'oe'),
+ tdTestResultFileReadWrite(fRc = False) ]
+ ]);
+
+ if oTestVm.isWindows():
+ aaTests.extend([
+ # Create a file which must not exist (but it hopefully does).
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = 'C:\\Windows\\System32\\calc.exe', \
+ sOpenMode = 'w', sDisposition = 'ce'),
+ tdTestResultFileReadWrite(fRc = False) ],
+ # Open a file which must exist.
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = 'C:\\Windows\\System32\\kernel32.dll', \
+ sOpenMode = 'r', sDisposition = 'oe'),
+ tdTestResultFileReadWrite(fRc = True) ],
+ # Try truncating a file which already is opened with a different sharing mode (and thus should fail).
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = 'C:\\Windows\\System32\\kernel32.dll', \
+ sOpenMode = 'w', sDisposition = 'ot'),
+ tdTestResultFileReadWrite(fRc = False) ]
+ ]);
+
+ if oTestVm.sKind == "WindowsXP":
+ aaTests.extend([
+ # Reading from beginning.
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = 'C:\\Windows\\System32\\eula.txt', \
+ sOpenMode = 'r', sDisposition = 'oe', cbToReadWrite = 33),
+ tdTestResultFileReadWrite(fRc = True, aBuf = 'Microsoft Windows XP Professional', \
+ cbProcessed = 33, cbOffset = 33) ],
+ # Reading from offset.
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = 'C:\\Windows\\System32\\eula.txt', \
+ sOpenMode = 'r', sDisposition = 'oe', cbOffset = 17782, cbToReadWrite = 26),
+ tdTestResultFileReadWrite(fRc = True, aBuf = 'LINKS TO THIRD PARTY SITES', \
+ cbProcessed = 26, cbOffset = 17782 + 26) ]
+ ]);
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestFileReadWrite, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ reporter.log('Testing #%d, sFile="%s", cbToReadWrite=%d, sOpenMode="%s", sDisposition="%s", cbOffset=%d ...' % \
+ (i, curTest.sFile, curTest.cbToReadWrite, curTest.sOpenMode, curTest.sDisposition, curTest.cbOffset));
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlFileRead: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ try:
+ if curTest.cbOffset > 0: # The offset parameter is gone.
+ if self.oTstDrv.fpApiVer >= 5.0:
+ curFile = curGuestSession.fileOpenEx(curTest.sFile, curTest.getAccessMode(), curTest.getOpenAction(),
+ curTest.getSharingMode(), curTest.lCreationMode, []);
+ curFile.seek(curTest.cbOffset, vboxcon.FileSeekOrigin_Begin);
+ else:
+ curFile = curGuestSession.fileOpenEx(curTest.sFile, curTest.sOpenMode, curTest.sDisposition, \
+ curTest.sSharingMode, curTest.lCreationMode, curTest.cbOffset);
+ curOffset = long(curFile.offset);
+ resOffset = long(curTest.cbOffset);
+ if curOffset != resOffset:
+ reporter.error('Test #%d failed: Initial offset on open does not match: Got %d, expected %d' \
+ % (i, curOffset, resOffset));
+ fRc = False;
+ else:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ curFile = curGuestSession.fileOpen(curTest.sFile, curTest.getAccessMode(), curTest.getOpenAction(),
+ curTest.lCreationMode);
+ else:
+ curFile = curGuestSession.fileOpen(curTest.sFile, curTest.sOpenMode, curTest.sDisposition, \
+ curTest.lCreationMode);
+ if fRc \
+ and curTest.cbToReadWrite > 0:
+ ## @todo Split this up in 64K reads. Later.
+ ## @todo Test timeouts.
+ aBufRead = curFile.read(curTest.cbToReadWrite, 30 * 1000);
+ if curRes.cbProcessed > 0 \
+ and curRes.cbProcessed is not len(aBufRead):
+ reporter.error('Test #%d failed: Read buffer length does not match: Got %d, expected %d' \
+ % (i, len(aBufRead), curRes.cbProcessed));
+ fRc = False;
+ if fRc:
+ if curRes.aBuf is not None \
+ and bytes(curRes.aBuf) != bytes(aBufRead):
+ reporter.error('Test #%d failed: Got buffer\n%s (%d bytes), expected\n%s (%d bytes)' \
+ % (i, map(hex, map(ord, aBufRead)), len(aBufRead), \
+ map(hex, map(ord, curRes.aBuf)), len(curRes.aBuf)));
+ reporter.error('Test #%d failed: Got buffer\n%s, expected\n%s' \
+ % (i, aBufRead, curRes.aBuf));
+ fRc = False;
+ # Test final offset.
+ curOffset = long(curFile.offset);
+ resOffset = long(curRes.cbOffset);
+ if curOffset != resOffset:
+ reporter.error('Test #%d failed: Final offset does not match: Got %d, expected %d' \
+ % (i, curOffset, resOffset));
+ fRc = False;
+ curFile.close();
+ except:
+ reporter.logXcpt('Opening "%s" failed:' % (curTest.sFile,));
+ fRc = False;
+
+ curTest.closeSession();
+
+ if fRc != curRes.fRc:
+ reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc, curRes.fRc));
+ fRc = False;
+ break;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlFileWrite(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests writing to guest files.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ if oTestVm.isWindows():
+ sScratch = "C:\\Temp\\vboxtest\\testGuestCtrlFileWrite\\";
+
+ if oTxsSession.syncMkDir('${SCRATCH}/testGuestCtrlFileWrite') is False:
+ reporter.error('Could not create scratch directory on guest');
+ return (False, oTxsSession);
+
+ aaTests = [];
+
+ cScratchBuf = 512;
+ aScratchBuf = array('b', [random.randint(-128, 127) for i in range(cScratchBuf)]);
+ aaTests.extend([
+ # Write to a non-existing file.
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = sScratch + 'testGuestCtrlFileWrite.txt', \
+ sOpenMode = 'w+', sDisposition = 'ce', cbToReadWrite = cScratchBuf,
+ aBuf = aScratchBuf),
+ tdTestResultFileReadWrite(fRc = True, aBuf = aScratchBuf, \
+ cbProcessed = cScratchBuf, cbOffset = cScratchBuf) ]
+ ]);
+
+ aScratchBuf2 = array('b', [random.randint(-128, 127) for i in range(cScratchBuf)]);
+ aaTests.extend([
+ # Append the same amount of data to the just created file.
+ [ tdTestFileReadWrite(sUser = sUser, sPassword = sPassword, sFile = sScratch + 'testGuestCtrlFileWrite.txt', \
+ sOpenMode = 'w+', sDisposition = 'oa', cbToReadWrite = cScratchBuf,
+ cbOffset = cScratchBuf, aBuf = aScratchBuf2),
+ tdTestResultFileReadWrite(fRc = True, aBuf = aScratchBuf2, \
+ cbProcessed = cScratchBuf, cbOffset = cScratchBuf * 2) ],
+ ]);
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestFileReadWrite, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ reporter.log('Testing #%d, sFile="%s", cbToReadWrite=%d, sOpenMode="%s", sDisposition="%s", cbOffset=%d ...' %
+ (i, curTest.sFile, curTest.cbToReadWrite, curTest.sOpenMode, curTest.sDisposition, curTest.cbOffset));
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlFileWrite: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+
+ try:
+ if curTest.cbOffset > 0: # The offset parameter is gone.
+ if self.oTstDrv.fpApiVer >= 5.0:
+ curFile = curGuestSession.fileOpenEx(curTest.sFile, curTest.getAccessMode(), curTest.getOpenAction(),
+ curTest.getSharingMode(), []);
+ curFile.seek(curTest.cbOffset, vboxcon.FileSeekOrigin_Begin);
+ else:
+ curFile = curGuestSession.fileOpenEx(curTest.sFile, curTest.sOpenMode, curTest.sDisposition,
+ curTest.sSharingMode, curTest.lCreationMode, curTest.cbOffset);
+ curOffset = long(curFile.offset);
+ resOffset = long(curTest.cbOffset);
+ if curOffset != resOffset:
+ reporter.error('Test #%d failed: Initial offset on open does not match: Got %d, expected %d'
+ % (i, curOffset, resOffset));
+ fRc = False;
+ else:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ curFile = curGuestSession.fileOpen(curTest.sFile, curTest.getAccessMode(), curTest.getOpenAction(),
+ curTest.lCreationMode);
+ else:
+ curFile = curGuestSession.fileOpen(curTest.sFile, curTest.sOpenMode, curTest.sDisposition,
+ curTest.lCreationMode);
+ if fRc and curTest.cbToReadWrite > 0:
+ ## @todo Split this up in 64K writes. Later.
+ ## @todo Test timeouts.
+ cBytesWritten = curFile.write(curTest.aBuf, 30 * 1000);
+ if curRes.cbProcessed > 0 \
+ and curRes.cbProcessed != cBytesWritten:
+ reporter.error('Test #%d failed: Written buffer length does not match: Got %d, expected %d'
+ % (i, cBytesWritten, curRes.cbProcessed));
+ fRc = False;
+ if fRc:
+ # Verify written content by seeking back to the initial offset and
+ # re-read & compare the written data.
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ curFile.seek(-(curTest.cbToReadWrite), vboxcon.FileSeekOrigin_Current);
+ else:
+ curFile.seek(-(curTest.cbToReadWrite), vboxcon.FileSeekType_Current);
+ except:
+ reporter.logXcpt('Seeking back to initial write position failed:');
+ fRc = False;
+ if fRc and long(curFile.offset) != curTest.cbOffset:
+ reporter.error('Test #%d failed: Initial write position does not match current position, '
+ 'got %d, expected %d' % (i, long(curFile.offset), curTest.cbOffset));
+ fRc = False;
+ if fRc:
+ aBufRead = curFile.read(curTest.cbToReadWrite, 30 * 1000);
+ if len(aBufRead) != curTest.cbToReadWrite:
+ reporter.error('Test #%d failed: Got buffer length %d, expected %d'
+ % (i, len(aBufRead), curTest.cbToReadWrite));
+ fRc = False;
+ if fRc \
+ and curRes.aBuf is not None \
+ and curRes.aBuf != aBufRead:
+ reporter.error('Test #%d failed: Got buffer\n%s, expected\n%s'
+ % (i, aBufRead, curRes.aBuf));
+ fRc = False;
+ # Test final offset.
+ curOffset = long(curFile.offset);
+ resOffset = long(curRes.cbOffset);
+ if curOffset != resOffset:
+ reporter.error('Test #%d failed: Final offset does not match: Got %d, expected %d'
+ % (i, curOffset, resOffset));
+ fRc = False;
+ curFile.close();
+ except:
+ reporter.logXcpt('Opening "%s" failed:' % (curTest.sFile,));
+ fRc = False;
+
+ curTest.closeSession();
+
+ if fRc != curRes.fRc:
+ reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc, curRes.fRc));
+ fRc = False;
+ break;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlCopyTo(self, oSession, oTxsSession, oTestVm):
+ """
+ Tests copying files from host to the guest.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ sScratchGst = "C:\\Temp\\vboxtest\\testGuestCtrlCopyTo\\";
+ sScratchGstNotExist = "C:\\does-not-exist\\";
+ sScratchGstInvalid = "?*|invalid-name?*|";
+ else:
+ sUser = "vbox";
+ sScratchGst = "/tmp/testGuestCtrlCopyTo/";
+ sScratchGstNotExist = "/tmp/does-not-exist/";
+ sScratchGstInvalid = "/";
+ sPassword = "password";
+
+ if oTxsSession.syncMkDir('${SCRATCH}/testGuestCtrlCopyTo') is False:
+ reporter.error('Could not create scratch directory on guest');
+ return (False, oTxsSession);
+
+ ## @todo r=klaus It's not good to use files with unpredictable size
+ # for testing. Causes all sorts of weird failures as things grow,
+ # exceeding the free space of the test VMs. Especially as this used
+ # the very big (and quickly growing) validation kit ISO originally.
+ sTestFileBig = self.oTstDrv.getFullResourceName('5.3/guestctrl/50mb_rnd.dat');
+ if not os.path.isfile(sTestFileBig):
+ sTestFileBig = self.oTstDrv.getGuestAdditionsIso();
+ if sTestFileBig == '' or not os.path.isfile(sTestFileBig):
+ sTestFileBig = self.oTstDrv.sVBoxValidationKitIso;
+ if os.path.isfile(sTestFileBig):
+ reporter.log('Test file for big copy found at: %s' % (sTestFileBig,));
+ else:
+ reporter.log('Warning: Test file for big copy not found -- some tests might fail');
+
+ aaTests = [];
+ if oTestVm.isWindows():
+ aaTests.extend([
+ # Destination missing.
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = ''),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = '/placeholder',
+ aFlags = [ 80 ] ),
+ tdTestResult(fRc = False) ],
+ # Source missing.
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sDst = ''),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sDst = '/placeholder',
+ aFlags = [ 80 ] ),
+ tdTestResult(fRc = False) ],
+ # Testing DirectoryCopyFlag flags.
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = sScratchGstInvalid, aFlags = [ 80 ] ),
+ tdTestResult(fRc = False) ],
+ # Testing FileCopyFlag flags.
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = sScratchGstInvalid, aFlags = [ 80 ] ),
+ tdTestResult(fRc = False) ],
+ # Nothing to copy (source and/or destination is empty).
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = 'z:\\'),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = '\\\\uncrulez\\foo'),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = 'non-exist',
+ sDst = os.path.join(sScratchGst, 'non-exist.dll')),
+ tdTestResult(fRc = False) ]
+ ]);
+
+ #
+ # Single file handling.
+ #
+ if self.oTstDrv.fpApiVer > 5.2:
+ aaTests.extend([
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = sScratchGstInvalid),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = sScratchGstNotExist),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = sScratchGstNotExist),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = os.path.join(sScratchGstNotExist, 'renamedfile.dll')),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = os.path.join(sScratchGst, 'HostGABig.dat')),
+ tdTestResult(fRc = True) ],
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = os.path.join(sScratchGst, 'HostGABig.dat')),
+ tdTestResult(fRc = True) ],
+ # Note: Copying files into directories via Main is supported only in versions > 5.2.
+ # Destination is a directory.
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = sScratchGst),
+ tdTestResult(fRc = True) ],
+ # Copy over file again into same directory (overwrite).
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = sScratchGst),
+ tdTestResult(fRc = True) ]
+ ]);
+
+ aaTests.extend([
+ # Copy the same file over to the guest, but this time store the file into the former
+ # file's ADS (Alternate Data Stream). Only works on Windows, of course.
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = sTestFileBig,
+ sDst = os.path.join(sScratchGst, 'HostGABig.dat:ADS-Test')),
+ tdTestResult(fRc = True) ]
+ ]);
+
+ #
+ # Directory handling.
+ #
+ ## @todo r=michaln disabled completely, can fill up the guest disk or fail without giving a reason
+ if self.oTstDrv.fpApiVer > 6.0: # Copying directories via Main is supported only in versions > 5.2.
+ if self.oTstDrv.sHost == "win":
+ sSystemRoot = os.getenv('SystemRoot', 'C:\\Windows')
+ aaTests.extend([
+ # Copying directories with contain files we don't have read access to.
+ ## @todo r=klaus disabled, because this can fill up the guest disk, making other tests fail,
+ ## additionally it's not really clear if this fails reliably on all Windows versions, even
+ ## the old ones like XP with a "proper" administrator.
+ #[ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = os.path.join(sSystemRoot, 'security'),
+ # sDst = sScratchGst, aFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting ]),
+ # tdTestResult(fRc = False) ],
+ # Copying directories with regular files.
+ [ tdTestCopyTo(sUser = sUser, sPassword = sPassword, sSrc = os.path.join(sSystemRoot, 'Help'),
+ sDst = sScratchGst, aFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting ]),
+ tdTestResult(fRc = True) ]
+ ]);
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ reporter.log('Testing #%d, sSrc=%s, sDst=%s, aFlags=%s ...' % \
+ (i, curTest.sSrc, curTest.sDst, curTest.aFlags));
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlCopyTo: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+
+ fRc2 = False;
+ if os.path.isdir(curTest.sSrc):
+ try:
+ curProgress = curGuestSession.directoryCopyToGuest(curTest.sSrc, curTest.sDst, curTest.aFlags);
+ if curProgress is not None:
+ oProgress = vboxwrappers.ProgressWrapper(curProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, \
+ "gctrlDirCopyTo");
+ try:
+ oProgress.wait();
+ if not oProgress.isSuccess():
+ oProgress.logResult(fIgnoreErrors = True);
+ fRc = False;
+ except:
+ reporter.logXcpt('Waiting exception for sSrc="%s", sDst="%s":' % (curTest.sSrc, curTest.sDst));
+ fRc2 = False;
+ else:
+ reporter.error('No progress object returned');
+ fRc2 = False;
+ except:
+ fRc2 = False;
+ else:
+ fRc2 = self.gctrlCopyFileTo(curGuestSession, curTest.sSrc, curTest.sDst, curTest.aFlags);
+
+ curTest.closeSession();
+
+ if fRc2 is curRes.fRc:
+ ## @todo Verify the copied results (size, checksum?).
+ pass;
+ else:
+ reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc2, curRes.fRc));
+ fRc = False;
+ break;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlCopyFrom(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests copying files from guest to the host.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ sScratchHst = os.path.join(self.oTstDrv.sScratchPath, "testGctrlCopyFrom");
+
+ if self.oTstDrv.sHost == "win":
+ sScratchHstNotExist = sScratchHst + "\\does-not-exist\\";
+ sScratchHstNotExistChain = sScratchHst + "\\does\\not\\exist\\";
+ sScratchHstInvalid = "?*|invalid-name?*|";
+ else:
+ sScratchHstNotExist = sScratchHst + "/does-not-exist/";
+ sScratchHstNotExistChain = sScratchHst + "/does/not/exist/";
+ sScratchHstInvalid = "/";
+
+ try:
+ os.makedirs(sScratchHst);
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ reporter.error('Failed: Unable to create scratch directory \"%s\"' % (sScratchHst,));
+ return (False, oTxsSession);
+ reporter.log('Scratch path is: %s' % (sScratchHst,));
+
+ aaTests = [];
+ if oTestVm.isWindows():
+ aaTests.extend([
+ # Destination missing.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = ''),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'Something',
+ aFlags = [ 80 ] ),
+ tdTestResult(fRc = False) ],
+ # Source missing.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sDst = ''),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sDst = 'Something',
+ aFlags = [ 80 ] ),
+ tdTestResult(fRc = False) ],
+ # Testing DirectoryCopyFlag flags.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\system32',
+ sDst = sScratchHstInvalid, aFlags = [ 80 ] ),
+ tdTestResult(fRc = False) ],
+ # Testing FileCopyFlag flags.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\system32\\ole32.dll',
+ sDst = sScratchHstInvalid, aFlags = [ 80 ] ),
+ tdTestResult(fRc = False) ],
+ # Nothing to copy (sDst is empty / unreachable).
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'z:\\'),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = '\\\\uncrulez\\foo'),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'non-exist',
+ sDst = os.path.join(sScratchHst, 'non-exist.dll')),
+ tdTestResult(fRc = False) ]
+ ]);
+
+ #
+ # Single file handling.
+ #
+ if self.oTstDrv.fpApiVer > 5.2:
+ aaTests.extend([
+ # Copying single files.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\system32\\ole32.dll',
+ sDst = sScratchHstInvalid),
+ tdTestResult(fRc = False) ],
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\system32\\ole32.dll',
+ sDst = os.path.join(sScratchHstInvalid, 'renamedfile.dll')),
+ tdTestResult(fRc = False) ],
+ # Copy over file using a different destination name.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\system32\\ole32.dll',
+ sDst = os.path.join(sScratchHst, 'renamedfile.dll')),
+ tdTestResult(fRc = True) ],
+ # Copy over same file (and overwrite existing one).
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\system32\\ole32.dll',
+ sDst = os.path.join(sScratchHst, 'renamedfile.dll')),
+ tdTestResult(fRc = True) ],
+ # Note: Copying files into directories via Main is supported only in versions > 5.2.
+ # Destination is a directory with a trailing slash (should work).
+ # See "cp" syntax.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\system32\\ole32.dll',
+ sDst = sScratchHst + "/"),
+ tdTestResult(fRc = True) ],
+ # Destination is a directory (without a trailing slash, should also work).
+ # See "cp" syntax.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\system32\\ole32.dll',
+ sDst = sScratchHst),
+ tdTestResult(fRc = True) ],
+ # Destination is a non-existing directory.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\system32\\ole32.dll',
+ sDst = sScratchHstNotExist),
+ tdTestResult(fRc = False) ]
+ ]);
+
+ #
+ # Directory handling.
+ #
+ if self.oTstDrv.fpApiVer > 5.2: # Copying directories via Main is supported only in versions > 5.2.
+ aaTests.extend([
+ # Copying entire directories (destination is "<sScratchHst>\Web").
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\Web',
+ sDst = sScratchHst),
+ tdTestResult(fRc = True) ],
+ # Repeat -- this time it should fail, as the destination directory already exists (and
+ # DirectoryCopyFlag_CopyIntoExisting is not specified).
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\Web',
+ sDst = sScratchHst + "/"),
+ tdTestResult(fRc = False) ],
+ # Next try with the DirectoryCopyFlag_CopyIntoExisting flag being set.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\Web',
+ sDst = sScratchHst, aFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting ]),
+ tdTestResult(fRc = True) ],
+ # Ditto, with trailing slash.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\Web',
+ sDst = sScratchHst + "/", aFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting ]),
+ tdTestResult(fRc = True) ],
+ # Copying contents of directories into a non-existing directory chain on the host which fail.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\Web\\',
+ sDst = sScratchHstNotExistChain,
+ aFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting ]),
+ tdTestResult(fRc = False) ],
+ # Copying contents of directories into a non-existing directory on the host, which should succeed.
+ [ tdTestCopyFrom(sUser = sUser, sPassword = sPassword, sSrc = 'C:\\Windows\\Web\\',
+ sDst = sScratchHstNotExist,
+ aFlags = [ vboxcon.DirectoryCopyFlag_CopyIntoExisting ]),
+ tdTestResult(fRc = True) ]
+ ]);
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ reporter.log('Testing #%d, sSrc="%s", sDst="%s", aFlags="%s" ...' % \
+ (i, curTest.sSrc, curTest.sDst, curTest.aFlags));
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, curGuestSession = curTest.createSession('testGuestCtrlCopyFrom: Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+
+ fRc2 = True;
+
+ try:
+ if self.oTstDrv.fpApiVer >= 5.0:
+ oFsInfo = curGuestSession.fsObjQueryInfo(curTest.sSrc, True); # fFollowSymlinks
+ else:
+ oFsInfo = curGuestSession.fileQueryInfo(curTest.sSrc);
+
+ if oFsInfo.type is vboxcon.FsObjType_Directory:
+ curProgress = curGuestSession.directoryCopyFromGuest(curTest.sSrc, curTest.sDst, curTest.aFlags);
+ if curProgress is not None:
+ oProgress = vboxwrappers.ProgressWrapper(curProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, \
+ "gctrlDirCopyFrom");
+ try:
+ oProgress.wait();
+ if not oProgress.isSuccess():
+ oProgress.logResult(fIgnoreErrors = True);
+ fRc2 = False;
+ except:
+ reporter.logXcpt('Waiting exception for sSrc="%s", sDst="%s":' % (curTest.sSrc, curTest.sDst));
+ fRc2 = False;
+ else:
+ reporter.error('No progress object returned');
+ fRc2 = False;
+ elif oFsInfo.type is vboxcon.FsObjType_File:
+ fRc2 = self.gctrlCopyFileFrom(curGuestSession, curTest.sSrc, curTest.sDst, curTest.aFlags);
+ else:
+ reporter.log2('Element "%s" not handled (yet), skipping' % oFsInfo.name);
+
+ except:
+ reporter.logXcpt('Query information exception for sSrc="%s", sDst="%s":' % (curTest.sSrc, curTest.sDst));
+ fRc2 = False;
+
+ curTest.closeSession();
+ if fRc2 is curRes.fRc:
+ ## @todo Verify the copied results (size, checksum?).
+ pass;
+ else:
+ reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc2, curRes.fRc));
+ fRc = False;
+ break;
+
+ return (fRc, oTxsSession);
+
+ def testGuestCtrlUpdateAdditions(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914
+ """
+ Tests updating the Guest Additions inside the guest.
+ """
+
+ if oTestVm.isWindows():
+ sUser = "Administrator";
+ else:
+ sUser = "vbox";
+ sPassword = "password";
+
+ # Some stupid trickery to guess the location of the iso.
+ sVBoxValidationKitISO = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxValidationKit.iso'));
+ if not os.path.isfile(sVBoxValidationKitISO):
+ sVBoxValidationKitISO = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxTestSuite.iso'));
+ if not os.path.isfile(sVBoxValidationKitISO):
+ sCur = os.getcwd();
+ for i in range(0, 10):
+ sVBoxValidationKitISO = os.path.join(sCur, 'validationkit/VBoxValidationKit.iso');
+ if os.path.isfile(sVBoxValidationKitISO):
+ break;
+ sVBoxValidationKitISO = os.path.join(sCur, 'testsuite/VBoxTestSuite.iso');
+ if os.path.isfile(sVBoxValidationKitISO):
+ break;
+ sCur = os.path.abspath(os.path.join(sCur, '..'));
+ if i is None: pass; # shut up pychecker/pylint.
+ if os.path.isfile(sVBoxValidationKitISO):
+ reporter.log('Validation Kit .ISO found at: %s' % (sVBoxValidationKitISO,));
+ else:
+ reporter.log('Warning: Validation Kit .ISO not found -- some tests might fail');
+
+ sScratch = os.path.join(self.oTstDrv.sScratchPath, "testGctrlUpdateAdditions");
+ try:
+ os.makedirs(sScratch);
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ reporter.error('Failed: Unable to create scratch directory \"%s\"' % (sScratch,));
+ return (False, oTxsSession);
+ reporter.log('Scratch path is: %s' % (sScratch,));
+
+ aaTests = [];
+ if oTestVm.isWindows():
+ aaTests.extend([
+ # Source is missing.
+ [ tdTestUpdateAdditions(sUser = sUser, sPassword = sPassword, sSrc = ''),
+ tdTestResult(fRc = False) ],
+ # Wrong aFlags.
+ [ tdTestUpdateAdditions(sUser = sUser, sPassword = sPassword, sSrc = self.oTstDrv.getGuestAdditionsIso(),
+ aFlags = [ 1234 ]),
+ tdTestResult(fRc = False) ],
+ # Non-existing .ISO.
+ [ tdTestUpdateAdditions(sUser = sUser, sPassword = sPassword, sSrc = "non-existing.iso"),
+ tdTestResult(fRc = False) ],
+ # Wrong .ISO.
+ [ tdTestUpdateAdditions(sUser = sUser, sPassword = sPassword, sSrc = sVBoxValidationKitISO),
+ tdTestResult(fRc = False) ],
+ # The real thing.
+ [ tdTestUpdateAdditions(sUser = sUser, sPassword = sPassword, sSrc = self.oTstDrv.getGuestAdditionsIso()),
+ tdTestResult(fRc = True) ],
+ # Test the (optional) installer arguments. This will extract the
+ # installer into our guest's scratch directory.
+ [ tdTestUpdateAdditions(sUser = sUser, sPassword = sPassword, sSrc = self.oTstDrv.getGuestAdditionsIso(),
+ aArgs = [ '/extract', '/D=' + sScratch ]),
+ tdTestResult(fRc = True) ]
+ # Some debg ISO. Only enable locally.
+ #[ tdTestUpdateAdditions(sUser = sUser, sPassword = sPassword,
+ # sSrc = "V:\\Downloads\\VBoxGuestAdditions-r80354.iso"),
+ # tdTestResult(fRc = True) ]
+ ]);
+ else:
+ reporter.log('No OS-specific tests for non-Windows yet!');
+
+ fRc = True;
+ for (i, aTest) in enumerate(aaTests):
+ curTest = aTest[0]; # tdTestExec, use an index, later.
+ curRes = aTest[1]; # tdTestResult
+ reporter.log('Testing #%d, sSrc="%s", aFlags="%s" ...' % \
+ (i, curTest.sSrc, curTest.aFlags));
+ curTest.setEnvironment(oSession, oTxsSession, oTestVm);
+ fRc, _ = curTest.createSession('Test #%d' % (i,));
+ if fRc is False:
+ reporter.error('Test #%d failed: Could not create session' % (i,));
+ break;
+ try:
+ curProgress = curTest.oTest.oGuest.updateGuestAdditions(curTest.sSrc, curTest.aArgs, curTest.aFlags);
+ if curProgress is not None:
+ oProgress = vboxwrappers.ProgressWrapper(curProgress, self.oTstDrv.oVBoxMgr, self.oTstDrv, "gctrlUpGA");
+ try:
+ oProgress.wait();
+ if not oProgress.isSuccess():
+ oProgress.logResult(fIgnoreErrors = True);
+ fRc = False;
+ except:
+ reporter.logXcpt('Waiting exception for updating Guest Additions:');
+ fRc = False;
+ else:
+ reporter.error('No progress object returned');
+ fRc = False;
+ except:
+ # Just log, don't assume an error here (will be done in the main loop then).
+ reporter.logXcpt('Updating Guest Additions exception for sSrc="%s", aFlags="%s":' \
+ % (curTest.sSrc, curTest.aFlags));
+ fRc = False;
+ curTest.closeSession();
+ if fRc is curRes.fRc:
+ if fRc:
+ ## @todo Verify if Guest Additions were really updated (build, revision, ...).
+ pass;
+ else:
+ reporter.error('Test #%d failed: Got %s, expected %s' % (i, fRc, curRes.fRc));
+ fRc = False;
+ break;
+
+ return (fRc, oTxsSession);
+
+
+
+class tdAddGuestCtrl(vbox.TestDriver): # pylint: disable=R0902,R0904
+ """
+ Guest control using VBoxService on the guest.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.oTestVmSet = self.oTestVmManager.getSmokeVmSet('nat');
+ self.asRsrcs = None;
+ self.fQuick = False; # Don't skip lengthly tests by default.
+ self.addSubTestDriver(SubTstDrvAddGuestCtrl(self));
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ """
+ Shows the testdriver usage.
+ """
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdAddGuestCtrl Options:');
+ reporter.log(' --quick');
+ reporter.log(' Same as --virt-modes hwvirt --cpu-counts 1.');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ """
+ Parses the testdriver arguments from the command line.
+ """
+ if asArgs[iArg] == '--quick':
+ self.parseOption(['--virt-modes', 'hwvirt'], 0);
+ self.parseOption(['--cpu-counts', '1'], 0);
+ self.fQuick = True;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionConfig(self):
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+ sGaIso = self.getGuestAdditionsIso();
+ return self.oTestVmSet.actionConfig(self, eNic0AttachType = eNic0AttachType, sDvdImage = sGaIso);
+
+ def actionExecute(self):
+ return self.oTestVmSet.actionExecute(self, self.testOneCfg);
+
+ #
+ # Test execution helpers.
+ #
+ def testOneCfg(self, oVM, oTestVm): # pylint: disable=R0915
+ """
+ Runs the specified VM thru the tests.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+
+ self.logVmInfo(oVM);
+
+ fRc = True;
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = False);
+ reporter.log("TxsSession: %s" % (oTxsSession,));
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ fManual = False; # Manual override for local testing. (Committed version shall be False.)
+ if not fManual:
+ fRc, oTxsSession = self.aoSubTstDrvs[0].testIt(oTestVm, oSession, oTxsSession);
+ else:
+ fRc, oTxsSession = self.testGuestCtrlManual(oSession, oTxsSession, oTestVm);
+
+ # Cleanup.
+ self.removeTask(oTxsSession);
+ if not fManual:
+ self.terminateVmBySession(oSession);
+ else:
+ fRc = False;
+ return fRc;
+
+ def gctrlReportError(self, progress):
+ """
+ Helper function to report an error of a
+ given progress object.
+ """
+ if progress is None:
+ reporter.log('No progress object to print error for');
+ else:
+ errInfo = progress.errorInfo;
+ if errInfo:
+ reporter.log('%s' % (errInfo.text,));
+ return False;
+
+ def gctrlGetRemainingTime(self, msTimeout, msStart):
+ """
+ Helper function to return the remaining time (in ms)
+ based from a timeout value and the start time (both in ms).
+ """
+ if msTimeout is 0:
+ return 0xFFFFFFFE; # Wait forever.
+ msElapsed = base.timestampMilli() - msStart;
+ if msElapsed > msTimeout:
+ return 0; # No time left.
+ return msTimeout - msElapsed;
+
+ def testGuestCtrlManual(self, oSession, oTxsSession, oTestVm): # pylint: disable=R0914,R0915,W0613,W0612
+ """
+ For manually testing certain bits.
+ """
+
+ reporter.log('Manual testing ...');
+ fRc = True;
+
+ sUser = 'Administrator';
+ sPassword = 'password';
+
+ oGuest = oSession.o.console.guest;
+ oGuestSession = oGuest.createSession(sUser,
+ sPassword,
+ "", "Manual Test");
+
+ aWaitFor = [ vboxcon.GuestSessionWaitForFlag_Start ];
+ _ = oGuestSession.waitForArray(aWaitFor, 30 * 1000);
+
+ sCmd = 'c:\\windows\\system32\\cmd.exe';
+ aArgs = [ sCmd, '/C', 'dir', '/S', 'c:\\windows' ];
+ aEnv = [];
+ aFlags = [];
+
+ for _ in range(100):
+ oProc = oGuestSession.processCreate(sCmd, aArgs if self.fpApiVer >= 5.0 else aArgs[1:],
+ aEnv, aFlags, 30 * 1000);
+
+ aWaitFor = [ vboxcon.ProcessWaitForFlag_Terminate ];
+ _ = oProc.waitForArray(aWaitFor, 30 * 1000);
+
+ oGuestSession.close();
+ oGuestSession = None;
+
+ time.sleep(5);
+
+ oSession.o.console.PowerDown();
+
+ return (fRc, oTxsSession);
+
+if __name__ == '__main__':
+ sys.exit(tdAddGuestCtrl().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/api/Makefile.kmk b/src/VBox/ValidationKit/tests/api/Makefile.kmk
new file mode 100644
index 00000000..3559d3ea
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/Makefile.kmk
@@ -0,0 +1,50 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - API Tests.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsApi
+ValidationKitTestsApi_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsApi_INST = $(INST_VALIDATIONKIT)tests/api/
+ValidationKitTestsApi_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdApi1.py \
+ $(PATH_SUB_CURRENT)/tdAppliance1.py \
+ $(PATH_SUB_CURRENT)/tdMoveMedium1.py \
+ $(PATH_SUB_CURRENT)/tdPython1.py \
+ $(PATH_SUB_CURRENT)/tdTreeDepth1.py \
+ $(PATH_SUB_CURRENT)/tdMoveVM1.py
+ValidationKitTestsApi_SOURCES := \
+ $(wildcard \
+ $(PATH_SUB_CURRENT)/*.ova \
+ )
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsApi_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/api/__init__.py b/src/VBox/ValidationKit/tests/api/__init__.py
new file mode 100644
index 00000000..c981b648
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/__init__.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# $Id: __init__.py $
+
+"""
+Just to make python 2.x happy.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
diff --git a/src/VBox/ValidationKit/tests/api/tdApi1.py b/src/VBox/ValidationKit/tests/api/tdApi1.py
new file mode 100755
index 00000000..ff769edc
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdApi1.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdApi1.py $
+
+"""
+VirtualBox Validation Kit - API Test wrapper #1 combining all API sub-tests
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import vbox
+
+
+class tdApi1(vbox.TestDriver):
+ """
+ API Test wrapper #1.
+ """
+
+ def __init__(self, aoSubTestDriverClasses = None):
+ vbox.TestDriver.__init__(self)
+ for oSubTestDriverClass in aoSubTestDriverClasses:
+ self.addSubTestDriver(oSubTestDriverClass(self));
+
+ #
+ # Overridden methods.
+ #
+
+ def actionConfig(self):
+ """
+ Import the API.
+ """
+ if not self.importVBoxApi():
+ return False
+ return True
+
+ def actionExecute(self):
+ """
+ Execute the testcase, i.e. all sub-tests.
+ """
+ fRc = True;
+ for oSubTstDrv in self.aoSubTstDrvs:
+ fRc &= oSubTstDrv.testIt();
+ return fRc;
+
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdPython1 import SubTstDrvPython1; # pylint: disable=relative-import
+ from tdAppliance1 import SubTstDrvAppliance1; # pylint: disable=relative-import
+ from tdMoveMedium1 import SubTstDrvMoveMedium1; # pylint: disable=relative-import
+ from tdTreeDepth1 import SubTstDrvTreeDepth1; # pylint: disable=relative-import
+ from tdMoveVM1 import SubTstDrvMoveVM1; # pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvPython1, SubTstDrvAppliance1, SubTstDrvMoveMedium1,
+ SubTstDrvTreeDepth1, SubTstDrvMoveVM1]).main(sys.argv))
+
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova
new file mode 100644
index 00000000..aba10dbb
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova
new file mode 100644
index 00000000..19c2c4b9
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova
new file mode 100644
index 00000000..66a173aa
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova
new file mode 100644
index 00000000..8027ce64
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova
new file mode 100644
index 00000000..7fbf44ee
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova
new file mode 100644
index 00000000..2434f37e
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova
new file mode 100644
index 00000000..a6549b15
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem
new file mode 100644
index 00000000..c155d659
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem
@@ -0,0 +1,74 @@
+-----BEGIN PRIVATE KEY-----
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALRXOpwrjjFzFZtb
+aTtB+3kNrgrqYkye6CqvrOODZwXsljr/pduYj6bYh5OdznAFqgeeCpkqEf/2p5mD
+XlqE4cDoL2opLk7LrdmVDqX9rUkGcGf6hwTp1EAllESBIUt6LdSbL7Y5goYq1E++
+/fKSYRIniCbWp9ahTLH8hjDcw48tAgMBAAECgYAiNl4vHHA4X13dAEWBcW4UtAyt
+k3Ocl5Tx7Cv/aYFU9WI2xSMg+ttdyrxFu+1bASgVk9zs27dYeOGo1OxEfesZzQkT
+mbzvYCdYk9wAWKXQwpp78HZyEsKVipIxO+riH9ph7SFQBzB5NoADPoqwahOmeQQW
+sE3oTJRa9O+JR3muXQJBAOOt4dj+1Rwmdy83j9uOLLfO75l2pJd/hq5gN7+eNFks
+R68cbhFGkrOU13dVLquyqxAoaKc2PgZS+RfGRrohjm8CQQDKxeuqYXyrYBU4jNAS
+TgcR4lbb8HiC1AyQrdiJVtH4qLpqk92M7muHXZQGOXRizHQMrRDY+PcgLoFAnRzJ
+j0ojAkEA3rqL5ivlbtRyY86G/NHpDSdzXT2jZlFq/8tAvkOWEmYu+i9lvaC8gtFo
+t2Stc2olzni5aFq38pfY9lkRd6S8IQJBAJe3LJPnqwrish4Epa38eae07P5U1yY0
+GE6r9EcWEbZ2MDyL9Al9XjEDIDzkAiPmC7JsTx24cda/VPAOXbqlnncCQQCCevzg
+rrnTz0/3iLWkxowiKCE1EvbVALDxPwHi0zvjXbGZs8BodLxeChpJzFImbxRVAIrp
+WvZOuLqhAKjL7OOT
+-----END PRIVATE KEY-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 14180722474914962380 (0xc4cc0bf54fb45bcc)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=DE, ST=Example, L=For Instance, O=Beispiel GmbH, CN=beispiel.example.org
+ Validity
+ Not Before: Feb 1 01:45:27 2016 GMT
+ Not After : Jan 24 01:45:27 2046 GMT
+ Subject: C=DE, ST=Example, L=For Instance, O=Beispiel GmbH, CN=beispiel.example.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:b4:57:3a:9c:2b:8e:31:73:15:9b:5b:69:3b:41:
+ fb:79:0d:ae:0a:ea:62:4c:9e:e8:2a:af:ac:e3:83:
+ 67:05:ec:96:3a:ff:a5:db:98:8f:a6:d8:87:93:9d:
+ ce:70:05:aa:07:9e:0a:99:2a:11:ff:f6:a7:99:83:
+ 5e:5a:84:e1:c0:e8:2f:6a:29:2e:4e:cb:ad:d9:95:
+ 0e:a5:fd:ad:49:06:70:67:fa:87:04:e9:d4:40:25:
+ 94:44:81:21:4b:7a:2d:d4:9b:2f:b6:39:82:86:2a:
+ d4:4f:be:fd:f2:92:61:12:27:88:26:d6:a7:d6:a1:
+ 4c:b1:fc:86:30:dc:c3:8f:2d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 02:0A:B0:BC:21:63:C1:50:16:1E:8D:B7:F4:B0:1C:48:D8:E1:0A:2A
+ X509v3 Authority Key Identifier:
+ keyid:02:0A:B0:BC:21:63:C1:50:16:1E:8D:B7:F4:B0:1C:48:D8:E1:0A:2A
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 13:f1:33:5f:c7:d9:6c:20:a4:eb:f2:e5:e2:b5:e0:e8:b6:9c:
+ c7:62:4a:39:53:83:11:98:cf:11:3d:58:09:d8:38:78:71:16:
+ d4:24:cc:c8:2e:5a:2b:d3:94:6a:dc:ae:62:e7:81:6a:5f:04:
+ 84:ba:55:8c:dc:6b:ff:aa:78:4f:37:8e:fd:ba:b5:d1:27:83:
+ 47:29:30:92:63:85:53:f0:b1:b9:f4:c7:a8:b1:48:44:4e:30:
+ 6f:50:d3:35:14:87:59:d0:f8:ed:da:07:60:6c:de:6d:53:53:
+ 3d:d7:03:97:1f:6b:13:ce:92:49:20:57:4f:b0:87:30:76:66:
+ d3:43
+-----BEGIN CERTIFICATE-----
+MIICqDCCAhGgAwIBAgIJAMTMC/VPtFvMMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV
+BAYTAkRFMRAwDgYDVQQIDAdFeGFtcGxlMRUwEwYDVQQHDAxGb3IgSW5zdGFuY2Ux
+FjAUBgNVBAoMDUJlaXNwaWVsIEdtYkgxHTAbBgNVBAMMFGJlaXNwaWVsLmV4YW1w
+bGUub3JnMB4XDTE2MDIwMTAxNDUyN1oXDTQ2MDEyNDAxNDUyN1owbTELMAkGA1UE
+BhMCREUxEDAOBgNVBAgMB0V4YW1wbGUxFTATBgNVBAcMDEZvciBJbnN0YW5jZTEW
+MBQGA1UECgwNQmVpc3BpZWwgR21iSDEdMBsGA1UEAwwUYmVpc3BpZWwuZXhhbXBs
+ZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALRXOpwrjjFzFZtbaTtB
++3kNrgrqYkye6CqvrOODZwXsljr/pduYj6bYh5OdznAFqgeeCpkqEf/2p5mDXlqE
+4cDoL2opLk7LrdmVDqX9rUkGcGf6hwTp1EAllESBIUt6LdSbL7Y5goYq1E++/fKS
+YRIniCbWp9ahTLH8hjDcw48tAgMBAAGjUDBOMB0GA1UdDgQWBBQCCrC8IWPBUBYe
+jbf0sBxI2OEKKjAfBgNVHSMEGDAWgBQCCrC8IWPBUBYejbf0sBxI2OEKKjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBABPxM1/H2WwgpOvy5eK14Oi2nMdi
+SjlTgxGYzxE9WAnYOHhxFtQkzMguWivTlGrcrmLngWpfBIS6VYzca/+qeE83jv26
+tdEng0cpMJJjhVPwsbn0x6ixSEROMG9Q0zUUh1nQ+O3aB2Bs3m1TUz3XA5cfaxPO
+kkkgV0+whzB2ZtND
+-----END CERTIFICATE-----
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova
new file mode 100644
index 00000000..6a21a19b
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova
new file mode 100644
index 00000000..8e6e2e2e
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova
new file mode 100644
index 00000000..d5a92eb2
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova
new file mode 100644
index 00000000..6480e6d5
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem
new file mode 100644
index 00000000..1b682b0d
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem
@@ -0,0 +1,134 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAzYlueHZlbSqlLrVAnCyQx9xgnO7jSjK6wXqlbxkl0QZLyDnh
+vNowZYnu7YZI3V+RuVzYbczlEr1qZpUeO8NF04mK1kFPJRinRiuiOtih+WwvDQd7
+7RZJhjoAcfEeogt1OzOZg8QKF1AMGlUgk5zWma4VIS75ZM6G1b77qS3rge6L9NwZ
+gthyQ1PvddYue4MNEpK+2ekZZq/GGZvd+yNP0Wq7kzcmydCgCF9EdLCZwt4BHmOS
+ZM6BUxKv+PINZgO7Wp2tBBGigg9Mf/NXofjMHp7RI16xa2W9Q/Ct2A6rb5SAB10U
+ddrtxiI+F+Jv7lNkHOCLbju7cEs+Y6XIbt1l7aVqBFpgo6cwFZ7DdLPHZkgb50hx
+dnNdwncOR1syspTF544hbmkcb/8+weq+qgsD/57b/ZGrwb8ruByDz0LZiXzxxBRe
+w9OhHZGFnuC0D1ciIvpifKe4R/kLcEgVW7yYp4W1BAZgiBJPgXArJTlUBrxamzsQ
+m6N7GxlP+vTCny7IJsQ1KWu5BqkPv0T1+zD5vUT9/lAa+XBdDHhYVm0adr4dXIef
+6ws+0zbpz8DyaD/Bg9KvRKdnziVc+pKwkWOVrFvZ6NvBex0i8Bgh4FrpkdjmrZMZ
+uuCdex0CEayuYexgUqyMg9m0GwGqVNG6mVB4cCOg0rayaqEKrbtUSxNaD/MCAwEA
+AQKCAgAnjPGQBqBf0Fv2z/P92WmGu/ZvXFyqU3aycmpRJZKsVTzR66lvkMDNWSx7
+0mJFDvXYqHAROOM/pulJkho+P8Y4/XeU4P5c0hCmJRFTp4oLl/C53h3PsoE1bgXV
+5yMQ5YmKedRpkZirgcDCdG0PWpfE/MWeHA7rgf5aNSTyGh7+YqvV02CpWAMsx4MF
+ttA8/ivOziQhhIRZySsilGazw8jBMHulyXASV63jzok6txzvbY7jjR+HfGFQXgE0
+s0c2wTMVLdA0Pzx5MH51BJtxVJHato7h8n/LfclcwHyDXddJYlb8k8GzKAynGsG+
+ENmfD7btA5xw+teHtULtI+KcysepCeVXW7Fr7PFJe1XOx1c24WrFpjeGdnG3PdqA
+Y9YbjMCD/GpCUICla3N6iRDG3imY+DUWcExVjUo6TUbQ4ofQT4WF7+2Yzg6MRqYu
+41jgF+voCXS4BH8e+ngEjd8VLHma7FPuWujodCERsvxZ7aLXBcZCH1Lje9Y8a79x
+RGX83pVHTWFRyo4wKBpRUz+hwH7MjYdfE2Q+Zl153pXw3au9jX8PUMhx9swWPGRw
+CvLPwtTwrezzdFeXg4wfJwWnvqhKIViery4z3rdjh5Zke/9+7gXmFbZ8w+QUkXXq
+cnBEJySTvpI4B9abpcEI4OO84L/A6ENDO10W4ZGfm5aD2m0vIQKCAQEA6nZAmsSr
+T1CcgrWNcY6civ8o5cMF8fM9ib5QvibRlJN9dpcZPr6+Pp2UPCkjVowk/FybP1Kw
+sqA0aXOL3VR03YpLrqdUDpe9YiFCa3ttDX0pTOdU9QW2RbOrNQVurKhzNETd37cJ
+0RYBOHJY/RxA0F8l16wSr1jTO/SRbWkFJYZS/OaH1B6gCxz6H1IgfKJddrvFZsd8
+6PnLtqsPnsXuxugqHu3ENddRqiiSNyGnqpGu0jQpPBrQEQQN5o8/X9rfNfAobChH
+Nii0va7MHudwaTTo2ytUzEV+v0kueMNU9YXgJgxzSzf1/VQC+HoNI4Vbs9XCbOWV
+z2FVBwXM+0cIMQKCAQEA4Gr1pQAymJq/9p2fiWSR8TVdrYCwvAnBu8QSqSzweNZC
+SRt7Nkhhljm7M2DuUOioMaFAT9CnyD2nJyKSAqqOngb0vo7C53UzgFu+QFMtUx/Z
+t/C96WcLYrErXfnB/sTq0Gp4tpoo1S3FZecabhlZyv1O08WXiNCcEX1VMQz2+iF9
++n2PjFuEomeWHxBWCwfB9/S0pKVHP/7833sQ8Dfsm2EwtPy3v58ln3UguaUDIe5V
+r0TmKr8kDrYgbw1ZpBOd9Wbbvj3vIN34OPoSIrcar67DCp7qYq77LYAhngfdQGed
+MUMsbU1dODWf0kwP7mncTr3mPByQUHa3ImjvJPv1YwKCAQBYa04Dz8U2/RR46pSz
+zW9Vr9Ixi7GTRALiDkaO3z7MRC7daTAZDH/cRzre0TjFa8aK8TWO1NVUF7yMRAnr
+5uzHm17dN7coZasC9b4BoKNIofnQSbEtUgEiGhanwSuyqzf+7zWpJ3LpSd4d9ml+
+0ofSzP8NbZQCUoIeqyWo2CEbvKNRQnLY2M/MQRpGc4dS2TxcCYXxM6v0hDeB5NLY
+MpbQpj80OMB0+YWPoQs7BVMgrR37obYnN4ld0WSYnU7uDDF/OtlTqIDqeMFogyHx
+SaCH3G8wMBAjlNWut59x5WAF033re2iDZlA7P9J6+DQ6QBGMKUHQJWiws2kIY/Sg
+knIRAoIBAQCzlkR/Rxo2LthhZR/PFfEIQrl1Z9+GipRDSxPX2AOT33nqARjnhqK5
+UfexlOcBTj2SgcTyWjp6LoQ9+Bc6FPzODyj5+UqVaJ/PHxuvZCCIPZu/6+I+Dlz5
+HGhk6sJIu5JhOGLjVZhJiDhIZNkstBK8M1tKcvvh23aZNF/hQcu+vOCQfLxMCMyq
+HhTvROZmK04Yu/V3MGBFISuBN32FjmtEqFEO9JGiwZuc8GFAzoEkPRLKkGtUV+Nl
+9m8cD2XlvGESib5djjh3Z8oE5nFu4HJ1lne0XxmX4QlWDwxX51kx+fi7/FJoIZnw
+qlD8PCwfkQ1g4eyFvCHskiPZYHnHce2bAoIBACW40wFIPiDRhjNywEi4miN6P+Xs
+rlM9csmxw2GG6Z4c8H/Z4SNBRQmnj/F6PHsar6SGy9WlR9mNeR2XBn4Pyf/cNjTQ
+bKzV5wPRm6P81SQjhIz4Mxdx1S30AeF1LdagWFiq0on7oRTH2SeKQspdpNeiTDbC
+sBw4SVDKmGZYGZcuT3BdvvZFEW4qncSYuUM7l9bTmsbzid/v8zn/XDQrpdPYnptD
+ljJETKQzlyrJLtTbyFlo3Osf1N4408u3rqhpw2SgKdyMiHndhxkF869Vycll+VMz
+SzPU0wI62BIPWHDBJLnxGBTa+4kUSxP+oDvCfVYCmDcfDRew3MWCK9emJnU=
+-----END RSA PRIVATE KEY-----
+Certificate:
+ Data:
+ Version: 1 (0x0)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha512WithRSAEncryption
+ Issuer: C=DE, ST=Example, L=For Instance, O=Beispiel GmbH, CN=beispiel.example.org
+ Validity
+ Not Before: Feb 15 14:27:56 2016 GMT
+ Not After : Feb 2 14:27:56 2066 GMT
+ Subject: C=DE, ST=Instance-Example, L=Examplecity, O=For Instance GmbH, OU=The Example Unit, CN=subcert.example.org/emailAddress=subcert@example.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:cd:89:6e:78:76:65:6d:2a:a5:2e:b5:40:9c:2c:
+ 90:c7:dc:60:9c:ee:e3:4a:32:ba:c1:7a:a5:6f:19:
+ 25:d1:06:4b:c8:39:e1:bc:da:30:65:89:ee:ed:86:
+ 48:dd:5f:91:b9:5c:d8:6d:cc:e5:12:bd:6a:66:95:
+ 1e:3b:c3:45:d3:89:8a:d6:41:4f:25:18:a7:46:2b:
+ a2:3a:d8:a1:f9:6c:2f:0d:07:7b:ed:16:49:86:3a:
+ 00:71:f1:1e:a2:0b:75:3b:33:99:83:c4:0a:17:50:
+ 0c:1a:55:20:93:9c:d6:99:ae:15:21:2e:f9:64:ce:
+ 86:d5:be:fb:a9:2d:eb:81:ee:8b:f4:dc:19:82:d8:
+ 72:43:53:ef:75:d6:2e:7b:83:0d:12:92:be:d9:e9:
+ 19:66:af:c6:19:9b:dd:fb:23:4f:d1:6a:bb:93:37:
+ 26:c9:d0:a0:08:5f:44:74:b0:99:c2:de:01:1e:63:
+ 92:64:ce:81:53:12:af:f8:f2:0d:66:03:bb:5a:9d:
+ ad:04:11:a2:82:0f:4c:7f:f3:57:a1:f8:cc:1e:9e:
+ d1:23:5e:b1:6b:65:bd:43:f0:ad:d8:0e:ab:6f:94:
+ 80:07:5d:14:75:da:ed:c6:22:3e:17:e2:6f:ee:53:
+ 64:1c:e0:8b:6e:3b:bb:70:4b:3e:63:a5:c8:6e:dd:
+ 65:ed:a5:6a:04:5a:60:a3:a7:30:15:9e:c3:74:b3:
+ c7:66:48:1b:e7:48:71:76:73:5d:c2:77:0e:47:5b:
+ 32:b2:94:c5:e7:8e:21:6e:69:1c:6f:ff:3e:c1:ea:
+ be:aa:0b:03:ff:9e:db:fd:91:ab:c1:bf:2b:b8:1c:
+ 83:cf:42:d9:89:7c:f1:c4:14:5e:c3:d3:a1:1d:91:
+ 85:9e:e0:b4:0f:57:22:22:fa:62:7c:a7:b8:47:f9:
+ 0b:70:48:15:5b:bc:98:a7:85:b5:04:06:60:88:12:
+ 4f:81:70:2b:25:39:54:06:bc:5a:9b:3b:10:9b:a3:
+ 7b:1b:19:4f:fa:f4:c2:9f:2e:c8:26:c4:35:29:6b:
+ b9:06:a9:0f:bf:44:f5:fb:30:f9:bd:44:fd:fe:50:
+ 1a:f9:70:5d:0c:78:58:56:6d:1a:76:be:1d:5c:87:
+ 9f:eb:0b:3e:d3:36:e9:cf:c0:f2:68:3f:c1:83:d2:
+ af:44:a7:67:ce:25:5c:fa:92:b0:91:63:95:ac:5b:
+ d9:e8:db:c1:7b:1d:22:f0:18:21:e0:5a:e9:91:d8:
+ e6:ad:93:19:ba:e0:9d:7b:1d:02:11:ac:ae:61:ec:
+ 60:52:ac:8c:83:d9:b4:1b:01:aa:54:d1:ba:99:50:
+ 78:70:23:a0:d2:b6:b2:6a:a1:0a:ad:bb:54:4b:13:
+ 5a:0f:f3
+ Exponent: 65537 (0x10001)
+ Signature Algorithm: sha512WithRSAEncryption
+ 2d:1c:49:41:1a:4f:dc:d8:5c:b1:fa:ca:53:38:86:26:6e:56:
+ a5:6d:2e:1d:0d:74:64:0e:89:c3:3a:c7:1d:01:6e:d1:93:b2:
+ c9:37:01:6a:ae:31:42:96:05:d7:df:fd:01:f8:bc:f3:f3:4c:
+ cd:75:ae:16:00:61:78:f2:67:c5:b1:76:76:16:39:ba:d2:6b:
+ 09:ad:99:2b:22:ce:56:89:4d:08:ca:8c:76:4c:50:6b:83:c9:
+ 46:9b:f5:9f:2d:e2:7f:e5:72:aa:76:56:c4:67:83:45:26:b7:
+ e2:ae:f7:1e:61:c9:aa:2e:8d:b8:59:42:84:37:25:c8:16:92:
+ d6:d5
+-----BEGIN CERTIFICATE-----
+MIIEGjCCA4MCAQEwDQYJKoZIhvcNAQENBQAwbTELMAkGA1UEBhMCREUxEDAOBgNV
+BAgMB0V4YW1wbGUxFTATBgNVBAcMDEZvciBJbnN0YW5jZTEWMBQGA1UECgwNQmVp
+c3BpZWwgR21iSDEdMBsGA1UEAwwUYmVpc3BpZWwuZXhhbXBsZS5vcmcwIBcNMTYw
+MjE1MTQyNzU2WhgPMjA2NjAyMDIxNDI3NTZaMIG3MQswCQYDVQQGEwJERTEZMBcG
+A1UECAwQSW5zdGFuY2UtRXhhbXBsZTEUMBIGA1UEBwwLRXhhbXBsZWNpdHkxGjAY
+BgNVBAoMEUZvciBJbnN0YW5jZSBHbWJIMRkwFwYDVQQLDBBUaGUgRXhhbXBsZSBV
+bml0MRwwGgYDVQQDDBNzdWJjZXJ0LmV4YW1wbGUub3JnMSIwIAYJKoZIhvcNAQkB
+FhNzdWJjZXJ0QGV4YW1wbGUub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAzYlueHZlbSqlLrVAnCyQx9xgnO7jSjK6wXqlbxkl0QZLyDnhvNowZYnu
+7YZI3V+RuVzYbczlEr1qZpUeO8NF04mK1kFPJRinRiuiOtih+WwvDQd77RZJhjoA
+cfEeogt1OzOZg8QKF1AMGlUgk5zWma4VIS75ZM6G1b77qS3rge6L9NwZgthyQ1Pv
+ddYue4MNEpK+2ekZZq/GGZvd+yNP0Wq7kzcmydCgCF9EdLCZwt4BHmOSZM6BUxKv
++PINZgO7Wp2tBBGigg9Mf/NXofjMHp7RI16xa2W9Q/Ct2A6rb5SAB10UddrtxiI+
+F+Jv7lNkHOCLbju7cEs+Y6XIbt1l7aVqBFpgo6cwFZ7DdLPHZkgb50hxdnNdwncO
+R1syspTF544hbmkcb/8+weq+qgsD/57b/ZGrwb8ruByDz0LZiXzxxBRew9OhHZGF
+nuC0D1ciIvpifKe4R/kLcEgVW7yYp4W1BAZgiBJPgXArJTlUBrxamzsQm6N7GxlP
++vTCny7IJsQ1KWu5BqkPv0T1+zD5vUT9/lAa+XBdDHhYVm0adr4dXIef6ws+0zbp
+z8DyaD/Bg9KvRKdnziVc+pKwkWOVrFvZ6NvBex0i8Bgh4FrpkdjmrZMZuuCdex0C
+EayuYexgUqyMg9m0GwGqVNG6mVB4cCOg0rayaqEKrbtUSxNaD/MCAwEAATANBgkq
+hkiG9w0BAQ0FAAOBgQAtHElBGk/c2Fyx+spTOIYmblalbS4dDXRkDonDOscdAW7R
+k7LJNwFqrjFClgXX3/0B+Lzz80zNda4WAGF48mfFsXZ2Fjm60msJrZkrIs5WiU0I
+yox2TFBrg8lGm/WfLeJ/5XKqdlbEZ4NFJrfirvceYcmqLo24WUKENyXIFpLW1Q==
+-----END CERTIFICATE-----
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova b/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova
new file mode 100644
index 00000000..48e49059
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova
Binary files differ
diff --git a/src/VBox/ValidationKit/tests/api/tdAppliance1.py b/src/VBox/ValidationKit/tests/api/tdAppliance1.py
new file mode 100755
index 00000000..22a5a197
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdAppliance1.py
@@ -0,0 +1,206 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdAppliance1.py $
+
+"""
+VirtualBox Validation Kit - IAppliance Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import tarfile
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import vboxwrappers;
+
+
+class SubTstDrvAppliance1(base.SubTestDriverBase):
+ """
+ Sub-test driver for IAppliance Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, 'appliance', oTstDrv);
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ fRc = True;
+
+ # Import a set of simple OVAs.
+ # Note! Manifests generated by ovftool 4.0.0 does not include the ovf, while the ones b 4.1.0 does.
+ for sOva in (
+ # t1 is a plain VM without any disks, ovftool 4.0 export from fusion
+ 'tdAppliance1-t1.ova',
+ # t2 is a plain VM with one disk. Both 4.0 and 4.1.0 exports.
+ 'tdAppliance1-t2.ova',
+ 'tdAppliance1-t2-ovftool-4.1.0.ova',
+ # t3 is a VM with one gzipped disk and selecting SHA256 on the ovftool cmdline (--compress=9 --shaAlgorithm=sha256).
+ 'tdAppliance1-t3.ova',
+ 'tdAppliance1-t3-ovftool-4.1.0.ova',
+ # t4 is a VM with with two gzipped disk, SHA256 and a (self) signed manifest (--privateKey=./tdAppliance1-t4.pem).
+ 'tdAppliance1-t4.ova',
+ 'tdAppliance1-t4-ovftool-4.1.0.ova',
+ # t5 is a VM with with one gzipped disk, SHA1 and a manifest signed by a valid (2016) DigiCert code signing cert.
+ 'tdAppliance1-t5.ova',
+ 'tdAppliance1-t5-ovftool-4.1.0.ova',
+ # t6 is a VM with with one gzipped disk, SHA1 and a manifest signed by a certificate issued by the t4 certificate,
+ # thus it should be impossible to establish a trusted path to a root CA.
+ 'tdAppliance1-t6.ova',
+ 'tdAppliance1-t6-ovftool-4.1.0.ova',
+ # t7 is based on tdAppliance1-t2-ovftool-4.1.0.ova and has modified to have an invalid InstanceID as well as an
+ # extra readme file. It was tarred up using bsdtar 2.4.12 on windows, so it uses a slightly different tar format and
+ # have different file attributes.
+ 'tdAppliance1-t7-bad-instance.ova',
+ ):
+ reporter.testStart(sOva);
+ try:
+ fRc = self.testImportOva(os.path.join(g_ksValidationKitDir, 'tests', 'api', sOva)) and fRc;
+ fRc = self.testImportOvaAsOvf(os.path.join(g_ksValidationKitDir, 'tests', 'api', sOva)) and fRc;
+ except:
+ reporter.errorXcpt();
+ fRc = False;
+ fRc = reporter.testDone() and fRc;
+
+ ## @todo more stuff
+ return fRc;
+
+ #
+ # Test execution helpers.
+ #
+
+ def testImportOva(self, sOva):
+ """ xxx """
+ oVirtualBox = self.oTstDrv.oVBoxMgr.getVirtualBox();
+
+ #
+ # Import it as OVA.
+ #
+ try:
+ oAppliance = oVirtualBox.createAppliance();
+ except:
+ return reporter.errorXcpt('IVirtualBox::createAppliance failed');
+
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oAppliance.read(sOva), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'read "%s"' % (sOva,));
+ except:
+ return reporter.errorXcpt('IAppliance::read("%s") failed' % (sOva,));
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return False;
+
+ try:
+ oAppliance.interpret();
+ except:
+ return reporter.errorXcpt('IAppliance::interpret() failed on "%s"' % (sOva,));
+
+ #
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oAppliance.importMachines([]),
+ self.oTstDrv.oVBoxMgr, self.oTstDrv, 'importMachines "%s"' % (sOva,));
+ except:
+ return reporter.errorXcpt('IAppliance::importMachines failed on "%s"' % (sOva,));
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return False;
+
+ #
+ # Export the
+ #
+ ## @todo do more with this OVA. Like untaring it and loading it as an OVF. Export it and import it again.
+
+ return True;
+
+ def testImportOvaAsOvf(self, sOva):
+ """
+ Unpacks the OVA into a subdirectory in the scratch area and imports it as an OVF.
+ """
+ oVirtualBox = self.oTstDrv.oVBoxMgr.getVirtualBox();
+
+ sTmpDir = os.path.join(self.oTstDrv.sScratchPath, os.path.split(sOva)[1] + '-ovf');
+ sOvf = os.path.join(sTmpDir, os.path.splitext(os.path.split(sOva)[1])[0] + '.ovf');
+
+ #
+ # Unpack
+ #
+ try:
+ os.mkdir(sTmpDir, 0o755);
+ oTarFile = tarfile.open(sOva, 'r:*');
+ oTarFile.extractall(sTmpDir);
+ oTarFile.close();
+ except:
+ return reporter.errorXcpt('Unpacking "%s" to "%s" for OVF style importing failed' % (sOvf, sTmpDir,));
+
+ #
+ # Import.
+ #
+ try:
+ oAppliance2 = oVirtualBox.createAppliance();
+ except:
+ return reporter.errorXcpt('IVirtualBox::createAppliance failed (#2)');
+
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oAppliance2.read(sOvf), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'read "%s"' % (sOvf,));
+ except:
+ return reporter.errorXcpt('IAppliance::read("%s") failed' % (sOvf,));
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return False;
+
+ try:
+ oAppliance2.interpret();
+ except:
+ return reporter.errorXcpt('IAppliance::interpret() failed on "%s"' % (sOvf,));
+
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oAppliance2.importMachines([]),
+ self.oTstDrv.oVBoxMgr, self.oTstDrv, 'importMachines "%s"' % (sOvf,));
+ except:
+ return reporter.errorXcpt('IAppliance::importMachines failed on "%s"' % (sOvf,));
+ oProgress.wait();
+ if oProgress.logResult() is False:
+ return False;
+
+ return True;
+
+
+if __name__ == '__main__':
+ from tests.api.tdApi1 import tdApi1;
+ sys.exit(tdApi1([SubTstDrvAppliance1]).main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/api/tdMoveMedium1.py b/src/VBox/ValidationKit/tests/api/tdMoveMedium1.py
new file mode 100755
index 00000000..f1edfc62
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdMoveMedium1.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdMoveMedium1.py $
+
+"""
+VirtualBox Validation Kit - Medium Move Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+from testdriver import vboxwrappers
+
+
+class SubTstDrvMoveMedium1(base.SubTestDriverBase):
+ """
+ Sub-test driver for Medium Move Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, 'move-medium', oTstDrv)
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ return self.testMediumMove()
+
+ #
+ # Test execution helpers.
+ #
+
+ def moveTo(self, sLocation, aoMediumAttachments):
+ for oAttachment in aoMediumAttachments:
+ try:
+ oMedium = oAttachment.medium
+ reporter.log('Move medium "%s" to "%s"' % (oMedium.name, sLocation,))
+ except:
+ reporter.errorXcpt('failed to get the medium from the IMediumAttachment "%s"' % (oAttachment))
+
+ if self.oTstDrv.fpApiVer >= 5.3 and self.oTstDrv.uRevision > 124748:
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oMedium.moveTo(sLocation), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'move "%s"' % (oMedium.name,));
+ except:
+ return reporter.errorXcpt('Medium::moveTo("%s") for medium "%s" failed' % (sLocation, oMedium.name,));
+ else:
+ try:
+ oProgress = vboxwrappers.ProgressWrapper(oMedium.setLocation(sLocation), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'move "%s"' % (oMedium.name,));
+ except:
+ return reporter.errorXcpt('Medium::setLocation("%s") for medium "%s" failed' % (sLocation, oMedium.name,));
+
+
+ oProgress.wait()
+ if oProgress.logResult() is False:
+ return False
+ return True
+
+ def checkLocation(self, sLocation, aoMediumAttachments, asFiles):
+ fRc = True
+ for oAttachment in aoMediumAttachments:
+ sFilePath = os.path.join(sLocation, asFiles[oAttachment.port])
+ sActualFilePath = oAttachment.medium.location
+ if os.path.abspath(sFilePath) != os.path.abspath(sActualFilePath):
+ reporter.log('medium location expected to be "%s" but is "%s"' % (sFilePath, sActualFilePath))
+ fRc = False;
+ if not os.path.exists(sFilePath):
+ reporter.log('medium file does not exist at "%s"' % (sFilePath,))
+ fRc = False;
+ return fRc
+
+ def testMediumMove(self):
+ """
+ Test medium moving.
+ """
+ reporter.testStart('medium moving')
+
+ try:
+ oVM = self.oTstDrv.createTestVM('test-medium-move', 1, None, 4)
+ assert oVM is not None
+
+ # create hard disk images, one for each file-based backend, using the first applicable extension
+ fRc = True
+ oSession = self.oTstDrv.openSession(oVM)
+ aoDskFmts = self.oTstDrv.oVBoxMgr.getArray(self.oTstDrv.oVBox.systemProperties, 'mediumFormats')
+ asFiles = []
+ for oDskFmt in aoDskFmts:
+ aoDskFmtCaps = self.oTstDrv.oVBoxMgr.getArray(oDskFmt, 'capabilities')
+ if vboxcon.MediumFormatCapabilities_File not in aoDskFmtCaps \
+ or vboxcon.MediumFormatCapabilities_CreateDynamic not in aoDskFmtCaps:
+ continue
+ (asExts, aTypes) = oDskFmt.describeFileExtensions()
+ for i in range(0, len(asExts)): #pylint: disable=consider-using-enumerate
+ if aTypes[i] is vboxcon.DeviceType_HardDisk:
+ sExt = '.' + asExts[i]
+ break
+ if sExt is None:
+ fRc = False
+ break
+ sFile = 'Test' + str(len(asFiles)) + sExt
+ sHddPath = os.path.join(self.oTstDrv.sScratchPath, sFile)
+ oHd = oSession.createBaseHd(sHddPath, sFmt=oDskFmt.id, cb=1024*1024)
+ if oHd is None:
+ fRc = False
+ break
+
+ # attach HDD, IDE controller exists by default, but we use SATA just in case
+ sController='SATA Controller'
+ fRc = fRc and oSession.attachHd(sHddPath, sController, iPort = len(asFiles),
+ fImmutable=False, fForceResource=False)
+ if fRc:
+ asFiles.append(sFile)
+
+ fRc = fRc and oSession.saveSettings()
+
+ #create temporary subdirectory in the current working directory
+ sOrigLoc = self.oTstDrv.sScratchPath
+ sNewLoc = os.path.join(sOrigLoc, 'newLocation')
+ os.mkdir(sNewLoc, 0o775)
+
+ aoMediumAttachments = oVM.getMediumAttachmentsOfController(sController)
+ #case 1. Only path without file name, with trailing separator
+ fRc = self.moveTo(sNewLoc + os.sep, aoMediumAttachments) and fRc
+ fRc = self.checkLocation(sNewLoc, aoMediumAttachments, asFiles) and fRc
+
+ #case 2. Only path without file name, without trailing separator
+ fRc = self.moveTo(sOrigLoc, aoMediumAttachments) and fRc
+ fRc = self.checkLocation(sOrigLoc, aoMediumAttachments, asFiles) and fRc
+
+ #case 3. Path with file name
+ #The case supposes that user has passed a destination path with a file name but hasn't added an extension/suffix
+ #to this destination file. User supposes that the extension would be added automatically and to be the same as
+ #for the original file. Difficult case, apparently this case should follow mv(1) logic
+ #and the file name is processed as folder name (aka mv(1) logic).
+ #Be discussed.
+ fRc = self.moveTo(os.path.join(sNewLoc, 'newName'), aoMediumAttachments) and fRc
+ asNewFiles = ['newName' + os.path.splitext(s)[1] for s in asFiles]
+ fRc = self.checkLocation(os.path.join(sNewLoc, 'newName'), aoMediumAttachments, asFiles) and fRc
+
+ #after the case the destination path must be corrected
+ sNewLoc = os.path.join(sNewLoc, 'newName')
+
+ #case 4. Only file name
+ fRc = self.moveTo('onlyMediumName', aoMediumAttachments) and fRc
+ asNewFiles = ['onlyMediumName' + os.path.splitext(s)[1] for s in asFiles]
+ if self.oTstDrv.fpApiVer >= 5.3:
+ fRc = self.checkLocation(sNewLoc, aoMediumAttachments, asNewFiles) and fRc
+ else:
+ fRc = self.checkLocation(sNewLoc, aoMediumAttachments,
+ [s.replace('.hdd', '.parallels') for s in asNewFiles]) and fRc
+
+ #case 5. Move all files from a snapshot
+ fRc = fRc and oSession.takeSnapshot('Snapshot1')
+ if fRc:
+ aoMediumAttachments = oVM.getMediumAttachmentsOfController(sController)
+ asSnapFiles = [os.path.basename(o.medium.name) for o in aoMediumAttachments]
+ fRc = self.moveTo(sOrigLoc, aoMediumAttachments) and fRc
+ fRc = self.checkLocation(sOrigLoc, aoMediumAttachments, asSnapFiles) and fRc
+
+ fRc = oSession.close() and fRc
+
+ assert fRc is True
+ except:
+ reporter.errorXcpt()
+
+ return reporter.testDone()[1] == 0
+
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdApi1 import tdApi1; # pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvMoveMedium1]).main(sys.argv))
+
diff --git a/src/VBox/ValidationKit/tests/api/tdMoveVM1.py b/src/VBox/ValidationKit/tests/api/tdMoveVM1.py
new file mode 100755
index 00000000..62bc3bd9
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdMoveVM1.py
@@ -0,0 +1,734 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# "$Id: tdMoveVM1.py $"
+
+"""
+VirtualBox Validation Kit - VM Move Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+# Standard Python imports.
+import os
+import sys
+import time
+import shutil
+from collections import defaultdict
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+from testdriver import vboxwrappers
+from tdMoveMedium1 import SubTstDrvMoveMedium1; # pylint: disable=relative-import
+
+
+class SubTstDrvMoveVM1(base.SubTestDriverBase):
+ """
+ Sub-test driver for VM Move Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, 'move-vm', oTstDrv)
+
+ # Note! Hardcoded indexing in test code further down.
+ self.asRsrcs = [
+ os.path.join('5.3','isos','tdMoveVM1.iso'),
+ os.path.join('5.3','floppy','tdMoveVM1.img')
+ ];
+
+ self.asImagesNames = []
+ self.dsKeys = {
+ 'StandardImage': 'SATA Controller',
+ 'ISOImage': 'IDE Controller',
+ 'FloppyImage': 'Floppy Controller',
+ 'SettingsFile': 'Settings File',
+ 'LogFile': 'Log File',
+ 'SavedStateFile': 'Saved State File',
+ 'SnapshotFile': 'Snapshot File'
+ };
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ reporter.log('ValidationKit folder is "%s"' % (g_ksValidationKitDir,))
+ return self.testVMMove()
+
+ #
+ # Test execution helpers.
+ #
+
+ def createTestMachine(self):
+ """
+ Document me here, not with hashes above.
+ """
+ oVM = self.oTstDrv.createTestVM('test-vm-move', 1, None, 4)
+ if oVM is None:
+ return None
+
+ # create hard disk images, one for each file-based backend, using the first applicable extension
+ fRc = True
+ oSession = self.oTstDrv.openSession(oVM)
+ aoDskFmts = self.oTstDrv.oVBoxMgr.getArray(self.oTstDrv.oVBox.systemProperties, 'mediumFormats')
+
+ for oDskFmt in aoDskFmts:
+ aoDskFmtCaps = self.oTstDrv.oVBoxMgr.getArray(oDskFmt, 'capabilities')
+ if vboxcon.MediumFormatCapabilities_File not in aoDskFmtCaps \
+ or vboxcon.MediumFormatCapabilities_CreateDynamic not in aoDskFmtCaps:
+ continue
+ (asExts, aTypes) = oDskFmt.describeFileExtensions()
+ for i in range(0, len(asExts)): # pylint: disable=consider-using-enumerate
+ if aTypes[i] is vboxcon.DeviceType_HardDisk:
+ sExt = '.' + asExts[i]
+ break
+ if sExt is None:
+ fRc = False
+ break
+ sFile = 'test-vm-move' + str(len(self.asImagesNames)) + sExt
+ sHddPath = os.path.join(self.oTstDrv.sScratchPath, sFile)
+ oHd = oSession.createBaseHd(sHddPath, sFmt=oDskFmt.id, cb=1024*1024)
+ if oHd is None:
+ fRc = False
+ break
+
+ # attach HDD, IDE controller exists by default, but we use SATA just in case
+ sController = self.dsKeys['StandardImage']
+ fRc = fRc and oSession.attachHd(sHddPath, sController, iPort = len(self.asImagesNames),
+ fImmutable=False, fForceResource=False)
+ if fRc:
+ self.asImagesNames.append(sFile)
+
+ fRc = fRc and oSession.saveSettings()
+ fRc = oSession.close() and fRc
+
+ if fRc is False:
+ oVM = None
+
+ return oVM
+
+ def moveVMToLocation(self, sLocation, oVM):
+ """
+ Document me here, not with hashes above.
+ """
+ fRc = True
+ try:
+
+ ## @todo r=bird: Too much unncessary crap inside try clause. Only oVM.moveTo needs to be here.
+ ## Though, you could make an argument for oVM.name too, perhaps.
+
+ # move machine
+ reporter.log('Moving machine "%s" to the "%s"' % (oVM.name, sLocation))
+ sType = 'basic'
+ oProgress = vboxwrappers.ProgressWrapper(oVM.moveTo(sLocation, sType), self.oTstDrv.oVBoxMgr, self.oTstDrv,
+ 'moving machine "%s"' % (oVM.name,))
+
+ except:
+ return reporter.errorXcpt('Machine::moveTo("%s") for machine "%s" failed' % (sLocation, oVM.name,))
+
+ oProgress.wait()
+ if oProgress.logResult() is False:
+ fRc = False
+ reporter.log('Progress object returned False')
+ else:
+ fRc = True
+
+ return fRc
+
+ def checkLocation(self, oMachine, dsReferenceFiles):
+ """
+ Document me.
+
+ Prerequisites:
+ 1. All standard images are attached to SATA controller
+ 2. All ISO images are attached to IDE controller
+ 3. All floppy images are attached to Floppy controller
+ 4. The type defaultdict from collection is used here (some sort of multimap data structure)
+ 5. The dsReferenceFiles parameter here is the structure defaultdict(set):
+ [
+ ('StandardImage': ['somedisk.vdi', 'somedisk.vmdk',...]),
+ ('ISOImage': ['somedisk_1.iso','somedisk_2.iso',...]),
+ ('FloppyImage': ['somedisk_1.img','somedisk_2.img',...]),
+ ('SnapshotFile': ['snapshot file 1','snapshot file 2', ...]),
+ ('SettingsFile', ['setting file',...]),
+ ('SavedStateFile': ['state file 1','state file 2',...]),
+ ('LogFile': ['log file 1','log file 2',...]),
+ ]
+ """
+
+ fRc = True
+
+ for sKey, sValue in self.dsKeys.items():
+ aActuals = set()
+ aReferences = set()
+
+ # Check standard images locations, ISO files locations, floppy images locations, snapshots files locations
+ if sKey == 'StandardImage' or sKey == 'ISOImage' or sKey == 'FloppyImage':
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sValue) ##@todo r=bird: API call, try-except!
+ for oAttachment in aoMediumAttachments:
+ aActuals.add(oAttachment.medium.location)
+
+ elif sKey == 'SnapshotFile':
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aActuals = self.__getSnapshotsFiles(oMachine)
+
+ # Check setting file location
+ elif sKey == 'SettingsFile':
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aActuals.add(oMachine.settingsFilePath)
+
+ # Check saved state files location
+ elif sKey == 'SavedStateFile':
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aActuals = self.__getStatesFiles(oMachine)
+
+ # Check log files location
+ elif sKey == 'LogFile':
+ aReferences = dsReferenceFiles[sKey]
+ if aReferences:
+ aActuals = self.__getLogFiles(oMachine)
+
+ if aActuals:
+ reporter.log('Check %s' % (sKey))
+ intersection = aReferences.intersection(aActuals)
+ for eachItem in intersection:
+ reporter.log('Item location "%s" is correct' % (eachItem))
+
+ difference = aReferences.difference(aActuals)
+ for eachItem in difference:
+ reporter.log('Item location "%s" isn\'t correct' % (eachItem))
+
+ reporter.log('####### Reference locations: #######')
+ for eachItem in aReferences:
+ reporter.log(' "%s"' % (eachItem))
+
+ if len(intersection) != len(aActuals):
+ reporter.log('Not all items in the right location. Check it.')
+ fRc = False
+
+ return fRc
+
+ def checkAPIVersion(self):
+ return self.oTstDrv.fpApiVer >= 5.3;
+
+ def __getStatesFiles(self, oMachine, fPrint = False):
+ asStateFilesList = set()
+ sFolder = oMachine.snapshotFolder
+ for sFile in os.listdir(sFolder):
+ if sFile.endswith(".sav"):
+ sFullPath = os.path.join(sFolder, sFile)
+ asStateFilesList.add(sFullPath)
+ if fPrint is True:
+ reporter.log("State file is %s" % (sFullPath))
+ return asStateFilesList
+
+ def __getSnapshotsFiles(self, oMachine, fPrint = False):
+ asSnapshotsFilesList = set()
+ sFolder = oMachine.snapshotFolder
+ for sFile in os.listdir(sFolder):
+ if sFile.endswith(".sav") is False:
+ sFullPath = os.path.join(sFolder, sFile)
+ asSnapshotsFilesList.add(sFullPath)
+ if fPrint is True:
+ reporter.log("Snapshot file is %s" % (sFullPath))
+ return asSnapshotsFilesList
+
+ def __getLogFiles(self, oMachine, fPrint = False):
+ asLogFilesList = set()
+ sFolder = oMachine.logFolder
+ for sFile in os.listdir(sFolder):
+ if sFile.endswith(".log"):
+ sFullPath = os.path.join(sFolder, sFile)
+ asLogFilesList.add(sFullPath)
+ if fPrint is True:
+ reporter.log("Log file is %s" % (sFullPath))
+ return asLogFilesList
+
+
+ def __testScenario_2(self, oSession, oMachine, sNewLoc, sOldLoc):
+ """
+ All disks attached to VM are located inside the VM's folder.
+ There are no any snapshots and logs.
+ """
+
+ sController = self.dsKeys['StandardImage']
+ aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sController)
+ oSubTstDrvMoveMedium1Instance = SubTstDrvMoveMedium1(self.oTstDrv)
+ oSubTstDrvMoveMedium1Instance.moveTo(sOldLoc, aoMediumAttachments)
+
+ del oSubTstDrvMoveMedium1Instance
+
+ dsReferenceFiles = defaultdict(set)
+
+ for s in self.asImagesNames:
+ reporter.log('"%s"' % (s,))
+ dsReferenceFiles['StandardImage'].add(sNewLoc + os.sep + oMachine.name + os.sep + s)
+
+ sSettingFile = os.path.join(sNewLoc, os.path.join(oMachine.name, oMachine.name + '.vbox'))
+ dsReferenceFiles['SettingsFile'].add(sSettingFile)
+
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 2nd scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 2nd scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+
+ fRes = oSession.saveSettings()
+ if fRes is False:
+ reporter.log('2nd scenario: Couldn\'t save machine settings')
+
+ return fRc
+
+ def __testScenario_3(self, oSession, oMachine, sNewLoc):
+ """
+ There are snapshots
+ """
+
+ # At moment, it's used only one snapshot due to the difficulty to get
+ # all attachments of the machine (i.e. not only attached at moment)
+ cSnap = 1
+
+ for counter in range(1,cSnap+1):
+ strSnapshot = 'Snapshot' + str(counter)
+ fRc = oSession.takeSnapshot(strSnapshot)
+ if fRc is False:
+ reporter.testFailure('3rd scenario: Can\'t take snapshot "%s"' % (strSnapshot,))
+
+ dsReferenceFiles = defaultdict(set)
+
+ sController = self.dsKeys['StandardImage']
+ aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sController)
+ if fRc is True:
+ for oAttachment in aoMediumAttachments:
+ sRes = oAttachment.medium.location.rpartition(os.sep)
+ dsReferenceFiles['SnapshotFile'].add(sNewLoc + os.sep + oMachine.name + os.sep +
+ 'Snapshots' + os.sep + sRes[2])
+
+ sSettingFile = os.path.join(sNewLoc, os.path.join(oMachine.name, oMachine.name + '.vbox'))
+ dsReferenceFiles['SettingsFile'].add(sSettingFile)
+
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 3rd scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 3rd scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+
+ fRes = oSession.saveSettings()
+ if fRes is False:
+ reporter.log('3d scenario: Couldn\'t save machine settings')
+
+ return fRc
+
+ def __testScenario_4(self, oMachine, sNewLoc):
+ """
+ There are one or more save state files in the snapshots folder
+ and some files in the logs folder.
+ Here we run VM, next stop it in the "save" state.
+ And next move VM
+ """
+
+ # Run VM and get new Session object.
+ oSession = self.oTstDrv.startVm(oMachine)
+
+ # Some time interval should be here for not closing VM just after start.
+ time.sleep(1)
+
+ if oMachine.state != self.oTstDrv.oVBoxMgr.constants.MachineState_Running:
+ reporter.log("Machine '%s' is not Running" % (oMachine.name))
+ fRc = False
+
+ # Call Session::saveState(), already closes session unless it failed.
+ fRc = oSession.saveState()
+ if fRc is True:
+ reporter.log("Machine is in saved state")
+
+ fRc = self.oTstDrv.terminateVmBySession(oSession)
+
+ if fRc is True or False:
+ # Create a new Session object for moving VM.
+ oSession = self.oTstDrv.openSession(oMachine)
+
+ # Always clear before each scenario.
+ dsReferenceFiles = defaultdict(set)
+
+ asLogs = self.__getLogFiles(oMachine)
+ for sFile in asLogs:
+ sRes = sFile.rpartition(os.sep)
+ dsReferenceFiles['LogFile'].add(sNewLoc + os.sep + oMachine.name + os.sep + 'Logs' + os.sep + sRes[2])
+
+ asStates = self.__getStatesFiles(oMachine)
+ for sFile in asStates:
+ sRes = sFile.rpartition(os.sep)
+ dsReferenceFiles['SavedStateFile'].add(sNewLoc + os.sep + oMachine.name + os.sep + 'Snapshots' + os.sep + sRes[2])
+
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 4th scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 4th scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+
+ # cleaning up: get rid of saved state
+ fRes = oSession.discardSavedState(True)
+ if fRes is False:
+ reporter.log('4th scenario: Failed to discard the saved state of machine')
+
+ fRes = oSession.close()
+ if fRes is False:
+ reporter.log('4th scenario: Couldn\'t close machine session')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 4th scenario: Terminate machine by session failed... !!!!!!!!!!!!!!!!!!')
+
+ return fRc
+
+ def __testScenario_5(self, oMachine, sNewLoc, sOldLoc):
+ """
+ There is an ISO image (.iso) attached to the VM.
+ Prerequisites - there is IDE Controller and there are no any images attached to it.
+ """
+
+ fRc = True
+ sISOImageName = 'tdMoveVM1.iso'
+
+ # Always clear before each scenario.
+ dsReferenceFiles = defaultdict(set)
+
+ # Create a new Session object.
+ oSession = self.oTstDrv.openSession(oMachine)
+
+ sISOLoc = self.asRsrcs[0] # '5.3/isos/tdMoveVM1.iso'
+ reporter.log("sHost is '%s', sResourcePath is '%s'" % (self.oTstDrv.sHost, self.oTstDrv.sResourcePath))
+ sISOLoc = self.oTstDrv.getFullResourceName(sISOLoc)
+ reporter.log("sISOLoc is '%s'" % (sISOLoc,))
+
+ if not os.path.exists(sISOLoc):
+ reporter.log('ISO file does not exist at "%s"' % (sISOLoc,))
+ fRc = False
+
+ # Copy ISO image from the common resource folder into machine folder.
+ shutil.copy(sISOLoc, sOldLoc)
+
+ # Attach ISO image to the IDE controller.
+ if fRc is True:
+ # Set actual ISO location.
+ sISOLoc = sOldLoc + os.sep + sISOImageName
+ reporter.log("sISOLoc is '%s'" % (sISOLoc,))
+ if not os.path.exists(sISOLoc):
+ reporter.log('ISO file does not exist at "%s"' % (sISOLoc,))
+ fRc = False
+
+ sController=self.dsKeys['ISOImage']
+ aoMediumAttachments = oMachine.getMediumAttachmentsOfController(sController)
+ iPort = len(aoMediumAttachments)
+ fRc = oSession.attachDvd(sISOLoc, sController, iPort, iDevice = 0)
+ dsReferenceFiles['ISOImage'].add(os.path.join(os.path.join(sNewLoc, oMachine.name), sISOImageName))
+
+ if fRc is True:
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 5th scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 5th scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 5th scenario: Attach ISO image failed... !!!!!!!!!!!!!!!!!!')
+
+ # Detach ISO image.
+ fRes = oSession.detachHd(sController, iPort, 0)
+ if fRes is False:
+ reporter.log('5th scenario: Couldn\'t detach image from the controller %s '
+ 'port %s device %s' % (sController, iPort, 0))
+
+ fRes = oSession.saveSettings()
+ if fRes is False:
+ reporter.log('5th scenario: Couldn\'t save machine settings')
+
+ fRes = oSession.close()
+ if fRes is False:
+ reporter.log('5th scenario: Couldn\'t close machine session')
+
+ return fRc
+
+ def __testScenario_6(self, oMachine, sNewLoc, sOldLoc):
+ """
+ There is a floppy image (.img) attached to the VM.
+ Prerequisites - there is Floppy Controller and there are no any images attached to it.
+ """
+
+ fRc = True
+
+ # Always clear before each scenario.
+ dsReferenceFiles = defaultdict(set)
+
+ # Create a new Session object.
+ oSession = self.oTstDrv.openSession(oMachine)
+
+ sFloppyLoc = self.asRsrcs[1] # '5.3/floppy/tdMoveVM1.img'
+ sFloppyLoc = self.oTstDrv.getFullResourceName(sFloppyLoc)
+
+ if not os.path.exists(sFloppyLoc):
+ reporter.log('Floppy disk does not exist at "%s"' % (sFloppyLoc,))
+ fRc = False
+
+ # Copy floppy image from the common resource folder into machine folder.
+ shutil.copy(sFloppyLoc, sOldLoc)
+
+ # Attach floppy image.
+ if fRc is True:
+ # Set actual floppy location.
+ sFloppyImageName = 'tdMoveVM1.img'
+ sFloppyLoc = sOldLoc + os.sep + sFloppyImageName
+ sController=self.dsKeys['FloppyImage']
+ fRc = fRc and oSession.attachFloppy(sFloppyLoc, sController, 0, 0)
+ dsReferenceFiles['FloppyImage'].add(os.path.join(os.path.join(sNewLoc, oMachine.name), sFloppyImageName))
+
+ if fRc is True:
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 6th scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 6th scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 6th scenario: Attach floppy image failed... !!!!!!!!!!!!!!!!!!')
+
+ # Detach floppy image.
+ fRes = oSession.detachHd(sController, 0, 0)
+ if fRes is False:
+ reporter.log('6th scenario: Couldn\'t detach image from the controller %s port %s device %s' % (sController, 0, 0))
+
+ fRes = oSession.saveSettings()
+ if fRes is False:
+ reporter.testFailure('6th scenario: Couldn\'t save machine settings')
+
+ fRes = oSession.close()
+ if fRes is False:
+ reporter.log('6th scenario: Couldn\'t close machine session')
+ return fRc
+
+
+ def testVMMove(self):
+ """
+ Test machine moving.
+ """
+ reporter.testStart('machine moving')
+
+ if not self.oTstDrv.importVBoxApi():
+ return False
+
+ fSupported = self.checkAPIVersion()
+
+ if fSupported is False:
+ reporter.log('API version %s is too old. Just skip this test.' % (self.oTstDrv.fpApiVer))
+ return reporter.testDone()[1] == 0
+ else:
+ reporter.log('API version is "%s".' % (self.oTstDrv.fpApiVer))
+
+ # Scenarios
+ # 1. All disks attached to VM are located outside the VM's folder.
+ # There are no any snapshots and logs.
+ # In this case only VM setting file should be moved (.vbox file)
+ #
+ # 2. All disks attached to VM are located inside the VM's folder.
+ # There are no any snapshots and logs.
+ #
+ # 3. There are snapshots.
+ #
+ # 4. There are one or more save state files in the snapshots folder
+ # and some files in the logs folder.
+ #
+ # 5. There is an ISO image (.iso) attached to the VM.
+ #
+ # 6. There is a floppy image (.img) attached to the VM.
+ #
+ # 7. There are shareable disk and immutable disk attached to the VM.
+
+ try:
+ # Create test machine.
+ oMachine = self.createTestMachine()
+ if oMachine is None:
+ reporter.error('Failed to create test machine')
+
+ # Create temporary subdirectory in the current working directory.
+ sOrigLoc = self.oTstDrv.sScratchPath
+ sBaseLoc = os.path.join(sOrigLoc, 'moveFolder')
+ os.mkdir(sBaseLoc, 0o775)
+
+ # lock machine
+ # get session machine
+ oSession = self.oTstDrv.openSession(oMachine)
+ fRc = True
+
+ sNewLoc = sBaseLoc + os.sep
+
+ dsReferenceFiles = defaultdict(set)
+
+ #
+ # 1. case:
+ #
+ # All disks attached to VM are located outside the VM's folder.
+ # There are no any snapshots and logs.
+ # In this case only VM setting file should be moved (.vbox file)
+ #
+ for s in self.asImagesNames:
+ reporter.log('"%s"' % (s,))
+ dsReferenceFiles['StandardImage'].add(os.path.join(sOrigLoc, s))
+
+ sSettingFile = os.path.join(sNewLoc, os.path.join(oMachine.name, oMachine.name + '.vbox'))
+ dsReferenceFiles['SettingsFile'].add(sSettingFile)
+
+ fRc = self.moveVMToLocation(sNewLoc, oSession.o.machine)
+
+ if fRc is True:
+ fRc = self.checkLocation(oSession.o.machine, dsReferenceFiles)
+ if fRc is False:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 1st scenario: Check locations failed... !!!!!!!!!!!!!!!!!!')
+ return reporter.testDone()[1] == 0
+ else:
+ reporter.testFailure('!!!!!!!!!!!!!!!!!! 1st scenario: Move VM failed... !!!!!!!!!!!!!!!!!!')
+ return reporter.testDone()[1] == 0
+
+ fRc = oSession.saveSettings()
+ if fRc is False:
+ reporter.testFailure('1st scenario: Couldn\'t save machine settings')
+
+ #
+ # 2. case:
+ #
+ # All disks attached to VM are located inside the VM's folder.
+ # There are no any snapshots and logs.
+ #
+ sOldLoc = sNewLoc + oMachine.name + os.sep
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_2d_scenario')
+ os.mkdir(sNewLoc, 0o775)
+
+ fRc = self.__testScenario_2(oSession, oMachine, sNewLoc, sOldLoc)
+ if fRc is False:
+ return reporter.testDone()[1] == 0
+
+ #
+ # 3. case:
+ #
+ # There are snapshots.
+ #
+ sOldLoc = sNewLoc + oMachine.name + os.sep
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_3d_scenario')
+ os.mkdir(sNewLoc, 0o775)
+
+ fRc = self.__testScenario_3(oSession, oMachine, sNewLoc)
+ if fRc is False:
+ return reporter.testDone()[1] == 0
+
+ #
+ # 4. case:
+ #
+ # There are one or more save state files in the snapshots folder
+ # and some files in the logs folder.
+ # Here we run VM, next stop it in the "save" state.
+ # And next move VM
+ #
+ sOldLoc = sNewLoc + oMachine.name + os.sep
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_4th_scenario')
+ os.mkdir(sNewLoc, 0o775)
+
+ # Close Session object because after starting VM we get new instance of session
+ fRc = oSession.close() and fRc
+ if fRc is False:
+ reporter.log('Couldn\'t close machine session')
+
+ del oSession
+
+ fRc = self.__testScenario_4(oMachine, sNewLoc)
+ if fRc is False:
+ return reporter.testDone()[1] == 0
+
+ #
+ # 5. case:
+ #
+ # There is an ISO image (.iso) attached to the VM.
+ # Prerequisites - there is IDE Controller and there are no any images attached to it.
+ #
+ sOldLoc = sNewLoc + os.sep + oMachine.name
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_5th_scenario')
+ os.mkdir(sNewLoc, 0o775)
+ fRc = self.__testScenario_5(oMachine, sNewLoc, sOldLoc)
+ if fRc is False:
+ return reporter.testDone()[1] == 0
+
+ #
+ # 6. case:
+ #
+ # There is a floppy image (.img) attached to the VM.
+ # Prerequisites - there is Floppy Controller and there are no any images attached to it.
+ #
+ sOldLoc = sNewLoc + os.sep + oMachine.name
+ sNewLoc = os.path.join(sOrigLoc, 'moveFolder_6th_scenario')
+ os.mkdir(sNewLoc, 0o775)
+ fRc = self.__testScenario_6(oMachine, sNewLoc, sOldLoc)
+ if fRc is False:
+ return reporter.testDone()[1] == 0
+
+# #
+# # 7. case:
+# #
+# # There are shareable disk and immutable disk attached to the VM.
+# #
+# fRc = fRc and oSession.saveSettings()
+# if fRc is False:
+# reporter.log('Couldn\'t save machine settings')
+#
+
+ assert fRc is True
+ except:
+ reporter.errorXcpt()
+
+ return reporter.testDone()[1] == 0
+
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdApi1 import tdApi1; # pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvMoveVM1]).main(sys.argv))
+
diff --git a/src/VBox/ValidationKit/tests/api/tdPython1.py b/src/VBox/ValidationKit/tests/api/tdPython1.py
new file mode 100755
index 00000000..8080fe44
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdPython1.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdPython1.py $
+
+"""
+VirtualBox Validation Kit - Python Bindings Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import time
+import threading
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import base;
+from testdriver import reporter;
+
+
+class SubTstDrvPython1(base.SubTestDriverBase):
+ """
+ Sub-test driver for Python Bindings Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, 'python-binding', oTstDrv)
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ return self.testEventQueueWaiting() \
+ and self.testEventQueueInterrupt();
+
+ #
+ # Test execution helpers.
+ #
+
+ def testEventQueueWaitingThreadProc(self):
+ """ Thread procedure for checking that waitForEvents fails when not called by the main thread. """
+ try:
+ rc2 = self.oTstDrv.oVBoxMgr.waitForEvents(0);
+ except:
+ return True;
+ reporter.error('waitForEvents() returned "%s" when called on a worker thread, expected exception.' % (rc2,));
+ return False;
+
+ def testEventQueueWaiting(self):
+ """
+ Test event queue waiting.
+ """
+ reporter.testStart('waitForEvents');
+
+ # Check return values and such.
+ for cMsTimeout in (0, 1, 2, 3, 256, 1000, 0):
+ iLoop = 0;
+ while True:
+ try:
+ rc = self.oTstDrv.oVBoxMgr.waitForEvents(cMsTimeout);
+ except:
+ reporter.errorXcpt();
+ break;
+ if not isinstance(rc, int):
+ reporter.error('waitForEvents returns non-integer type');
+ break;
+ if rc == 1:
+ break;
+ if rc != 0:
+ reporter.error('waitForEvents returns "%s", expected 0 or 1' % (rc,));
+ break;
+ iLoop += 1;
+ if iLoop > 10240:
+ reporter.error('waitForEvents returns 0 (success) %u times. '
+ 'Expected 1 (timeout/interrupt) after a call or two.'
+ % (iLoop,));
+ break;
+ if reporter.testErrorCount() != 0:
+ break;
+
+ # Check that we get an exception when trying to call the method from
+ # a different thread.
+ reporter.log('If running a debug build, you will see an ignored assertion now. Please ignore it.')
+ sVBoxAssertSaved = os.environ.get('VBOX_ASSERT', 'breakpoint');
+ os.environ['VBOX_ASSERT'] = 'ignore';
+ oThread = threading.Thread(target=self.testEventQueueWaitingThreadProc);
+ oThread.start();
+ oThread.join();
+ os.environ['VBOX_ASSERT'] = sVBoxAssertSaved;
+
+ return reporter.testDone()[1] == 0;
+
+ def interruptWaitEventsThreadProc(self):
+ """ Thread procedure that's used for waking up the main thread. """
+ time.sleep(2);
+ try:
+ rc2 = self.oTstDrv.oVBoxMgr.interruptWaitEvents();
+ except:
+ reporter.errorXcpt();
+ else:
+ if rc2 is True:
+ return True;
+ reporter.error('interruptWaitEvents returned "%s" when called from other thread, expected True' % (rc2,));
+ return False;
+
+ def testEventQueueInterrupt(self):
+ """
+ Test interrupting an event queue wait.
+ """
+ reporter.testStart('interruptWait');
+
+ # interrupt ourselves first and check the return value.
+ for i in range(0, 10):
+ try:
+ rc = self.oTstDrv.oVBoxMgr.interruptWaitEvents();
+ except:
+ reporter.errorXcpt();
+ break;
+ if rc is not True:
+ reporter.error('interruptWaitEvents returned "%s" expected True' % (rc,));
+ break
+
+ if reporter.testErrorCount() == 0:
+ #
+ # Interrupt a waitForEvents call.
+ #
+ # This test ASSUMES that no other events are posted to the thread's
+ # event queue once we've drained it. Also ASSUMES the box is
+ # relatively fast and not too busy because we're timing sensitive.
+ #
+ for i in range(0, 4):
+ # Try quiesce the event queue.
+ for _ in range(1, 100):
+ self.oTstDrv.oVBoxMgr.waitForEvents(0);
+
+ # Create a thread that will interrupt us in 2 seconds.
+ try:
+ oThread = threading.Thread(target=self.interruptWaitEventsThreadProc);
+ oThread.setDaemon(False);
+ except:
+ reporter.errorXcpt();
+ break;
+
+ cMsTimeout = 20000;
+ if i == 2:
+ cMsTimeout = -1;
+ elif i == 3:
+ cMsTimeout = -999999;
+
+ # Do the wait.
+ oThread.start();
+ msNow = base.timestampMilli();
+ try:
+ rc = self.oTstDrv.oVBoxMgr.waitForEvents(cMsTimeout);
+ except:
+ reporter.errorXcpt();
+ else:
+ msElapsed = base.timestampMilli() - msNow;
+
+ # Check the return code and elapsed time.
+ if not isinstance(rc, int):
+ reporter.error('waitForEvents returns non-integer type after %u ms, expected 1' % (msElapsed,));
+ elif rc != 1:
+ reporter.error('waitForEvents returned "%s" after %u ms, expected 1' % (rc, msElapsed));
+ if msElapsed > 15000:
+ reporter.error('waitForEvents after %u ms, expected just above 2-3 seconds' % (msElapsed,));
+ elif msElapsed < 100:
+ reporter.error('waitForEvents after %u ms, expected more than 100 ms.' % (msElapsed,));
+
+ oThread.join();
+ oThread = None;
+ if reporter.testErrorCount() != 0:
+ break;
+ reporter.log('Iteration %u was successful...' % (i + 1,));
+ return reporter.testDone()[1] == 0;
+
+
+if __name__ == '__main__':
+ from tests.api.tdApi1 import tdApi1;
+ sys.exit(tdApi1([SubTstDrvPython1]).main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py b/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py
new file mode 100755
index 00000000..398feb8b
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdTreeDepth1.py $
+
+"""
+VirtualBox Validation Kit - Medium and Snapshot Tree Depth Test #1
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+
+
+class SubTstDrvTreeDepth1(base.SubTestDriverBase):
+ """
+ Sub-test driver for Medium and Snapshot Tree Depth Test #1.
+ """
+
+ def __init__(self, oTstDrv):
+ base.SubTestDriverBase.__init__(self, 'tree-depth', oTstDrv)
+
+ def testIt(self):
+ """
+ Execute the sub-testcase.
+ """
+ return self.testMediumTreeDepth() \
+ and self.testSnapshotTreeDepth()
+
+ #
+ # Test execution helpers.
+ #
+
+ def testMediumTreeDepth(self):
+ """
+ Test medium tree depth.
+ """
+ reporter.testStart('mediumTreeDepth')
+
+ try:
+ oVM = self.oTstDrv.createTestVM('test-medium', 1, None, 4)
+ assert oVM is not None
+
+ # create chain with 300 disk images (medium tree depth limit)
+ fRc = True
+ oSession = self.oTstDrv.openSession(oVM)
+ for i in range(1, 301):
+ sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'Test' + str(i) + '.vdi')
+ if i is 1:
+ oHd = oSession.createBaseHd(sHddPath, cb=1024*1024)
+ else:
+ oHd = oSession.createDiffHd(oHd, sHddPath)
+ if oHd is None:
+ fRc = False
+ break
+
+ # modify the VM config, attach HDD
+ fRc = fRc and oSession.attachHd(sHddPath, sController='SATA Controller', fImmutable=False, fForceResource=False)
+ fRc = fRc and oSession.saveSettings()
+ fRc = oSession.close() and fRc
+
+ # unregister and re-register to test loading of settings
+ sSettingsFile = oVM.settingsFilePath
+ reporter.log('unregistering VM')
+ oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone)
+ oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox()
+ reporter.log('opening VM %s, testing config reading' % (sSettingsFile))
+ oVM = oVBox.openMachine(sSettingsFile)
+
+ assert fRc is True
+ except:
+ reporter.errorXcpt()
+
+ return reporter.testDone()[1] == 0
+
+ def testSnapshotTreeDepth(self):
+ """
+ Test snapshot tree depth.
+ """
+ reporter.testStart('snapshotTreeDepth')
+
+ try:
+ oVM = self.oTstDrv.createTestVM('test-snap', 1, None, 4)
+ assert oVM is not None
+
+ # modify the VM config, create and attach empty HDD
+ oSession = self.oTstDrv.openSession(oVM)
+ sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'TestSnapEmpty.vdi')
+ fRc = True
+ fRc = fRc and oSession.createAndAttachHd(sHddPath, cb=1024*1024, sController='SATA Controller', fImmutable=False)
+ fRc = fRc and oSession.saveSettings()
+
+ # take 250 snapshots (snapshot tree depth limit)
+ for i in range(1, 251):
+ fRc = fRc and oSession.takeSnapshot('Snapshot ' + str(i))
+ fRc = oSession.close() and fRc
+
+ # unregister and re-register to test loading of settings
+ sSettingsFile = oVM.settingsFilePath
+ reporter.log('unregistering VM')
+ oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone)
+ oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox()
+ reporter.log('opening VM %s, testing config reading' % (sSettingsFile))
+ oVM = oVBox.openMachine(sSettingsFile)
+
+ assert fRc is True
+ except:
+ reporter.errorXcpt()
+
+ return reporter.testDone()[1] == 0
+
+
+if __name__ == '__main__':
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+ from tdApi1 import tdApi1; # pylint: disable=relative-import
+ sys.exit(tdApi1([SubTstDrvTreeDepth1]).main(sys.argv))
+
diff --git a/src/VBox/ValidationKit/tests/audio/Makefile.kup b/src/VBox/ValidationKit/tests/audio/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/audio/Makefile.kup
diff --git a/src/VBox/ValidationKit/tests/audio/tdGuestHostTimings.py b/src/VBox/ValidationKit/tests/audio/tdGuestHostTimings.py
new file mode 100755
index 00000000..34b2bef2
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/audio/tdGuestHostTimings.py
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+# $Id: tdGuestHostTimings.py $
+
+"""
+????????
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+import os
+import sys
+import time
+import subprocess
+import re
+import time
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter
+from testdriver import base
+from testdriver import vbox
+from testdriver import vboxcon
+from testdriver import vboxtestvms
+
+class tdGuestHostTimings(vbox.TestDriver): # pylint: disable=R0902
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.sSessionTypeDef = 'gui';
+
+ self.oTestVmSet = self.oTestVmManager.getStandardVmSet('nat') ## ???
+
+ # Use the command line "--test-vms mw7x64 execute" to run the only "mw7x64" VM
+ oTestVm = vboxtestvms.TestVm('mw7x64', oSet = self.oTestVmSet, sHd = 'mw7x64.vdi',
+ sKind = 'Windows7', acCpusSup = range(1, 2), fIoApic = True, sFirmwareType = 'bios',
+ asParavirtModesSup = ['hyperv'], asVirtModesSup = ['hwvirt-np'],
+ sHddControllerType = 'SATA Controller');
+
+ self.oTestVmSet.aoTestVms.append(oTestVm);
+
+ self.sVMname = None
+
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdGuestHostTimings Options:');
+ reporter.log(' --runningvmname <vmname>');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ if asArgs[iArg] == '--runningvmname':
+ iArg += 1
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "----runningvmname" needs VM name')
+
+ self.sVMname = asArgs[iArg]
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg)
+ return iArg + 1
+
+ def actionConfig(self):
+ return True
+
+ def actionExecute(self):
+ #self.sTempPathHost = os.environ.get("IPRT_TMPDIR")
+ self.sTempPathHost = os.path.normpath(os.environ.get("TEMP") + "/VBoxAudioValKit")
+
+ if self.sVMname is None:
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+ else:
+ return self.actionExecuteOnRunnigVM()
+
+ def doTest(self, oSession):
+ oConsole = oSession.console
+ oGuest = oConsole.guest
+
+ sOSTypeId = oGuest.OSTypeId.lower()
+ if sOSTypeId.find("win") == -1 :
+ reporter.log("Only Windows guests are currently supported")
+ reporter.testDone()
+ return True
+
+ oGuestSession = oGuest.createSession("Administrator", "password", "", "Audio Validation Kit")
+ guestSessionWaitResult = oGuestSession.waitFor(self.oVBoxMgr.constants.GuestSessionWaitResult_Start, 2000)
+ reporter.log("guestSessionWaitResult = %d" % guestSessionWaitResult)
+
+ for duration in range(3, 6):
+ reporter.testStart("Checking for duration of " + str(duration) + " seconds")
+ sPathToPlayer = "D:\\win\\" + ("amd64" if (sOSTypeId.find('_64') >= 0) else "x86") + "\\ntPlayToneWaveX.exe"
+ oProcess = oGuestSession.processCreate(sPathToPlayer, ["xxx0", "--total-duration-in-secs", str(duration)], [], [], 0)
+ processWaitResult = oProcess.waitFor(self.oVBoxMgr.constants.ProcessWaitForFlag_Start, 1000)
+ reporter.log("Started: pid %d, waitResult %d" % (oProcess.PID, processWaitResult))
+
+ processWaitResult = oProcess.waitFor(self.oVBoxMgr.constants.ProcessWaitForFlag_Terminate, 2 * duration * 1000)
+ reporter.log("Terminated: pid %d, waitResult %d" % (oProcess.PID, processWaitResult))
+ time.sleep(1) # Give audio backend sometime to save a stream to .wav file
+
+ absFileName = self.seekLatestAudioFileName(oGuestSession, duration)
+
+ if absFileName is None:
+ reporter.testFailure("Unable to find audio file")
+ continue
+
+ reporter.log("Checking audio file '" + absFileName + "'")
+
+ diff = self.checkGuestHostTimings(absFileName + ".timing")
+ if diff is not None:
+ if diff > 0.0: # Guest sends data quicker than a host can play
+ if diff > 0.01: # 1% is probably good threshold here
+ reporter.testFailure("Guest sends audio buffers too quickly")
+ else:
+ diff = -diff; # Much worse case: guest sends data very slow, host feels starvation
+ if diff > 0.005: # 0.5% is probably good threshold here
+ reporter.testFailure("Guest sends audio buffers too slowly")
+
+ reporter.testDone()
+ else:
+ reporter.testFailure("Unable to parse a file with timings")
+
+ oGuestSession.close()
+
+ del oGuest
+ del oConsole
+
+ return True
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ #self.logVmInfo(oVM)
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName,
+ fCdWait = True,
+ cMsTimeout = 60 * 1000)
+ if oSession is not None and oTxsSession is not None:
+ # Wait until guest reported success
+ reporter.log('Guest started. Connection to TXS service established.')
+ self.doTest(oSessionWrapper.o)
+
+ return True
+
+ def actionExecuteOnRunnigVM(self):
+ if not self.importVBoxApi():
+ return False;
+
+ oVirtualBox = self.oVBoxMgr.getVirtualBox()
+ oMachine = oVirtualBox.findMachine(self.sVMname)
+
+ if oMachine == None:
+ reporter.log("Machine '%s' is unknown" % (oMachine.name))
+ return False
+
+ if oMachine.state != self.oVBoxMgr.constants.MachineState_Running:
+ reporter.log("Machine '%s' is not Running" % (oMachine.name))
+ return False
+
+ oSession = self.oVBoxMgr.mgr.getSessionObject(oVirtualBox)
+ oMachine.lockMachine(oSession, self.oVBoxMgr.constants.LockType_Shared)
+
+ self.doTest(oSession);
+
+ oSession.unlockMachine()
+
+ del oSession
+ del oMachine
+ del oVirtualBox
+ return True
+
+ def seekLatestAudioFileName(self, guestSession, duration):
+
+ listOfFiles = os.listdir(self.sTempPathHost)
+ # Assuming that .wav files are named like 2016-11-15T12_08_27.669573100Z.wav by VBOX audio backend
+ # So that sorting by name = sorting by creation date
+ listOfFiles.sort(reverse = True)
+
+ for fileName in listOfFiles:
+ if not fileName.endswith(".wav"):
+ continue
+
+ absFileName = os.path.join(self.sTempPathHost, fileName)
+
+ # Ignore too small wav files (usually uncompleted audio streams)
+ statInfo = os.stat(absFileName)
+ if statInfo.st_size > 100:
+ return absFileName
+
+ return
+
+ def checkGuestHostTimings(self, absFileName):
+ with open(absFileName) as f:
+ for line_terminated in f:
+ line = line_terminated.rstrip('\n')
+
+ reporter.log("Last line is: " + line)
+ matchObj = re.match( r'(\d+) (\d+)', line, re.I)
+ if matchObj:
+ hostTime = int(matchObj.group(1))
+ guestTime = int(matchObj.group(2))
+
+ diff = float(guestTime - hostTime) / hostTime
+ return diff
+
+ return
+
+if __name__ == '__main__':
+ sys.exit(tdGuestHostTimings().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/autostart/Makefile.kmk b/src/VBox/ValidationKit/tests/autostart/Makefile.kmk
new file mode 100644
index 00000000..3702d5d7
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/autostart/Makefile.kmk
@@ -0,0 +1,41 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Autostart.
+#
+
+#
+# Copyright (C) 2013-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsAutostart
+ValidationKitTestsAutostart_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsAutostart_INST = $(INST_VALIDATIONKIT)tests/autostart/
+ValidationKitTestsAutostart_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdAutostart1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsAutostart_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/autostart/tdAutostart1.py b/src/VBox/ValidationKit/tests/autostart/tdAutostart1.py
new file mode 100755
index 00000000..354e1862
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/autostart/tdAutostart1.py
@@ -0,0 +1,699 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+AUtostart testcase using.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2013-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Id: tdAutostart1.py $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+import re;
+import array;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+class VBoxManageStdOutWrapper(object):
+ """ Parser for VBoxManage list runningvms """
+ def __init__(self):
+ self.sVmRunning = '';
+
+ def read(self, cb):
+ """file.read"""
+ _ = cb;
+ return "";
+
+ def write(self, sText):
+ """VBoxManage stdout write"""
+ if isinstance(sText, array.array):
+ sText = sText.tostring();
+
+ asLines = sText.splitlines();
+ for sLine in asLines:
+ sLine = sLine.strip();
+
+ # Extract the value
+ idxVmNameStart = sLine.find('"');
+ if idxVmNameStart == -1:
+ raise Exception('VBoxManageStdOutWrapper: Invalid output');
+
+ idxVmNameStart += 1;
+ idxVmNameEnd = idxVmNameStart;
+ while sLine[idxVmNameEnd] != '"':
+ idxVmNameEnd += 1;
+
+ self.sVmRunning = sLine[idxVmNameStart:idxVmNameEnd];
+ reporter.log('Logging: ' + self.sVmRunning);
+
+ return None;
+
+class tdAutostartOs(object):
+ """
+ Base autostart helper class to provide common methods.
+ """
+
+ def _findFile(self, sRegExp, sTestBuildDir):
+ """
+ Returns a filepath based on the given regex and path to look into
+ or None if no matching file is found.
+ """
+
+ oRegExp = re.compile(sRegExp);
+
+ asFiles = os.listdir(sTestBuildDir);
+
+ for sFile in asFiles:
+ if oRegExp.match(os.path.basename(sFile)) and os.path.exists(sTestBuildDir + '/' + sFile):
+ return sTestBuildDir + '/' + sFile;
+
+ reporter.error('Failed to find a file matching "%s" in %s.' % (sRegExp, sTestBuildDir));
+ return None;
+
+ def _createAutostartCfg(self, sDefaultPolicy = 'allow', asUserAllow = (), asUserDeny = ()):
+ """
+ Creates a autostart config for VirtualBox
+ """
+
+ sVBoxCfg = 'default_policy=' + sDefaultPolicy + '\n';
+
+ for sUserAllow in asUserAllow:
+ sVBoxCfg = sVBoxCfg + sUserAllow + ' = {\n allow = true\n }\n';
+
+ for sUserDeny in asUserDeny:
+ sVBoxCfg = sVBoxCfg + sUserDeny + ' = {\n allow = false\n }\n';
+
+ return sVBoxCfg;
+
+class tdAutostartOsLinux(tdAutostartOs):
+ """
+ Autostart support methods for Linux guests.
+ """
+
+ def __init__(self, oTestDriver, sTestBuildDir):
+ tdAutostartOs.__init__(self);
+ self.sTestBuild = self._findFile('^VirtualBox-.*\\.run$', sTestBuildDir);
+ self.oTestDriver = oTestDriver;
+
+ def installVirtualBox(self, oSession, oTxsSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ fRc = False;
+
+ if self.sTestBuild is not None:
+ fRc = self.oTestDriver.txsUploadFile(oSession, oTxsSession, self.sTestBuild, \
+ '/tmp/' + os.path.basename(self.sTestBuild), \
+ cMsTimeout = 120 * 1000);
+ fRc = fRc and self.oTestDriver.txsRunTest(oTxsSession, 'Installing VBox', 10 * 1000, \
+ '/bin/chmod',
+ ('chmod', '755', '/tmp/' + os.path.basename(self.sTestBuild)));
+ fRc = fRc and self.oTestDriver.txsRunTest(oTxsSession, 'Installing VBox', 240 * 1000, \
+ '/tmp/' + os.path.basename(self.sTestBuild));
+
+ return fRc;
+
+ def configureAutostart(self, oSession, oTxsSession, sDefaultPolicy = 'allow',
+ asUserAllow = (), asUserDeny = ()):
+ """
+ Configures the autostart feature in the guest.
+ """
+
+ # Create autostart database directory writeable for everyone
+ fRc = self.oTestDriver.txsRunTest(oTxsSession, 'Creating autostart database', 10 * 1000, \
+ '/bin/mkdir',
+ ('mkdir', '-m', '1777', '/etc/vbox/autostart.d'));
+
+ # Create /etc/default/virtualbox
+ sVBoxCfg = 'VBOXAUTOSTART_CONFIG=/etc/vbox/autostart.cfg\n' \
+ + 'VBOXAUTOSTART_DB=/etc/vbox/autostart.d\n';
+ fRc = fRc and self.oTestDriver.txsUploadString(oSession, oTxsSession, sVBoxCfg, '/etc/default/virtualbox');
+ fRc = fRc and self.oTestDriver.txsRunTest(oTxsSession, 'Setting permissions', 10 * 1000, \
+ '/bin/chmod',
+ ('chmod', '644', '/etc/default/virtualbox'));
+
+ sVBoxCfg = self._createAutostartCfg(sDefaultPolicy, asUserAllow, asUserDeny);
+ fRc = fRc and self.oTestDriver.txsUploadString(oSession, oTxsSession, sVBoxCfg, '/etc/vbox/autostart.cfg');
+ fRc = fRc and self.oTestDriver.txsRunTest(oTxsSession, 'Setting permissions', 10 * 1000, \
+ '/bin/chmod',
+ ('chmod', '644', '/etc/vbox/autostart.cfg'));
+
+ return fRc;
+
+ def createUser(self, oTxsSession, sUser):
+ """
+ Create a new user with the given name
+ """
+
+ fRc = self.oTestDriver.txsRunTest(oTxsSession, 'Creating new user', 10 * 1000, \
+ '/usr/sbin/useradd',
+ ('useradd', '-m', '-U', sUser));
+
+ return fRc;
+
+ # pylint: disable=R0913
+
+ def runProgAsUser(self, oTxsSession, sTestName, cMsTimeout, sExecName, sAsUser, asArgs = (),
+ oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
+ """
+ Runs a program as the specififed user
+ """
+ asNewArgs = ('sudo', '-u', sAsUser, sExecName) + asArgs;
+
+ fRc = self.oTestDriver.txsRunTestRedirectStd(oTxsSession, sTestName, cMsTimeout, '/usr/bin/sudo',
+ asNewArgs, (), "", oStdIn, oStdOut, oStdErr, oTestPipe);
+
+ return fRc;
+
+ # pylint: enable=R0913
+
+ def createTestVM(self, oSession, oTxsSession, sUser, sVmName):
+ """
+ Create a test VM in the guest and enable autostart.
+ """
+
+ _ = oSession;
+
+ fRc = self.runProgAsUser(oTxsSession, 'Configuring autostart database', 10 * 1000, \
+ '/opt/VirtualBox/VBoxManage', sUser,
+ ('setproperty', 'autostartdbpath', '/etc/vbox/autostart.d'));
+ fRc = fRc and self.runProgAsUser(oTxsSession, 'Create VM ' + sVmName, 10 * 1000, \
+ '/opt/VirtualBox/VBoxManage', sUser,
+ ('createvm', '--name', sVmName, '--register'));
+ fRc = fRc and self.runProgAsUser(oTxsSession, 'Enabling autostart for test VM', 10 * 1000, \
+ '/opt/VirtualBox/VBoxManage', sUser,
+ ('modifyvm', sVmName, '--autostart-enabled', 'on'));
+
+ return fRc;
+
+ def checkForRunningVM(self, oSession, oTxsSession, sUser, sVmName):
+ """
+ Check for VM running in the guest after autostart.
+ """
+
+ _ = oSession;
+
+ oStdOut = VBoxManageStdOutWrapper();
+ fRc = self.runProgAsUser(oTxsSession, 'Check for running VM', 20 * 1000, \
+ '/opt/VirtualBox/VBoxManage', sUser, ('list', 'runningvms'),
+ '/dev/null', oStdOut, '/dev/null', '/dev/null');
+
+ fRc = fRc is True and oStdOut.sVmRunning == sVmName;
+
+ return fRc;
+
+class tdAutostartOsDarwin(tdAutostartOs):
+ """
+ Autostart support methods for Darwin guests.
+ """
+
+ def __init__(self, sTestBuildDir):
+ _ = sTestBuildDir;
+ tdAutostartOs.__init__(self);
+
+ def installVirtualBox(self, oSession, oTxsSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ _ = oSession;
+ _ = oTxsSession;
+ return False;
+
+ def configureAutostart(self, oSession, oTxsSession):
+ """
+ Configures the autostart feature in the guest.
+ """
+ _ = oSession;
+ _ = oTxsSession;
+ return False;
+
+ def createTestVM(self, oSession, oTxsSession):
+ """
+ Create a test VM in the guest and enable autostart.
+ """
+ _ = oSession;
+ _ = oTxsSession;
+ return False;
+
+ def checkForRunningVM(self, oSession, oTxsSession):
+ """
+ Check for VM running in the guest after autostart.
+ """
+ _ = oSession;
+ _ = oTxsSession;
+ return False;
+
+class tdAutostartOsSolaris(tdAutostartOs):
+ """
+ Autostart support methods for Solaris guests.
+ """
+
+ def __init__(self, oTestDriver, sTestBuildDir):
+ tdAutostartOs.__init__(self);
+ self.sTestBuildDir = sTestBuildDir;
+ self.sTestBuild = self._findFile('^VirtualBox-.*-SunOS-.*\\.tar.gz$', sTestBuildDir);
+ self.oTestDriver = oTestDriver;
+
+ def installVirtualBox(self, oSession, oTxsSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ fRc = False;
+
+ if self.sTestBuild is not None:
+ reporter.log('Testing build: %s' % (self.sTestBuild));
+ sTestBuildFilename = os.path.basename(self.sTestBuild);
+
+ # Construct the .pkg filename from the tar.gz name.
+ oMatch = re.search(r'\d+.\d+.\d+-SunOS-r\d+', sTestBuildFilename);
+ if oMatch is not None:
+ sPkgFilename = 'VirtualBox-' + oMatch.group() + '.pkg';
+
+ reporter.log('Extracted package filename: %s' % (sPkgFilename));
+
+ fRc = self.oTestDriver.txsUploadFile(oSession, oTxsSession, self.sTestBuild, \
+ '${SCRATCH}/' + sTestBuildFilename, \
+ cMsTimeout = 120 * 1000);
+ fRc = fRc and self.oTestDriver.txsUnpackFile(oSession, oTxsSession, \
+ '${SCRATCH}/' + sTestBuildFilename, \
+ '${SCRATCH}', cMsTimeout = 120 * 1000);
+ fRc = fRc and self.oTestDriver.txsRunTest(oTxsSession, 'Installing package', 240 * 1000, \
+ '/usr/sbin/pkgadd',
+ ('pkgadd', '-d', '${SCRATCH}/' + sPkgFilename, \
+ '-n', '-a', '${SCRATCH}/autoresponse', 'SUNWvbox'));
+ return fRc;
+
+ def configureAutostart(self, oSession, oTxsSession, sDefaultPolicy = 'allow',
+ asUserAllow = (), asUserDeny = ()):
+ """
+ Configures the autostart feature in the guest.
+ """
+
+ fRc = self.oTestDriver.txsRunTest(oTxsSession, 'Creating /etc/vbox directory', 10 * 1000, \
+ '/usr/bin/mkdir',
+ ('mkdir', '-m', '755', '/etc/vbox'));
+
+ sVBoxCfg = self._createAutostartCfg(sDefaultPolicy, asUserAllow, asUserDeny);
+ fRc = fRc and self.oTestDriver.txsUploadString(oSession, oTxsSession, sVBoxCfg, '/etc/vbox/autostart.cfg');
+ fRc = fRc and self.oTestDriver.txsRunTest(oTxsSession, 'Setting permissions', 10 * 1000, \
+ '/usr/bin/chmod',
+ ('chmod', '644', '/etc/vbox/autostart.cfg'));
+
+
+ fRc = fRc and self.oTestDriver.txsRunTest(oTxsSession, 'Importing the service', 10 * 1000, \
+ '/usr/sbin/svccfg',
+ ('svccfg', '-s', 'svc:/application/virtualbox/autostart:default', \
+ 'setprop', 'config/config=/etc/vbox/autostart.cfg'));
+ fRc = fRc and self.oTestDriver.txsRunTest(oTxsSession, 'Enabling the service', 10 * 1000, \
+ '/usr/sbin/svcadm',
+ ('svcadm', 'enable', 'svc:/application/virtualbox/autostart:default'));
+
+ return fRc;
+
+ def createUser(self, oTxsSession, sUser):
+ """
+ Create a new user with the given name
+ """
+
+ fRc = self.oTestDriver.txsRunTest(oTxsSession, 'Creating new user', 10 * 1000, \
+ '/usr/sbin/useradd',
+ ('useradd', '-m', '-g', 'staff', sUser));
+
+ return fRc;
+
+ # pylint: disable=R0913
+
+ def runProgAsUser(self, oTxsSession, sTestName, cMsTimeout, sExecName, sAsUser, asArgs = (),
+ oStdIn = '/dev/null', oStdOut = '/dev/null', oStdErr = '/dev/null', oTestPipe = '/dev/null'):
+ """
+ Runs a program as the specififed user
+ """
+ asNewArgs = ('sudo', '-u', sAsUser, sExecName) + asArgs;
+
+ fRc = self.oTestDriver.txsRunTestRedirectStd(oTxsSession, sTestName, cMsTimeout, '/usr/bin/sudo',
+ asNewArgs, (), "", oStdIn, oStdOut, oStdErr, oTestPipe);
+
+ return fRc;
+
+ # pylint: enable=R0913
+
+ def createTestVM(self, oSession, oTxsSession, sUser, sVmName):
+ """
+ Create a test VM in the guest and enable autostart.
+ """
+
+ _ = oSession;
+
+ fRc = self.runProgAsUser(oTxsSession, 'Create VM ' + sVmName, 10 * 1000, \
+ '/opt/VirtualBox/VBoxManage', sUser,
+ ('createvm', '--name', sVmName, '--register'));
+ fRc = fRc and self.runProgAsUser(oTxsSession, 'Enabling autostart for test VM', 10 * 1000, \
+ '/opt/VirtualBox/VBoxManage', sUser,
+ ('modifyvm', sVmName, '--autostart-enabled', 'on'));
+
+ return fRc;
+
+ def checkForRunningVM(self, oSession, oTxsSession, sUser, sVmName):
+ """
+ Check for VM running in the guest after autostart.
+ """
+
+ _ = oSession;
+
+ oStdOut = VBoxManageStdOutWrapper();
+ fRc = self.runProgAsUser(oTxsSession, 'Check for running VM', 20 * 1000, \
+ '/opt/VirtualBox/VBoxManage', sUser, ('list', 'runningvms'),
+ '/dev/null', oStdOut, '/dev/null', '/dev/null');
+
+ fRc = fRc is True and oStdOut.sVmRunning == sVmName;
+
+ return fRc;
+
+
+class tdAutostartOsWin(tdAutostartOs):
+ """
+ Autostart support methods for Windows guests.
+ """
+
+ def __init__(self, sTestBuildDir):
+ _ = sTestBuildDir;
+ tdAutostartOs.__init__(self);
+ return;
+
+ def installVirtualBox(self, oSession, oTxsSession):
+ """
+ Install VirtualBox in the guest.
+ """
+ _ = oSession;
+ _ = oTxsSession;
+ return False;
+
+ def configureAutostart(self, oSession, oTxsSession):
+ """
+ Configures the autostart feature in the guest.
+ """
+ _ = oSession;
+ _ = oTxsSession;
+ return False;
+
+ def createTestVM(self, oSession, oTxsSession):
+ """
+ Create a test VM in the guest and enable autostart.
+ """
+ _ = oSession;
+ _ = oTxsSession;
+ return False;
+
+ def checkForRunningVM(self, oSession, oTxsSession):
+ """
+ Check for VM running in the guest after autostart.
+ """
+ _ = oSession;
+ _ = oTxsSession;
+ return False;
+
+class tdAutostart(vbox.TestDriver): # pylint: disable=R0902
+ """
+ Autostart testcase.
+ """
+
+ ksOsLinux = 'tst-debian'
+ ksOsWindows = 'tst-win'
+ ksOsDarwin = 'tst-darwin'
+ ksOsSolaris = 'tst-solaris'
+ ksOsFreeBSD = 'tst-freebsd'
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.asTestVMsDef = [self.ksOsLinux, self.ksOsSolaris];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.sTestBuildDir = '/home/alexander/Downloads';
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdAutostart Options:');
+ reporter.log(' --test-build-dir <path>');
+ reporter.log(' Default: %s' % (self.sTestBuildDir));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ if asArgs[iArg] == '--test-build-dir':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-build-dir" takes a path argument');
+ self.sTestBuildDir = asArgs[iArg];
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+ if self.ksOsLinux in self.asTestVMs:
+ self.asRsrcs.append('4.2/autostart/tst-debian.vdi');
+ if self.ksOsSolaris in self.asTestVMs:
+ self.asRsrcs.append('4.2/autostart/tst-solaris.vdi');
+
+ return self.asRsrcs;
+
+ def actionConfig(self):
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure the VMs we're going to use.
+ #
+
+ # Linux VMs
+ if self.ksOsLinux in self.asTestVMs:
+ oVM = self.createTestVM(self.ksOsLinux, 1, '4.2/autostart/tst-debian.vdi', sKind = 'Debian_64', \
+ fIoApic = True, eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ eNic0Type = vboxcon.NetworkAdapterType_Am79C973);
+ if oVM is None:
+ return False;
+
+ # Solaris VMs
+ if self.ksOsSolaris in self.asTestVMs:
+ oVM = self.createTestVM(self.ksOsSolaris, 1, '4.2/autostart/tst-solaris.vdi', sKind = 'Solaris_64', \
+ fIoApic = True, eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ sHddControllerType = "SATA Controller");
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.testAutostart();
+ return fRc;
+
+ #
+ # Test execution helpers.
+ #
+ def testAutostartRunProgs(self, oSession, oTxsSession, sVmName):
+ """
+ Test VirtualBoxs autostart feature in a VM.
+ """
+ reporter.testStart('Autostart ' + sVmName);
+
+ oGuestOsHlp = None; # type: tdAutostartOs
+ if sVmName == self.ksOsLinux:
+ oGuestOsHlp = tdAutostartOsLinux(self, self.sTestBuildDir);
+ elif sVmName == self.ksOsSolaris:
+ oGuestOsHlp = tdAutostartOsSolaris(self, self.sTestBuildDir); # annoying - pylint: disable=redefined-variable-type
+ elif sVmName == self.ksOsDarwin:
+ oGuestOsHlp = tdAutostartOsDarwin(self.sTestBuildDir);
+ elif sVmName == self.ksOsWindows:
+ oGuestOsHlp = tdAutostartOsWin(self.sTestBuildDir);
+
+ sTestUserAllow = 'test1';
+ sTestUserDeny = 'test2';
+ sTestVmName = 'TestVM';
+
+ if oGuestOsHlp is not None:
+ # Create two new users
+ fRc = oGuestOsHlp.createUser(oTxsSession, sTestUserAllow);
+ fRc = fRc and oGuestOsHlp.createUser(oTxsSession, sTestUserDeny);
+ if fRc is True:
+ # Install VBox first
+ fRc = oGuestOsHlp.installVirtualBox(oSession, oTxsSession);
+ if fRc is True:
+ fRc = oGuestOsHlp.configureAutostart(oSession, oTxsSession, 'allow',
+ (sTestUserAllow,), (sTestUserDeny,));
+ if fRc is True:
+ # Create a VM with autostart enabled in the guest for both users
+ fRc = oGuestOsHlp.createTestVM(oSession, oTxsSession, sTestUserAllow, sTestVmName);
+ fRc = fRc and oGuestOsHlp.createTestVM(oSession, oTxsSession, sTestUserDeny, sTestVmName);
+ if fRc is True:
+ # Reboot the guest
+ (fRc, oTxsSession) = self.txsRebootAndReconnectViaTcp(oSession, oTxsSession, cMsTimeout = 3 * 60000, \
+ fNatForwardingForTxs = True);
+ if fRc is True:
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+ fRc = oGuestOsHlp.checkForRunningVM(oSession, oTxsSession, sTestUserAllow, sTestVmName);
+ if fRc is False:
+ reporter.error('Test VM is not running inside the guest for allowed user');
+
+ fRc = oGuestOsHlp.checkForRunningVM(oSession, oTxsSession, sTestUserDeny, sTestVmName);
+ if fRc is True:
+ reporter.error('Test VM is running inside the guest for denied user');
+ else:
+ reporter.log('Rebooting the guest failed');
+ else:
+ reporter.log('Creating test VM failed');
+ else:
+ reporter.log('Configuring autostart in the guest failed');
+ else:
+ reporter.log('Installing VirtualBox in the guest failed');
+ else:
+ reporter.log('Creating test users failed');
+ else:
+ reporter.log('Guest OS helper not created for VM %s' % (sVmName));
+ fRc = False;
+
+ reporter.testDone(not fRc);
+ return fRc;
+
+ def testAutostartOneCfg(self, sVmName):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ oVM = self.getVmByName(sVmName);
+
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(True);
+ fRc = fRc and oSession.enableNestedPaging(True);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(sVmName, fCdWait = False, fNatForwardingForTxs = True);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+
+ fRc = self.testAutostartRunProgs(oSession, oTxsSession, sVmName);
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+ else:
+ fRc = False;
+ return fRc;
+
+ def testAutostartForOneVM(self, sVmName):
+ """
+ Runs one VM thru the various configurations.
+ """
+ reporter.testStart(sVmName);
+ fRc = True;
+ self.testAutostartOneCfg(sVmName);
+ reporter.testDone();
+ return fRc;
+
+ def testAutostart(self):
+ """
+ Executes autostart test.
+ """
+
+ # Loop thru the test VMs.
+ for sVM in self.asTestVMs:
+ # run test on the VM.
+ if not self.testAutostartForOneVM(sVM):
+ fRc = False;
+ else:
+ fRc = True;
+
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdAutostart().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/benchmarks/Makefile.kmk b/src/VBox/ValidationKit/tests/benchmarks/Makefile.kmk
new file mode 100644
index 00000000..879809b4
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/benchmarks/Makefile.kmk
@@ -0,0 +1,41 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Benchmarks.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsBenchmarks
+ValidationKitTestsBenchmarks_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsBenchmarks_INST = $(INST_VALIDATIONKIT)tests/benchmarks/
+ValidationKitTestsBenchmarks_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdBenchmark1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsBenchmarks_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/benchmarks/tdBenchmark1.py b/src/VBox/ValidationKit/tests/benchmarks/tdBenchmark1.py
new file mode 100755
index 00000000..37e40f44
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/benchmarks/tdBenchmark1.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdBenchmark1.py $
+
+"""
+VirtualBox Validation Kit - Test that runs various benchmarks.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import vbox;
+from testdriver import vboxtestvms;
+
+
+class tdBenchmark1(vbox.TestDriver):
+ """
+ Benchmark #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ oTestVm = vboxtestvms.BootSectorTestVm(self.oTestVmSet, 'tst-bs-test1',
+ os.path.join(self.sVBoxBootSectors, 'bootsector2-test1.img'));
+ self.oTestVmSet.aoTestVms.append(oTestVm);
+
+
+ #
+ # Overridden methods.
+ #
+
+
+ def actionConfig(self):
+ self._detectValidationKit();
+ return self.oTestVmSet.actionConfig(self);
+
+ def actionExecute(self):
+ return self.oTestVmSet.actionExecute(self, self.testOneCfg);
+
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def testOneCfg(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru the tests.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ fRc = False;
+
+ sXmlFile = self.prepareResultFile();
+ asEnv = [ 'IPRT_TEST_FILE=' + sXmlFile];
+
+ self.logVmInfo(oVM);
+ oSession = self.startVm(oVM, sName = oTestVm.sVmName, asEnv = asEnv);
+ if oSession is not None:
+ cMsTimeout = 15*60*1000;
+ if not reporter.isLocal(): ## @todo need to figure a better way of handling timeouts on the testboxes ...
+ cMsTimeout = self.adjustTimeoutMs(180 * 60000);
+
+ oRc = self.waitForTasks(cMsTimeout);
+ if oRc == oSession:
+ fRc = oSession.assertPoweredOff();
+ else:
+ reporter.error('oRc=%s, expected %s' % (oRc, oSession));
+
+ reporter.addSubXmlFile(sXmlFile);
+ self.terminateVmBySession(oSession);
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdBenchmark1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/cpu/Makefile.kmk b/src/VBox/ValidationKit/tests/cpu/Makefile.kmk
new file mode 100644
index 00000000..fbac3324
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/cpu/Makefile.kmk
@@ -0,0 +1,41 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - CPU Tests.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsCpu
+ValidationKitTestsCpu_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsCpu_INST = $(INST_VALIDATIONKIT)tests/cpu/
+ValidationKitTestsCpu_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdCpuPae1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsCpu_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/cpu/tdCpuPae1.py b/src/VBox/ValidationKit/tests/cpu/tdCpuPae1.py
new file mode 100755
index 00000000..affe40fb
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/cpu/tdCpuPae1.py
@@ -0,0 +1,254 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdCpuPae1.py $
+
+"""
+VirtualBox Validation Kit - Catch PAE not enabled.
+
+Test that switching into PAE mode when it isn't enable, check that it produces
+the right runtime error.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+
+
+class tdCpuPae1ConsoleCallbacks(vbox.ConsoleEventHandlerBase):
+ """
+ For catching the PAE runtime error.
+ """
+ def __init__(self, dArgs):
+ oTstDrv = dArgs['oTstDrv'];
+ oVBoxMgr = dArgs['oVBoxMgr']; _ = oVBoxMgr;
+
+ vbox.ConsoleEventHandlerBase.__init__(self, dArgs, 'tdCpuPae1');
+ self.oTstDrv = oTstDrv;
+
+ def onRuntimeError(self, fFatal, sErrId, sMessage):
+ """ Verify the error. """
+ reporter.log('onRuntimeError: fFatal=%s sErrId="%s" sMessage="%s"' % (fFatal, sErrId, sMessage));
+ if sErrId != 'PAEmode':
+ reporter.testFailure('sErrId=%s, expected PAEmode' % (sErrId,));
+ elif fFatal is not True:
+ reporter.testFailure('fFatal=%s, expected True' % (fFatal,));
+ else:
+ self.oTstDrv.fCallbackSuccess = True;
+ self.oTstDrv.fCallbackFired = True;
+ self.oVBoxMgr.interruptWaitEvents();
+ return None;
+
+
+class tdCpuPae1(vbox.TestDriver):
+ """
+ PAE Test #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asSkipTests = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef
+ self.acCpusDef = [1, 2,]
+ self.acCpus = self.acCpusDef;
+ self.fCallbackFired = False;
+ self.fCallbackSuccess = False;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdCpuPae1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --quick');
+ reporter.log(' Shorthand for: --virt-modes raw --cpu-counts 1 32');
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--quick':
+ self.asVirtModes = ['raw',];
+ self.acCpus = [1,];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def getResourceSet(self):
+ return [];
+
+ def actionConfig(self):
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure a VM with the PAE bootsector as floppy image.
+ #
+
+ oVM = self.createTestVM('tst-bs-pae', 2, sKind = 'Other', fVirtEx = False, fPae = False, \
+ sFloppy = os.path.join(self.sVBoxBootSectors, 'bootsector-pae.img') );
+ if oVM is None:
+ return False;
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.test1();
+
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def test1OneCfg(self, oVM, cCpus, fHwVirt, fNestedPaging):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.setupBootLogo(True, 2500); # Race avoidance fudge.
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Zap the state (used by the callback).
+ self.fCallbackFired = False;
+ self.fCallbackSuccess = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession = self.startVm(oVM)
+ if oSession is not None:
+ # Set up a callback for catching the runtime error. !Races the guest bootup!
+ oConsoleCallbacks = oSession.registerDerivedEventHandler(tdCpuPae1ConsoleCallbacks, {'oTstDrv':self,})
+
+ fRc = False;
+ if oConsoleCallbacks is not None:
+ # Wait for 30 seconds for something to finish.
+ tsStart = base.timestampMilli();
+ while base.timestampMilli() - tsStart < 30000:
+ oTask = self.waitForTasks(1000);
+ if oTask is not None:
+ break;
+ if self.fCallbackFired:
+ break;
+ if not self.fCallbackFired:
+ reporter.testFailure('the callback did not fire');
+ fRc = self.fCallbackSuccess;
+
+ # cleanup.
+ oConsoleCallbacks.unregister();
+ self.terminateVmBySession(oSession) #, fRc);
+ else:
+ fRc = False;
+ return fRc;
+
+
+ def test1(self):
+ """
+ Executes test #1 - Negative API testing.
+
+ ASSUMES that the VMs are
+ """
+ reporter.testStart('Test 1');
+ oVM = self.getVmByName('tst-bs-pae');
+
+ for cCpus in self.acCpus:
+ if cCpus == 1: reporter.testStart('1 cpu');
+ else: reporter.testStart('%u cpus' % (cCpus));
+
+ for sVirtMode in self.asVirtModes:
+ if sVirtMode == 'raw' and cCpus > 1:
+ continue;
+
+ hsVirtModeDesc = {};
+ hsVirtModeDesc['raw'] = 'Raw-mode';
+ hsVirtModeDesc['hwvirt'] = 'HwVirt';
+ hsVirtModeDesc['hwvirt-np'] = 'NestedPaging';
+ reporter.testStart(hsVirtModeDesc[sVirtMode]);
+
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+ self.test1OneCfg(oVM, cCpus, fHwVirt, fNestedPaging);
+
+ reporter.testDone();
+ reporter.testDone();
+
+ return reporter.testDone()[1] == 0;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdCpuPae1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/installation/Makefile.kmk b/src/VBox/ValidationKit/tests/installation/Makefile.kmk
new file mode 100644
index 00000000..0d74094e
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/installation/Makefile.kmk
@@ -0,0 +1,41 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Automatic guest OS installation tests.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitInstallationTests
+ValidationKitInstallationTests_TEMPLATE = VBoxValidationKitR3
+ValidationKitInstallationTests_INST = $(INST_VALIDATIONKIT)tests/installation/
+ValidationKitInstallationTests_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdGuestOsInstTest1.py \
+ $(PATH_SUB_CURRENT)/tdGuestOsInstOs2.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitInstallationTests_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/ValidationKit/tests/installation/tdGuestOsInstOs2.py b/src/VBox/ValidationKit/tests/installation/tdGuestOsInstOs2.py
new file mode 100755
index 00000000..32869cd9
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/installation/tdGuestOsInstOs2.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdGuestOsInstOs2.py $
+
+"""
+VirtualBox Validation Kit - OS/2 install tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import vbox
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+
+
+class tdGuestOsInstOs2(vbox.TestDriver):
+ """
+ OS/2 unattended installation.
+
+ Scenario:
+ - Create new VM that corresponds specified installation ISO image
+ - Create HDD that corresponds to OS type that will be installed
+ - Set VM boot order: HDD, Floppy, ISO
+ - Start VM: sinse there is no OS installed on HDD, VM will booted from floppy
+ - After first reboot VM will continue installation from HDD automatically
+ - Wait for incomming TCP connection (guest should initiate such a
+ connection in case installation has been completed successfully)
+ """
+
+ ksSataController = 'SATA Controller'
+ ksIdeController = 'IDE Controller'
+
+ # VM parameters required to run ISO image.
+ # Format: (cBytesHdd, sKind)
+ kaoVmParams = {
+ 'acp2-txs.iso': ( 2*1024*1024*1024, 'OS2', ksIdeController ),
+ 'mcp2-txs.iso': ( 2*1024*1024*1024, 'OS2', ksIdeController ),
+ }
+
+ def __init__(self):
+ """
+ Reinitialize child class instance.
+ """
+ vbox.TestDriver.__init__(self)
+
+ self.sVmName = 'TestVM'
+ self.sHddName = 'TestHdd.vdi'
+ self.sIso = None
+ self.sFloppy = None
+ self.sIsoPathBase = os.path.join(self.sResourcePath, '4.2', 'isos')
+ self.fEnableIOAPIC = True
+ self.cCpus = 1
+ self.fEnableNestedPaging = True
+ self.fEnablePAE = False
+ self.asExtraData = []
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ """
+ Extend usage info
+ """
+ rc = vbox.TestDriver.showUsage(self)
+ reporter.log(' --install-iso <ISO file name>')
+ reporter.log(' --cpus <# CPUs>')
+ reporter.log(' --no-ioapic')
+ reporter.log(' --no-nested-paging')
+ reporter.log(' --pae')
+ reporter.log(' --set-extradata <key>:value')
+ reporter.log(' Set VM extra data. This command line option might be used multiple times.')
+ return rc
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Extend standard options set
+ """
+ if asArgs[iArg] == '--install-iso':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--install-iso" option requires an argument')
+ self.sIso = asArgs[iArg]
+ elif asArgs[iArg] == '--cpus':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpus" option requires an argument')
+ self.cCpus = int(asArgs[iArg])
+ elif asArgs[iArg] == '--no-ioapic':
+ self.fEnableIOAPIC = False
+ elif asArgs[iArg] == '--no-nested-paging':
+ self.fEnableNestedPaging = False
+ elif asArgs[iArg] == '--pae':
+ self.fEnablePAE = True
+ elif asArgs[iArg] == '--extra-mem':
+ self.fEnablePAE = True
+ elif asArgs[iArg] == '--set-extradata':
+ iArg = self.requireMoreArgs(1, asArgs, iArg)
+ self.asExtraData.append(asArgs[iArg])
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg)
+
+ return iArg + 1
+
+ def actionConfig(self):
+ """
+ Configure pre-conditions.
+ """
+
+ if not self.importVBoxApi():
+ return False
+
+ assert self.sIso is not None
+ if self.sIso not in self.kaoVmParams:
+ reporter.log('Error: unknown ISO image specified: %s' % self.sIso)
+ return False
+
+ # Get VM params specific to ISO image
+ cBytesHdd, sKind, sController = self.kaoVmParams[self.sIso]
+
+ # Create VM itself
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT
+ self.sIso = os.path.join(self.sIsoPathBase, self.sIso)
+ assert os.path.isfile(self.sIso)
+
+ self.sFloppy = os.path.join(self.sIsoPathBase, os.path.splitext(self.sIso)[0] + '.img')
+
+ oVM = self.createTestVM(self.sVmName, 1, sKind = sKind, sDvdImage = self.sIso, cCpus = self.cCpus,
+ sFloppy = self.sFloppy, eNic0AttachType = eNic0AttachType)
+ assert oVM is not None
+
+ oSession = self.openSession(oVM)
+
+ # Create HDD
+ sHddPath = os.path.join(self.sScratchPath, self.sHddName)
+ fRc = True
+ if sController == self.ksSataController:
+ fRc = oSession.setStorageControllerType(vboxcon.StorageControllerType_IntelAhci, sController)
+
+ fRc = fRc and oSession.createAndAttachHd(sHddPath, cb = cBytesHdd,
+ sController = sController, iPort = 0, fImmutable=False)
+ if sController == self.ksSataController:
+ fRc = fRc and oSession.setStorageControllerPortCount(sController, 1)
+
+ # Set proper boot order
+ fRc = fRc and oSession.setBootOrder(1, vboxcon.DeviceType_HardDisk)
+ fRc = fRc and oSession.setBootOrder(2, vboxcon.DeviceType_Floppy)
+
+ # Enable HW virt
+ fRc = fRc and oSession.enableVirtEx(True)
+
+ # Enable I/O APIC
+ fRc = fRc and oSession.enableIoApic(self.fEnableIOAPIC)
+
+ # Enable Nested Paging
+ fRc = fRc and oSession.enableNestedPaging(self.fEnableNestedPaging)
+
+ # Enable PAE
+ fRc = fRc and oSession.enablePae(self.fEnablePAE)
+
+ # Remote desktop
+ oSession.setupVrdp(True)
+
+ # Set extra data
+ if self.asExtraData != []:
+ for sExtraData in self.asExtraData:
+ try:
+ sKey, sValue = sExtraData.split(':')
+ except ValueError:
+ raise base.InvalidOption('Invalid extradata specified: %s' % sExtraData)
+ reporter.log('Set extradata: %s => %s' % (sKey, sValue))
+ fRc = fRc and oSession.setExtraData(sKey, sValue)
+
+ fRc = fRc and oSession.saveSettings()
+ fRc = oSession.close()
+ assert fRc is True
+
+ return vbox.TestDriver.actionConfig(self)
+
+ def actionExecute(self):
+ """
+ Execute the testcase itself.
+ """
+ if not self.importVBoxApi():
+ return False
+ return self.testDoInstallGuestOs()
+
+ #
+ # Test execution helpers.
+ #
+
+ def testDoInstallGuestOs(self):
+ """
+ Install guest OS and wait for result
+ """
+ reporter.testStart('Installing %s' % (os.path.basename(self.sIso),))
+
+ cMsTimeout = 40*60000;
+ if not reporter.isLocal(): ## @todo need to figure a better way of handling timeouts on the testboxes ...
+ cMsTimeout = 180 * 60000; # will be adjusted down.
+
+ oSession, _ = self.startVmAndConnectToTxsViaTcp(self.sVmName, fCdWait = False, cMsTimeout = cMsTimeout)
+ if oSession is not None:
+ # Wait until guest reported success
+ reporter.log('Guest reported success')
+ reporter.testDone()
+ fRc = self.terminateVmBySession(oSession)
+ return fRc is True
+ reporter.error('Installation of %s has failed' % (self.sIso,))
+ reporter.testDone()
+ return False
+
+if __name__ == '__main__':
+ sys.exit(tdGuestOsInstOs2().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/installation/tdGuestOsInstTest1.py b/src/VBox/ValidationKit/tests/installation/tdGuestOsInstTest1.py
new file mode 100755
index 00000000..f47c747d
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/installation/tdGuestOsInstTest1.py
@@ -0,0 +1,429 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdGuestOsInstTest1.py $
+
+"""
+VirtualBox Validation Kit - Guest OS installation tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os
+import sys
+
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import vbox;
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import vboxcon;
+from testdriver import vboxtestvms;
+
+
+class InstallTestVm(vboxtestvms.TestVm):
+ """ Installation test VM. """
+
+ ## @name The primary controller, to which the disk will be attached.
+ ## @{
+ ksScsiController = 'SCSI Controller'
+ ksSataController = 'SATA Controller'
+ ksIdeController = 'IDE Controller'
+ ## @}
+
+ ## @name VM option flags (OR together).
+ ## @{
+ kf32Bit = 0x01;
+ kf64Bit = 0x02;
+ # most likely for ancient Linux kernels assuming that AMD processors have always an I/O-APIC
+ kfReqIoApic = 0x10;
+ kfReqIoApicSmp = 0x20;
+ kfReqPae = 0x40;
+ kfIdeIrqDelay = 0x80;
+ kfUbuntuNewAmdBug = 0x100;
+ kfNoWin81Paravirt = 0x200;
+ ## @}
+
+ ## IRQ delay extra data config for win2k VMs.
+ kasIdeIrqDelay = [ 'VBoxInternal/Devices/piix3ide/0/Config/IRQDelay:1', ];
+
+ ## Install ISO path relative to the testrsrc root.
+ ksIsoPathBase = os.path.join('4.2', 'isos');
+
+
+ def __init__(self, oSet, sVmName, sKind, sInstallIso, sHdCtrlNm, cGbHdd, fFlags):
+ vboxtestvms.TestVm.__init__(self, sVmName, oSet = oSet, sKind = sKind, sHddControllerType = sHdCtrlNm,
+ fRandomPvPMode = (fFlags & self.kfNoWin81Paravirt) == 0);
+ self.sDvdImage = os.path.join(self.ksIsoPathBase, sInstallIso);
+ self.cGbHdd = cGbHdd;
+ self.fInstVmFlags = fFlags;
+ if fFlags & self.kfReqPae:
+ self.fPae = True;
+ if fFlags & (self.kfReqIoApic | self.kfReqIoApicSmp):
+ self.fIoApic = True;
+
+ # Tweaks
+ self.iOptRamAdjust = 0;
+ self.asExtraData = [];
+ if fFlags & self.kfIdeIrqDelay:
+ self.asExtraData = self.kasIdeIrqDelay;
+
+ def detatchAndDeleteHd(self, oTestDrv):
+ """
+ Detaches and deletes the HD.
+ Returns success indicator, error info logged.
+ """
+ fRc = False;
+ oVM = oTestDrv.getVmByName(self.sVmName);
+ if oVM is not None:
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is not None:
+ (fRc, oHd) = oSession.detachHd(self.sHddControllerType, iPort = 0, iDevice = 0);
+ if fRc is True and oHd is not None:
+ fRc = oSession.saveSettings();
+ fRc = fRc and oTestDrv.oVBox.deleteHdByMedium(oHd);
+ fRc = fRc and oSession.saveSettings(); # Necessary for media reg?
+ fRc = oSession.close() and fRc;
+ return fRc;
+
+ def getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode = None):
+ #
+ # Do the standard reconfig in the base class first, it'll figure out
+ # if we can run the VM as requested.
+ #
+ (fRc, oVM) = vboxtestvms.TestVm.getReconfiguredVm(self, oTestDrv, cCpus, sVirtMode, sParavirtMode);
+
+ #
+ # Make sure there is no HD from the previous run attached nor taking
+ # up storage on the host.
+ #
+ if fRc is True:
+ fRc = self.detatchAndDeleteHd(oTestDrv);
+
+ #
+ # Check for ubuntu installer vs. AMD host CPU.
+ #
+ if fRc is True and (self.fInstVmFlags & self.kfUbuntuNewAmdBug):
+ if self.isHostCpuAffectedByUbuntuNewAmdBug(oTestDrv):
+ return (None, None); # (skip)
+
+ #
+ # Make adjustments to the default config, and adding a fresh HD.
+ #
+ if fRc is True:
+ oSession = oTestDrv.openSession(oVM);
+ if oSession is not None:
+ if self.sHddControllerType == self.ksSataController:
+ fRc = fRc and oSession.setStorageControllerType(vboxcon.StorageControllerType_IntelAhci,
+ self.sHddControllerType);
+ fRc = fRc and oSession.setStorageControllerPortCount(self.sHddControllerType, 1);
+ elif self.sHddControllerType == self.ksScsiController:
+ fRc = fRc and oSession.setStorageControllerType(vboxcon.StorageControllerType_LsiLogic,
+ self.sHddControllerType);
+ try:
+ sHddPath = os.path.join(os.path.dirname(oVM.settingsFilePath),
+ '%s-%s-%s.vdi' % (self.sVmName, sVirtMode, cCpus,));
+ except:
+ reporter.errorXcpt();
+ sHddPath = None;
+ fRc = False;
+
+ fRc = fRc and oSession.createAndAttachHd(sHddPath,
+ cb = self.cGbHdd * 1024*1024*1024,
+ sController = self.sHddControllerType,
+ iPort = 0,
+ fImmutable = False);
+
+ # Set proper boot order
+ fRc = fRc and oSession.setBootOrder(1, vboxcon.DeviceType_HardDisk)
+ fRc = fRc and oSession.setBootOrder(2, vboxcon.DeviceType_DVD)
+
+ # Adjust memory if requested.
+ if self.iOptRamAdjust != 0:
+ fRc = fRc and oSession.setRamSize(oSession.o.machine.memorySize + self.iOptRamAdjust);
+
+ # Set extra data
+ for sExtraData in self.asExtraData:
+ try:
+ sKey, sValue = sExtraData.split(':')
+ except ValueError:
+ raise base.InvalidOption('Invalid extradata specified: %s' % sExtraData)
+ reporter.log('Set extradata: %s => %s' % (sKey, sValue))
+ fRc = fRc and oSession.setExtraData(sKey, sValue)
+
+ # Other variations?
+
+ # Save the settings.
+ fRc = fRc and oSession.saveSettings()
+ fRc = oSession.close() and fRc;
+ else:
+ fRc = False;
+ if fRc is not True:
+ oVM = None;
+
+ # Done.
+ return (fRc, oVM)
+
+ def isHostCpuAffectedByUbuntuNewAmdBug(self, oTestDrv):
+ """
+ Checks if the host OS is affected by older ubuntu installers being very
+ picky about which families of AMD CPUs it would run on.
+
+ The installer checks for family 15, later 16, later 20, and in 11.10
+ they remove the family check for AMD CPUs.
+ """
+ if not oTestDrv.isHostCpuAmd():
+ return False;
+ try:
+ (uMaxExt, _, _, _) = oTestDrv.oVBox.host.getProcessorCPUIDLeaf(0, 0x80000000, 0);
+ (uFamilyModel, _, _, _) = oTestDrv.oVBox.host.getProcessorCPUIDLeaf(0, 0x80000001, 0);
+ except:
+ reporter.logXcpt();
+ return False;
+ if uMaxExt < 0x80000001 or uMaxExt > 0x8000ffff:
+ return False;
+
+ uFamily = (uFamilyModel >> 8) & 0xf
+ if uFamily == 0xf:
+ uFamily = ((uFamilyModel >> 20) & 0x7f) + 0xf;
+ ## @todo Break this down into which old ubuntu release supports exactly
+ ## which AMD family, if we care.
+ if uFamily <= 15:
+ return False;
+ reporter.log('Skipping "%s" because host CPU is a family %u AMD, which may cause trouble for the guest OS installer.'
+ % (self.sVmName, uFamily,));
+ return True;
+
+
+
+
+
+class tdGuestOsInstTest1(vbox.TestDriver):
+ """
+ Guest OS installation tests.
+
+ Scenario:
+ - Create new VM that corresponds specified installation ISO image.
+ - Create HDD that corresponds to OS type that will be installed.
+ - Boot VM from ISO image (i.e. install guest OS).
+ - Wait for incomming TCP connection (guest should initiate such a
+ connection in case installation has been completed successfully).
+ """
+
+
+ def __init__(self):
+ """
+ Reinitialize child class instance.
+ """
+ vbox.TestDriver.__init__(self)
+ self.fLegacyOptions = False;
+ assert self.fEnableVrdp; # in parent driver.
+
+ #
+ # Our install test VM set.
+ #
+ oSet = vboxtestvms.TestVmSet(self.oTestVmManager, fIgnoreSkippedVm = True);
+ oSet.aoTestVms.extend([
+ # pylint: disable=C0301
+ InstallTestVm(oSet, 'tst-fedora4', 'Fedora', 'fedora4-txs.iso', InstallTestVm.ksIdeController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-fedora5', 'Fedora', 'fedora5-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae | InstallTestVm.kfReqIoApicSmp),
+ InstallTestVm(oSet, 'tst-fedora6', 'Fedora', 'fedora6-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqIoApic),
+ InstallTestVm(oSet, 'tst-fedora7', 'Fedora', 'fedora7-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfUbuntuNewAmdBug | InstallTestVm.kfReqIoApic),
+ InstallTestVm(oSet, 'tst-fedora9', 'Fedora', 'fedora9-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-fedora18-64', 'Fedora_64', 'fedora18-x64-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-fedora18', 'Fedora', 'fedora18-txs.iso', InstallTestVm.ksScsiController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-ols6', 'Oracle', 'ols6-i386-txs.iso', InstallTestVm.ksSataController, 12, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae),
+ InstallTestVm(oSet, 'tst-ols6-64', 'Oracle_64', 'ols6-x86_64-txs.iso', InstallTestVm.ksSataController, 12, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-rhel5', 'RedHat', 'rhel5-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae | InstallTestVm.kfReqIoApic),
+ InstallTestVm(oSet, 'tst-suse102', 'OpenSUSE', 'opensuse102-txs.iso', InstallTestVm.ksIdeController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqIoApic),
+ ## @todo InstallTestVm(oSet, 'tst-ubuntu606', 'Ubuntu', 'ubuntu606-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit),
+ ## @todo InstallTestVm(oSet, 'tst-ubuntu710', 'Ubuntu', 'ubuntu710-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-ubuntu804', 'Ubuntu', 'ubuntu804-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfUbuntuNewAmdBug | InstallTestVm.kfReqPae | InstallTestVm.kfReqIoApic),
+ InstallTestVm(oSet, 'tst-ubuntu804-64', 'Ubuntu_64', 'ubuntu804-amd64-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-ubuntu904', 'Ubuntu', 'ubuntu904-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfUbuntuNewAmdBug | InstallTestVm.kfReqPae),
+ InstallTestVm(oSet, 'tst-ubuntu904-64', 'Ubuntu_64', 'ubuntu904-amd64-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf64Bit),
+ #InstallTestVm(oSet, 'tst-ubuntu1404', 'Ubuntu', 'ubuntu1404-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfUbuntuNewAmdBug | InstallTestVm.kfReqPae), bird: Is 14.04 one of the 'older ones'?
+ InstallTestVm(oSet, 'tst-ubuntu1404', 'Ubuntu', 'ubuntu1404-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae),
+ InstallTestVm(oSet, 'tst-ubuntu1404-64','Ubuntu_64', 'ubuntu1404-amd64-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-debian7', 'Debian', 'debian-7.0.0-txs.iso', InstallTestVm.ksSataController, 8, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-debian7-64', 'Debian_64', 'debian-7.0.0-x64-txs.iso', InstallTestVm.ksScsiController, 8, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-vista-64', 'WindowsVista_64', 'vista-x64-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-vista-32', 'WindowsVista', 'vista-x86-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-w7-64', 'Windows7_64', 'win7-x64-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-w7-32', 'Windows7', 'win7-x86-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-w2k3', 'Windows2003', 'win2k3ent-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-w2k', 'Windows2000', 'win2ksp0-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit | InstallTestVm.kfIdeIrqDelay),
+ InstallTestVm(oSet, 'tst-w2ksp4', 'Windows2000', 'win2ksp4-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit | InstallTestVm.kfIdeIrqDelay),
+ InstallTestVm(oSet, 'tst-wxp', 'WindowsXP', 'winxppro-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-wxpsp2', 'WindowsXP', 'winxpsp2-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-wxp64', 'WindowsXP_64', 'winxp64-txs.iso', InstallTestVm.ksIdeController, 25, InstallTestVm.kf64Bit),
+ ## @todo disable paravirt for Windows 8.1 guests as long as it's not fixed in the code
+ InstallTestVm(oSet, 'tst-w81-32', 'Windows81', 'win81-x86-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf32Bit),
+ InstallTestVm(oSet, 'tst-w81-64', 'Windows81_64', 'win81-x64-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf64Bit),
+ InstallTestVm(oSet, 'tst-w10-32', 'Windows10', 'win10-x86-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf32Bit | InstallTestVm.kfReqPae),
+ InstallTestVm(oSet, 'tst-w10-64', 'Windows10_64', 'win10-x64-txs.iso', InstallTestVm.ksSataController, 25, InstallTestVm.kf64Bit),
+ # pylint: enable=C0301
+ ]);
+ self.oTestVmSet = oSet;
+
+
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ """
+ Extend usage info
+ """
+ rc = vbox.TestDriver.showUsage(self)
+ reporter.log('');
+ reporter.log('tdGuestOsInstTest1 options:');
+ reporter.log(' --ioapic, --no-ioapic');
+ reporter.log(' Enable or disable the I/O apic.');
+ reporter.log(' Default: --ioapic');
+ reporter.log(' --pae, --no-pae');
+ reporter.log(' Enable or disable PAE support for 32-bit guests.');
+ reporter.log(' Default: Guest dependent.');
+ reporter.log(' --ram-adjust <MBs>')
+ reporter.log(' Adjust the VM ram size by the given delta. Both negative and positive');
+ reporter.log(' values are accepted.');
+ reporter.log(' --set-extradata <key>:value')
+ reporter.log(' Set VM extra data. This command line option might be used multiple times.')
+ reporter.log('obsolete:');
+ reporter.log(' --nested-paging, --no-nested-paging');
+ reporter.log(' --raw-mode');
+ reporter.log(' --cpus <# CPUs>');
+ reporter.log(' --install-iso <ISO file name>');
+
+ return rc
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Extend standard options set
+ """
+
+ if False is True:
+ pass;
+ elif asArgs[iArg] == '--ioapic':
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fIoApic = True;
+ elif asArgs[iArg] == '--no-ioapic':
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fIoApic = False;
+ elif asArgs[iArg] == '--pae':
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fPae = True;
+ elif asArgs[iArg] == '--no-pae':
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fPae = False;
+ elif asArgs[iArg] == '--ram-adjust':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.iOptRamAdjust = int(asArgs[iArg]);
+ elif asArgs[iArg] == '--set-extradata':
+ iArg = self.requireMoreArgs(1, asArgs, iArg)
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.asExtraData.append(asArgs[iArg]);
+
+ # legacy, to be removed once TM is reconfigured.
+ elif asArgs[iArg] == '--install-iso':
+ self.legacyOptions();
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ oTestVm.fSkip = os.path.basename(oTestVm.sDvdImage) != asArgs[iArg];
+ elif asArgs[iArg] == '--cpus':
+ self.legacyOptions();
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ self.oTestVmSet.acCpus = [ int(asArgs[iArg]), ];
+ elif asArgs[iArg] == '--raw-mode':
+ self.legacyOptions();
+ self.oTestVmSet.asVirtModes = [ 'raw', ];
+ elif asArgs[iArg] == '--nested-paging':
+ self.legacyOptions();
+ self.oTestVmSet.asVirtModes = [ 'hwvirt-np', ];
+ elif asArgs[iArg] == '--no-nested-paging':
+ self.legacyOptions();
+ self.oTestVmSet.asVirtModes = [ 'hwvirt', ];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg)
+
+ return iArg + 1
+
+ def legacyOptions(self):
+ """ Enables legacy option mode. """
+ if not self.fLegacyOptions:
+ self.fLegacyOptions = True;
+ self.oTestVmSet.asVirtModes = [ 'hwvirt', ];
+ self.oTestVmSet.acCpus = [ 1, ];
+ return True;
+
+ def actionConfig(self):
+ if not self.importVBoxApi(): # So we can use the constant below.
+ return False;
+ return self.oTestVmSet.actionConfig(self, eNic0AttachType = vboxcon.NetworkAttachmentType_NAT);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Install guest OS and wait for result
+ """
+
+ self.logVmInfo(oVM)
+ reporter.testStart('Installing %s' % (oTestVm.sVmName,))
+
+ cMsTimeout = 40*60000;
+ if not reporter.isLocal(): ## @todo need to figure a better way of handling timeouts on the testboxes ...
+ cMsTimeout = 180 * 60000; # will be adjusted down.
+
+ oSession, _ = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = False, cMsTimeout = cMsTimeout);
+ if oSession is not None:
+ # The guest has connected to TXS, so we're done (for now anyways).
+ reporter.log('Guest reported success')
+ ## @todo Do save + restore.
+
+ reporter.testDone()
+ fRc = self.terminateVmBySession(oSession)
+ return fRc is True
+
+ reporter.error('Installation of %s has failed' % (oTestVm.sVmName,))
+ oTestVm.detatchAndDeleteHd(self); # Save space.
+ reporter.testDone()
+ return False
+
+if __name__ == '__main__':
+ sys.exit(tdGuestOsInstTest1().main(sys.argv))
+
diff --git a/src/VBox/ValidationKit/tests/network/Makefile.kmk b/src/VBox/ValidationKit/tests/network/Makefile.kmk
new file mode 100644
index 00000000..119e0a60
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/network/Makefile.kmk
@@ -0,0 +1,41 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Network Tests.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsNetwork
+ValidationKitTestsNetwork_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsNetwork_INST = $(INST_VALIDATIONKIT)tests/network/
+ValidationKitTestsNetwork_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdNetBenchmark1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsNetwork_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/network/tdNetBenchmark1.py b/src/VBox/ValidationKit/tests/network/tdNetBenchmark1.py
new file mode 100755
index 00000000..af7d05d7
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/network/tdNetBenchmark1.py
@@ -0,0 +1,623 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdNetBenchmark1.py $
+
+"""
+VirtualBox Validation Kit - Networking benchmark #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import socket
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+
+class tdNetBenchmark1(vbox.TestDriver): # pylint: disable=R0902
+ """
+ Networking benchmark #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.sLocalName = socket.getfqdn();
+ self.sLocalIP = None
+ self.sRemoteName = None;
+ self.sRemoteIP = None;
+ self.sGuestName = None;
+ self.sGuestIP = None;
+ self.oGuestToGuestVM = None;
+ self.oGuestToGuestSess = None;
+ self.oGuestToGuestTxs = None;
+ self.asTestVMsDef = ['tst-rhel5', 'tst-win2k3ent', 'tst-sol10'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef
+ self.acCpusDef = [1, 2,]
+ self.acCpus = self.acCpusDef;
+ self.asNicTypesDef = ['E1000', 'PCNet', 'Virtio',];
+ self.asNicTypes = self.asNicTypesDef;
+ self.sNicAttachmentDef = 'bridged';
+ self.sNicAttachment = self.sNicAttachmentDef;
+ self.asSetupsDef = ['g2h', 'g2r', 'g2g',];
+ self.asSetups = self.asSetupsDef;
+ self.cSecsRunDef = 30;
+ self.cSecsRun = self.cSecsRunDef;
+ self.asTestsDef = ['tcp-latency', 'tcp-throughput', 'udp-latency', 'udp-throughput', 'tbench'];
+ self.asTests = self.asTestsDef
+ self.acbLatencyPktsDef = [32, 1024, 4096, 8192, 65536,];
+ self.acbLatencyPkts = self.acbLatencyPktsDef
+ self.acbThroughputPktsDef = [8192, 65536];
+ self.acbThroughputPkts = self.acbThroughputPktsDef
+
+ try: self.sLocalName = socket.gethostbyname(self.sLocalName);
+ except: pass;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdNetBenchmark1 Options:');
+ reporter.log(' --remote-host <hostname|address>');
+ reporter.log(' --local-host <hostname|address>');
+ reporter.log(' --guest-host <hostname|address>');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --nic-types <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asNicTypes)));
+ reporter.log(' --nic-attachment <bridged|nat>');
+ reporter.log(' Default: %s' % (self.sNicAttachmentDef));
+ reporter.log(' --setups <s1[:s2[:]]>');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asSetupsDef)));
+ reporter.log(' --secs-per-run <seconds>');
+ reporter.log(' Default: %s' % (self.cSecsRunDef));
+ reporter.log(' --tests <s1[:s2[:]]>');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
+ reporter.log(' --latency-sizes <size1[:size2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(str(cb) for cb in self.acbLatencyPktsDef))); # pychecker bug?
+ reporter.log(' --throughput-sizes <size1[:size2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(str(cb) for cb in self.acbThroughputPktsDef))); # pychecker bug?
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --quick');
+ reporter.log(' Shorthand for: --virt-modes hwvirt --cpu-counts 1 --secs-per-run 5 --latency-sizes 32');
+ reporter.log(' --throughput-sizes 8192 --test-vms tst-rhel5:tst-win2k3ent:tst-sol10');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ if asArgs[iArg] == '--remote-host':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--remote-host" takes an IP address or a hostname');
+ self.sRemoteName = asArgs[iArg];
+ elif asArgs[iArg] == '--local-host':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--local-host" takes an IP address or a hostname');
+ self.sLocalName = asArgs[iArg];
+ elif asArgs[iArg] == '--guest-host':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--guest-host" takes an IP address or a hostname');
+ self.sGuestName = asArgs[iArg];
+ elif asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--nic-types':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--nic-types" takes a colon separated list of NIC types');
+ self.asNicTypes = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--nic-attachment':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--nic-attachment" takes an argument');
+ self.sNicAttachment = asArgs[iArg];
+ if self.sNicAttachment not in ('bridged', 'nat'):
+ raise base.InvalidOption('The "--nic-attachment" value "%s" is not supported. Valid values are: bridged, nat' \
+ % (self.sNicAttachment));
+ elif asArgs[iArg] == '--setups':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--setups" takes a colon separated list of setups');
+ self.asSetups = asArgs[iArg].split(':');
+ for s in self.asSetups:
+ if s not in self.asSetupsDef:
+ raise base.InvalidOption('The "--setups" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asSetupsDef)));
+ elif asArgs[iArg] == '--secs-per-run':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--secs-per-run" takes second count');
+ try: self.cSecsRun = int(asArgs[iArg]);
+ except: raise base.InvalidOption('The "--secs-per-run" value "%s" is not an integer' % (self.cSecsRun,));
+ if self.cSecsRun <= 0:
+ raise base.InvalidOption('The "--secs-per-run" value "%s" is zero or negative.' % (self.cSecsRun,));
+ elif asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of tests');
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ raise base.InvalidOption('The "--tests" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestsDef)));
+ elif asArgs[iArg] == '--latency-sizes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--latency-sizes" takes a colon separated list of sizes');
+ self.acbLatencyPkts = [];
+ for s in asArgs[iArg].split(':'):
+ try: cb = int(s);
+ except: raise base.InvalidOption('The "--latency-sizes" value "%s" is not an integer' % (s,));
+ if cb <= 0: raise base.InvalidOption('The "--latency-sizes" value "%s" is zero or negative' % (s,));
+ self.acbLatencyPkts.append(cb);
+ elif asArgs[iArg] == '--throughput-sizes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--throughput-sizes" takes a colon separated list of sizes');
+ self.acbThroughputPkts = [];
+ for s in asArgs[iArg].split(':'):
+ try: cb = int(s);
+ except: raise base.InvalidOption('The "--throughput-sizes" value "%s" is not an integer' % (s,));
+ if cb <= 0: raise base.InvalidOption('The "--throughput-sizes" value "%s" is zero or negative' % (s,));
+ self.acbThroughputPkts.append(cb);
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
+ elif asArgs[iArg] == '--quick':
+ self.cSecsRun = 5;
+ self.asVirtModes = ['hwvirt',];
+ self.acCpus = [1,];
+ self.acbLatencyPkts = [32,];
+ self.acbThroughputPkts = [8192,];
+ self.asTestVMs = ['tst-rhel5', 'tst-win2k3ent', 'tst-sol10',];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ # Resolve any names we've been given.
+ self.sLocalIP = base.tryGetHostByName(self.sLocalName);
+ self.sRemoteIP = base.tryGetHostByName(self.sRemoteName);
+ self.sGuestIP = base.tryGetHostByName(self.sGuestName);
+
+ reporter.log('Local IP : %s' % (self.sLocalIP));
+ reporter.log('Remote IP: %s' % (self.sRemoteIP));
+ if self.sGuestIP is None:
+ reporter.log('Guest IP : use tst-guest2guest');
+ else:
+ reporter.log('Guest IP : %s' % (self.sGuestIP));
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+ if 'tst-rhel5' in self.asTestVMs or 'g2g' in self.asSetups:
+ self.asRsrcs.append('3.0/tcp/rhel5.vdi');
+ if 'tst-rhel5-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/rhel5-64.vdi');
+ if 'tst-sles11' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/sles11.vdi');
+ if 'tst-sles11-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/sles11-64.vdi');
+ if 'tst-oel' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/oel.vdi');
+ if 'tst-oel-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/oel-64.vdi');
+ if 'tst-win2k3ent' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k3ent-acpi.vdi');
+ if 'tst-win2k3ent-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k3ent-64.vdi');
+ if 'tst-win2k8' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k8.vdi');
+ if 'tst-sol10' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/solaris10.vdi');
+ if 'tst-sol11' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/solaris11.vdi');
+ return self.asRsrcs;
+
+ def actionConfig(self):
+ # Some stupid trickery to guess the location of the iso. ## fixme - testsuite unzip ++
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxValidationKit.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxTestSuite.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/testsuite/VBoxTestSuite.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sCur = os.getcwd();
+ for i in range(0, 10):
+ sVBoxValidationKit_iso = os.path.join(sCur, 'validationkit/VBoxValidationKit.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sVBoxValidationKit_iso = os.path.join(sCur, 'testsuite/VBoxTestSuite.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sCur = os.path.abspath(os.path.join(sCur, '..'));
+ if i is None: pass; # shut up pychecker/pylint.
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/testsuite/VBoxTestSuite.iso';
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ # Guest to Guest VM.
+ if self.sGuestName is None and 'g2g' in self.asSetups:
+ oVM = self.createTestVM('tst-guest2guest', 0, '3.0/tcp/rhel5.vdi', sKind = 'RedHat', fIoApic = True, \
+ eNic0Type = vboxcon.NetworkAdapterType_I82545EM, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_Bridged, \
+ fVirtEx = True, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+ self.oGuestToGuestVM = oVM;
+
+ #
+ # Configure the VMs we're going to use.
+ #
+ eNic0AttachType = vboxcon.NetworkAttachmentType_Bridged;
+ if self.sNicAttachment == 'nat':
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+
+ # Linux VMs
+ if 'tst-rhel5' in self.asTestVMs:
+ oVM = self.createTestVM('tst-rhel5', 1, '3.0/tcp/rhel5.vdi', sKind = 'RedHat', fIoApic = True, \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-rhel5-64' in self.asTestVMs:
+ oVM = self.createTestVM('tst-rhel5-64', 1, '3.0/tcp/rhel5-64.vdi', sKind = 'RedHat_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sles11' in self.asTestVMs:
+ oVM = self.createTestVM('tst-sles11', 1, '3.0/tcp/sles11.vdi', sKind = 'OpenSUSE', fIoApic = True, \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sles11-64' in self.asTestVMs:
+ oVM = self.createTestVM('tst-sles11-64', 1, '3.0/tcp/sles11-64.vdi', sKind = 'OpenSUSE_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-oel' in self.asTestVMs:
+ oVM = self.createTestVM('tst-oel', 1, '3.0/tcp/oel.vdi', sKind = 'Oracle', fIoApic = True, \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-oel-64' in self.asTestVMs:
+ oVM = self.createTestVM('tst-oel-64', 1, '3.0/tcp/oel-64.vdi', sKind = 'Oracle_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ # Windows VMs
+ if 'tst-win2k3ent' in self.asTestVMs:
+ oVM = self.createTestVM('tst-win2k3ent', 1, '3.0/tcp/win2k3ent-acpi.vdi', sKind = 'Windows2003', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-win2k3ent-64' in self.asTestVMs:
+ oVM = self.createTestVM('tst-win2k3ent-64', 1, '3.0/tcp/win2k3ent-64.vdi', sKind = 'Windows2003_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-win2k8' in self.asTestVMs:
+ oVM = self.createTestVM('tst-win2k8', 1, '3.0/tcp/win2k8.vdi', sKind = 'Windows2008_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ # Solaris VMs
+ if 'tst-sol10' in self.asTestVMs:
+ oVM = self.createTestVM('tst-sol10', 1, '3.0/tcp/solaris10.vdi', sKind = 'Solaris_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sol11' in self.asTestVMs:
+ oVM = self.createTestVM('tst-sol11', 1, '3.0/tcp/os2009-11.vdi', sKind = 'Solaris_64', \
+ eNic0AttachType = eNic0AttachType, sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.test1();
+ return fRc;
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def test1RunTestProgs(self, oTxsSession, fRc, sTestName, sAddr):
+ """
+ Runs all the test programs against one 'server' machine.
+ """
+ reporter.testStart(sTestName);
+
+ reporter.testStart('TCP latency');
+ if fRc and 'tcp-latency' in self.asTests and sAddr is not None:
+ for cbPkt in self.acbLatencyPkts:
+ fRc = self.txsRunTest(oTxsSession, '%u bytes' % (cbPkt), self.cSecsRun * 1000 * 4,
+ '${CDROM}/${OS/ARCH}/NetPerf${EXESUFF}',
+ ('NetPerf', '--client', sAddr, '--interval', self.cSecsRun, '--len', cbPkt,
+ '--mode', 'latency'));
+ if not fRc:
+ break;
+ reporter.testDone();
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testStart('TCP throughput');
+ if fRc and 'tcp-throughput' in self.asTests and sAddr is not None:
+ for cbPkt in self.acbThroughputPkts:
+ fRc = self.txsRunTest(oTxsSession, '%u bytes' % (cbPkt), self.cSecsRun * 2 * 1000 * 4,
+ '${CDROM}/${OS/ARCH}/NetPerf${EXESUFF}',
+ ('NetPerf', '--client', sAddr, '--interval', self.cSecsRun, '--len', cbPkt,
+ '--mode', 'throughput'));
+ if not fRc:
+ break;
+ reporter.testDone();
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testStart('UDP latency');
+ if fRc and 'udp-latency' in self.asTests and sAddr is not None:
+ ## @todo Netperf w/UDP.
+ reporter.testDone(fSkipped = True);
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testStart('UDP throughput');
+ if fRc and 'udp-throughput' in self.asTests and sAddr is not None:
+ ## @todo Netperf w/UDP.
+ reporter.testDone(fSkipped = True);
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testStart('tbench');
+ if fRc and 'tbench' in self.asTests and sAddr is not None:
+ ## @todo tbench.
+ reporter.testDone(fSkipped = True);
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testDone(not fRc);
+ return fRc;
+
+ def test1OneCfg(self, sVmName, eNicType, cCpus, fHwVirt, fNestedPaging):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ oVM = self.getVmByName(sVmName);
+
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = fRc and oSession.setNicType(eNicType);
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(sVmName, fCdWait = True);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+
+ # Benchmark #1 - guest <-> host.
+ if 'g2h' in self.asSetups:
+ self.test1RunTestProgs(oTxsSession, fRc, 'guest <-> host', self.sLocalIP);
+
+ # Benchmark #2 - guest <-> host.
+ if 'g2r' in self.asSetups:
+ self.test1RunTestProgs(oTxsSession, fRc, 'guest <-> remote', self.sRemoteIP);
+
+ # Benchmark #3 - guest <-> guest.
+ if 'g2g' in self.asSetups:
+ self.test1RunTestProgs(oTxsSession, fRc, 'guest <-> guest', self.sGuestIP);
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+ else:
+ fRc = False;
+ return fRc;
+
+ def test1OneVM(self, sVmName, asSkipNicTypes = (), asSupVirtModes = None, rSupCpus = range(1, 256)):
+ """
+ Runs one VM thru the various configurations.
+ """
+ if asSupVirtModes is None:
+ asSupVirtModes = self.asVirtModes;
+
+ reporter.testStart(sVmName);
+ fRc = True;
+ for sNicType in self.asNicTypes:
+ if sNicType in asSkipNicTypes:
+ continue;
+ reporter.testStart(sNicType);
+
+ if sNicType == 'E1000':
+ eNicType = vboxcon.NetworkAdapterType_I82545EM;
+ elif sNicType == 'PCNet':
+ eNicType = vboxcon.NetworkAdapterType_Am79C973;
+ elif sNicType == 'Virtio':
+ eNicType = vboxcon.NetworkAdapterType_Virtio;
+ else:
+ eNicType = None;
+
+ for cCpus in self.acCpus:
+ if cCpus == 1: reporter.testStart('1 cpu');
+ else: reporter.testStart('%u cpus' % (cCpus));
+
+ for sVirtMode in self.asVirtModes:
+ if sVirtMode == 'raw' and cCpus > 1:
+ continue;
+ if cCpus not in rSupCpus:
+ continue;
+ if sVirtMode not in asSupVirtModes:
+ continue;
+ hsVirtModeDesc = {};
+ hsVirtModeDesc['raw'] = 'Raw-mode';
+ hsVirtModeDesc['hwvirt'] = 'HwVirt';
+ hsVirtModeDesc['hwvirt-np'] = 'NestedPaging';
+ reporter.testStart(hsVirtModeDesc[sVirtMode]);
+
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+ fRc = self.test1OneCfg(sVmName, eNicType, cCpus, fHwVirt, fNestedPaging) and fRc and True; # pychecker hack.
+
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ return fRc;
+
+ def test1(self):
+ """
+ Executes test #1.
+ """
+
+ # Start the VM for the guest to guest testing, if required.
+ fRc = True;
+ if 'g2g' in self.asSetups and self.sGuestName is None:
+ self.oGuestToGuestSess, self.oGuestToGuestTxs = self.startVmAndConnectToTxsViaTcp('tst-guest2guest', fCdWait = True);
+ if self.oGuestToGuestSess is None:
+ return False;
+ self.sGuestIP = self.oGuestToGuestSess.getPrimaryIp();
+ reporter.log('tst-guest2guest IP: %s' % (self.sGuestIP));
+
+ # Start the test servers on it.
+ fRc = self.oGuestToGuestTxs.syncExec('${CDROM}/${OS/ARCH}/NetPerf${EXESUFF}',
+ ('NetPerf', '--server', '--daemonize'), fWithTestPipe=False);
+
+ # Loop thru the test VMs.
+ if fRc:
+ for sVM in self.asTestVMs:
+ # figure args.
+ asSkipNicTypes = [];
+ if sVM not in ('tst-sles11', 'tst-sles11-64'):
+ asSkipNicTypes.append('Virtio');
+ if sVM in ('tst-sol11', 'tst-sol10'):
+ asSkipNicTypes.append('PCNet');
+ asSupVirtModes = None;
+ if sVM in ('tst-sol11', 'tst-sol10'): # 64-bit only
+ asSupVirtModes = ('hwvirt', 'hwvirt-np',);
+
+ # run test on the VM.
+ if not self.test1OneVM(sVM, asSkipNicTypes, asSupVirtModes):
+ fRc = False;
+
+ # Kill the guest to guest VM and clean up the state.
+ if self.oGuestToGuestSess is not None:
+ self.terminateVmBySession(self.oGuestToGuestSess);
+ self.oGuestToGuestSess = None;
+ self.oGuestToGuestTxs = None;
+ self.sGuestIP = None;
+
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdNetBenchmark1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/selftests/Makefile.kmk b/src/VBox/ValidationKit/tests/selftests/Makefile.kmk
new file mode 100644
index 00000000..70eca8d8
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/Makefile.kmk
@@ -0,0 +1,44 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Testsuite & TestManager Tests.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsSelfTests
+ValidationKitTestsSelfTests_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsSelfTests_INST = $(INST_VALIDATIONKIT)tests/selftests/
+ValidationKitTestsSelfTests_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdSelfTest1.py \
+ $(PATH_SUB_CURRENT)/tdSelfTest2.py \
+ $(PATH_SUB_CURRENT)/tdSelfTest3.py \
+ $(PATH_SUB_CURRENT)/tdSelfTest4.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsSelfTests_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/selftests/tdSelfTest1.py b/src/VBox/ValidationKit/tests/selftests/tdSelfTest1.py
new file mode 100755
index 00000000..a2ea939c
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/tdSelfTest1.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSelfTest1.py $
+
+"""
+Test Manager Self Test - Dummy Test Driver.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+import sys;
+
+print('dummydriver.py: hello world!');
+print('dummydriver.py: args: %s' % (sys.argv,));
+if sys.argv[-1] in [ 'all', 'execute' ]:
+
+ import time;
+ for i in range(10, 1, -1):
+ print('dummydriver.py: %u...', i);
+ sys.stdout.flush();
+ time.sleep(1);
+ print('dummydriver.py: ...0! done');
+
+sys.exit(0);
+
diff --git a/src/VBox/ValidationKit/tests/selftests/tdSelfTest2.py b/src/VBox/ValidationKit/tests/selftests/tdSelfTest2.py
new file mode 100755
index 00000000..937e7dcf
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/tdSelfTest2.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSelfTest2.py $
+
+"""
+Test Manager / Suite Self Test #2 - Everything should succeed.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import reporter;
+from testdriver.base import TestDriverBase;
+
+
+class tdSelfTest2(TestDriverBase):
+ """
+ Test Manager / Suite Self Test #2 - Everything should succeed.
+ """
+
+ def __init__(self):
+ TestDriverBase.__init__(self);
+
+
+ def actionExecute(self):
+ reporter.testStart('reporter.testXXXX API');
+ reporter.testValue('value-name1', 123456789, 'ms');
+
+ reporter.testStart('subtest');
+ reporter.testValue('value-name2', 11223344, 'times');
+ reporter.testDone();
+
+ reporter.testStart('subtest2');
+ reporter.testValue('value-name3', 39, 'sec');
+ reporter.testValue('value-name4', 42, 'ns');
+ reporter.testDone();
+
+ reporter.testStart('subtest3');
+ reporter.testDone(fSkipped = True);
+
+ # No spaces in XML.
+ reporter.testStart('subtest4');
+ oSubXmlFile = reporter.FileWrapperTestPipe();
+ oSubXmlFile.write('<?xml version="1.0" encoding="UTF-8" ?>');
+ oSubXmlFile.write('<Test timestamp="%s" name="foobar1">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Test timestamp="%s" name="sub1">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Passed timestamp="%s"/>' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('</Test>');
+ oSubXmlFile.write('<End timestamp="%s" errors="0"/>' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('</Test>');
+ oSubXmlFile.close();
+ oSubXmlFile = None;
+ reporter.testDone();
+
+ # Spaces + funny line endings.
+ reporter.testStart('subtest5');
+ oSubXmlFile = reporter.FileWrapperTestPipe();
+ oSubXmlFile.write('<?xml version="1.0" encoding="UTF-8" ?>\r\n');
+ oSubXmlFile.write('<Test timestamp="%s" name="foobar2">\n\n\t\n\r\n' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Test timestamp="%s" name="sub2">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write(' <Passed timestamp="%s"/>\n' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write(' </Test>\n');
+ oSubXmlFile.write(' <End timestamp="%s" errors="0"/>\r' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('</Test>');
+ oSubXmlFile.close();
+ reporter.testDone();
+
+ # A few long log times for WUI log testing.
+ reporter.log('long line: asdfasdfljkasdlfkjasldkfjlaksdfjl asdlfkjasdlkfjalskdfjlaksdjfa falkjaldkjfalskdjflaksdjf ' \
+ 'lajksdflkjasdlfkjalsdfj asldfkjlaskdjflaksdjflaksdjflkj asdlfkjalsdkfjalsdkjflaksdj fasdlfkj ' \
+ 'asdlkfj aljkasdflkj alkjdsf lakjsdf');
+ reporter.log('long line: asdfasdfljkasdlfkjasldkfjlaksdfjl asdlfkjasdlkfjalskdfjlaksdjfa falkjaldkjfalskdjflaksdjf ' \
+ 'lajksdflkjasdlfkjalsdfj asldfkjlaskdjflaksdjflaksdjflkj asdlfkjalsdkfjalsdkjflaksdj fasdlfkj ' \
+ 'asdlkfj aljkasdflkj alkjdsf lakjsdf');
+ reporter.log('long line: asdfasdfljkasdlfkjasldkfjlaksdfjl asdlfkjasdlkfjalskdfjlaksdjfa falkjaldkjfalskdjflaksdjf ' \
+ 'lajksdflkjasdlfkjalsdfj asldfkjlaskdjflaksdjflaksdjflkj asdlfkjalsdkfjalsdkjflaksdj fasdlfkj ' \
+ 'asdlkfj aljkasdflkj alkjdsf lakjsdf');
+
+ # Upload a file.
+ reporter.addLogFile(__file__, sKind = 'log/release/vm');
+
+ reporter.testDone();
+ return True;
+
+
+if __name__ == '__main__':
+ sys.exit(tdSelfTest2().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/selftests/tdSelfTest3.py b/src/VBox/ValidationKit/tests/selftests/tdSelfTest3.py
new file mode 100755
index 00000000..7fd11dda
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/tdSelfTest3.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSelfTest3.py $
+
+"""
+Test Manager / Suite Self Test #3 - Bad XML input and other Failures.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import reporter;
+from testdriver.base import TestDriverBase;
+
+
+class tdSelfTest3(TestDriverBase):
+ """
+ Test Manager / Suite Self Test #3 - Bad XML input and other Failures.
+ """
+
+ def __init__(self):
+ TestDriverBase.__init__(self);
+
+
+ def actionExecute(self):
+
+ # Testing PushHint/PopHint.
+ reporter.testStart('Negative XML #1');
+ oSubXmlFile = reporter.FileWrapperTestPipe();
+ oSubXmlFile.write('<Test timestamp="%s" name="foobar3">\n\n\t\n\r\n' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Test timestamp="%s" name="sub3">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.write('<Test timestamp="%s" name="subsub1">' % (utils.getIsoTimestamp(),));
+ oSubXmlFile.close();
+ reporter.testDone();
+
+ # Missing end, like we had with IRPT at one time.
+ reporter.testStart('Negative XML #2 (IPRT)');
+ oSubXmlFile = reporter.FileWrapperTestPipe();
+ oSubXmlFile.write("""
+<?xml version="1.0" encoding="UTF-8" ?>
+<Test timestamp="2013-05-29T08:59:05.930602000Z" name="tstRTGetOpt">
+ <Test timestamp="2013-05-29T08:59:05.930656000Z" name="Basics">
+ <Passed timestamp="2013-05-29T08:59:05.930756000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.930995000Z" name="RTGetOpt - IPv4">
+ <Passed timestamp="2013-05-29T08:59:05.931036000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931161000Z" name="RTGetOpt - MAC Address">
+ <Passed timestamp="2013-05-29T08:59:05.931194000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931313000Z" name="RTGetOpt - Option w/ Index">
+ <Passed timestamp="2013-05-29T08:59:05.931357000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931475000Z" name="RTGetOptFetchValue">
+ <Passed timestamp="2013-05-29T08:59:05.931516000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931640000Z" name="RTGetOpt - bool on/off">
+ <Passed timestamp="2013-05-29T08:59:05.931687000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931807000Z" name="Standard options">
+ <Passed timestamp="2013-05-29T08:59:05.931843000Z"/>
+ </Test>
+ <Test timestamp="2013-05-29T08:59:05.931963000Z" name="Options first">
+ <Passed timestamp="2013-05-29T08:59:05.932035000Z"/>
+ </Test>
+""");
+ oSubXmlFile.close();
+ oSubXmlFile = None;
+ reporter.testDone();
+
+ # The use of testFailure.
+ reporter.testStart('Using testFailure()');
+ reporter.testValue('value-name3', 12345678, 'times');
+ reporter.testFailure('failure detail message');
+ reporter.testDone();
+
+ return True;
+
+
+if __name__ == '__main__':
+ sys.exit(tdSelfTest3().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/selftests/tdSelfTest4.py b/src/VBox/ValidationKit/tests/selftests/tdSelfTest4.py
new file mode 100755
index 00000000..1258e5c6
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/selftests/tdSelfTest4.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSelfTest4.py $
+
+"""
+Test Manager / Suite Self Test #4 - Testing result overflow handling.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver.base import TestDriverBase, InvalidOption;
+
+
+class tdSelfTest4(TestDriverBase):
+ """
+ Test Manager / Suite Self Test #4 - Testing result overflow handling.
+ """
+
+ ## Valid tests.
+ kasValidTests = [ 'immediate-sub-tests', 'total-sub-tests', 'immediate-values', 'total-values', 'immediate-messages'];
+
+ def __init__(self):
+ TestDriverBase.__init__(self);
+ self.sOptWhich = 'immediate-sub-tests';
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--test':
+ iArg = self.requireMoreArgs(1, asArgs, iArg);
+ if asArgs[iArg] not in self.kasValidTests:
+ raise InvalidOption('Invalid test name "%s". Must be one of: %s'
+ % (asArgs[iArg], ', '.join(self.kasValidTests),));
+ self.sOptWhich = asArgs[iArg];
+ else:
+ return TestDriverBase.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionExecute(self):
+ # Too many immediate sub-tests.
+ if self.sOptWhich == 'immediate-sub-tests':
+ reporter.testStart('Too many immediate sub-tests (negative)');
+ for i in range(1024):
+ reporter.testStart('subsub%d' % i);
+ reporter.testDone();
+ # Too many sub-tests in total.
+ elif self.sOptWhich == 'total-sub-tests':
+ reporter.testStart('Too many sub-tests (negative)');
+ # 32 * 256 = 2^(5+8) = 2^13 = 8192.
+ for i in range(32):
+ reporter.testStart('subsub%d' % i);
+ for j in range(256):
+ reporter.testStart('subsubsub%d' % j);
+ reporter.testDone();
+ reporter.testDone();
+ # Too many immediate values.
+ elif self.sOptWhich == 'immediate-values':
+ reporter.testStart('Too many immediate values (negative)');
+ for i in range(512):
+ reporter.testValue('value%d' % i, i, 'times');
+ # Too many values in total.
+ elif self.sOptWhich == 'total-values':
+ reporter.testStart('Too many sub-tests (negative)');
+ for i in range(256):
+ reporter.testStart('subsub%d' % i);
+ for j in range(64):
+ reporter.testValue('value%d' % j, i * 10000 + j, 'times');
+ reporter.testDone();
+ # Too many failure reasons (only immediate since the limit is extremely low).
+ elif self.sOptWhich == 'immediate-messages':
+ reporter.testStart('Too many immediate messages (negative)');
+ for i in range(16):
+ reporter.testFailure('Detail %d' % i);
+ else:
+ reporter.testStart('Unknown test %s' % (self.sOptWhich,));
+ reporter.error('Invalid test selected: %s' % (self.sOptWhich,));
+ reporter.testDone();
+ return True;
+
+
+if __name__ == '__main__':
+ sys.exit(tdSelfTest4().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/serial/Makefile.kmk b/src/VBox/ValidationKit/tests/serial/Makefile.kmk
new file mode 100644
index 00000000..ac4235fb
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/serial/Makefile.kmk
@@ -0,0 +1,42 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Serial port.
+#
+
+#
+# Copyright (C) 2018-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsSerial
+ValidationKitTestsSerial_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsSerial_INST = $(INST_VALIDATIONKIT)tests/serial/
+ValidationKitTestsSerial_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdSerial1.py \
+ $(PATH_SUB_CURRENT)/loopback.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsSerial_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/serial/loopback.py b/src/VBox/ValidationKit/tests/serial/loopback.py
new file mode 100755
index 00000000..a0b71a91
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/serial/loopback.py
@@ -0,0 +1,247 @@
+# -*- coding: utf-8 -*-
+# $Id: loopback.py $
+
+"""
+VirtualBox Validation Kit - Serial loopback module.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2018-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+# Standard Python imports.
+#import os;
+import socket;
+import threading;
+
+
+g_ksLoopbackTcpServ = 'TcpServ';
+g_ksLoopbackTcpClient = 'TcpClient';
+g_ksLoopbackNamedPipeServ = 'NamedPipeServ';
+g_ksLoopbackNamedPipeClient = 'NamedPipeClient';
+
+class SerialLoopbackTcpServ(object):
+ """
+ Handler for a server TCP style connection.
+ """
+ def __init__(self, sLocation, iTimeout):
+ sHost, sPort = sLocation.split(':');
+ self.oConn = None;
+ self.oSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
+ self.oSock.settimeout(iTimeout);
+ self.oSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
+ self.oSock.bind((sHost, int(sPort)));
+ self.oSock.listen(1);
+ self.iTimeout = iTimeout;
+
+ def __del__(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ if self.oSock is not None:
+ self.oSock.close();
+ self.oSock = None;
+
+ def shutdown(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ self.oConn = None;
+ self.oSock.close();
+ self.oSock = None;
+
+ def pumpIo(self):
+ """
+ Main I/O pumping routine.
+ """
+ try:
+ if self.oConn is None:
+ oConn, _ = self.oSock.accept();
+ self.oConn = oConn;
+ else:
+ abData = self.oConn.recv(1024); # pylint: disable=no-member
+ if abData is not None:
+ self.oConn.send(abData); # pylint: disable=no-member
+ except:
+ pass;
+
+class SerialLoopbackTcpClient(object):
+ """
+ Handler for a client TCP style connection.
+ """
+ def __init__(self, sLocation, iTimeout):
+ sHost, sPort = sLocation.split(':');
+ self.oConn = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
+ self.oConn.connect((sHost, int(sPort)));
+ self.oConn.settimeout(iTimeout);
+ self.iTimeout = iTimeout;
+
+ def __del__(self):
+ if self.oConn is not None:
+ self.oConn.close();
+
+ def shutdown(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ self.oConn = None;
+
+ def pumpIo(self):
+ """
+ Main I/O pumping routine.
+ """
+ try:
+ abData = self.oConn.recv(1024);
+ if abData is not None:
+ self.oConn.send(abData);
+ except:
+ pass;
+
+class SerialLoopbackNamedPipeServ(object):
+ """
+ Handler for a named pipe server style connection.
+ """
+ def __init__(self, sLocation, iTimeout):
+ self.oConn = None;
+ self.oSock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM); # pylint: disable=no-member
+ self.oSock.settimeout(iTimeout);
+ self.oSock.bind(sLocation);
+ self.oSock.listen(1);
+ self.iTimeout = iTimeout;
+
+ def __del__(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ if self.oSock is not None:
+ self.oSock.close();
+ self.oSock = None;
+
+ def shutdown(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ self.oConn = None;
+ self.oSock.close();
+ self.oSock = None;
+
+ def pumpIo(self):
+ """
+ Main I/O pumping routine.
+ """
+ try:
+ if self.oConn is None:
+ oConn, _ = self.oSock.accept();
+ self.oConn = oConn;
+ else:
+ abData = self.oConn.recv(1024); # pylint: disable=no-member
+ if abData is not None:
+ self.oConn.send(abData); # pylint: disable=no-member
+ except:
+ pass;
+
+class SerialLoopbackNamedPipeClient(object):
+ """
+ Handler for a named pipe client style connection.
+ """
+ def __init__(self, sLocation, iTimeout):
+ self.oConn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM); # pylint: disable=no-member
+ self.oConn.connect(sLocation);
+ self.oConn.settimeout(iTimeout);
+ self.iTimeout = iTimeout;
+
+ def __del__(self):
+ if self.oConn is not None:
+ self.oConn.close();
+
+ def shutdown(self):
+ if self.oConn is not None:
+ self.oConn.close();
+ self.oConn = None;
+
+ def pumpIo(self):
+ """
+ Main I/O pumping routine.
+ """
+ try:
+ abData = self.oConn.recv(1024);
+ if abData is not None:
+ self.oConn.send(abData);
+ except:
+ pass;
+
+class SerialLoopback(object):
+ """
+ Serial port loopback module working with TCP and named pipes.
+ """
+
+ def __init__(self, sType, sLocation):
+ self.fShutdown = False;
+ self.sType = sType;
+ self.sLocation = sLocation;
+ self.oLock = threading.Lock();
+ self.oThread = threading.Thread(target=self.threadWorker, args=(), name=('SerLoopback'));
+
+ if sType == g_ksLoopbackTcpServ:
+ self.oIoPumper = SerialLoopbackTcpServ(sLocation, 0.5);
+ self.oThread.start();
+ elif sType == g_ksLoopbackNamedPipeServ:
+ self.oIoPumper = SerialLoopbackNamedPipeServ(sLocation, 0.5); # pylint: disable=redefined-variable-type
+ self.oThread.start();
+
+ def connect(self):
+ """
+ Connects to the server for a client type version.
+ """
+ fRc = True;
+ try:
+ if self.sType == g_ksLoopbackTcpClient:
+ self.oIoPumper = SerialLoopbackTcpClient(self.sLocation, 0.5);
+ elif self.sType == g_ksLoopbackNamedPipeClient:
+ self.oIoPumper = SerialLoopbackNamedPipeClient(self.sLocation, 0.5); # pylint: disable=redefined-variable-type
+ except:
+ fRc = False;
+ else:
+ self.oThread.start();
+ return fRc;
+
+ def shutdown(self):
+ """
+ Shutdown any connection and wait for it to become idle.
+ """
+ self.oLock.acquire();
+ self.fShutdown = True;
+ self.oLock.release();
+ self.oIoPumper.shutdown();
+
+ def isShutdown(self):
+ """
+ Returns whether the I/O pumping thread should shut down.
+ """
+ self.oLock.acquire();
+ fShutdown = self.fShutdown;
+ self.oLock.release();
+
+ return fShutdown;
+
+ def threadWorker(self):
+ """
+ The threaded worker.
+ """
+ while not self.isShutdown():
+ self.oIoPumper.pumpIo();
+
diff --git a/src/VBox/ValidationKit/tests/serial/tdSerial1.py b/src/VBox/ValidationKit/tests/serial/tdSerial1.py
new file mode 100755
index 00000000..b2f0b264
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/serial/tdSerial1.py
@@ -0,0 +1,337 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSerial1.py $
+
+"""
+VirtualBox Validation Kit - Serial port testing #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2018-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import random;
+import string;
+import struct;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import base;
+from testdriver import reporter;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+import loopback;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class tdSerial1(vbox.TestDriver):
+ """
+ VBox serial port testing #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oTestVmSet = self.oTestVmManager.selectSet(self.oTestVmManager.kfGrpStdSmoke);
+ self.asSerialModesDef = ['RawFile', 'Tcp', 'TcpServ', 'NamedPipe', 'NamedPipeServ', 'HostDev'];
+ self.asSerialModes = self.asSerialModesDef;
+ self.asSerialTestsDef = ['Write', 'ReadWrite'];
+ self.asSerialTests = self.asSerialTestsDef;
+ self.asUartsDef = ['16450', '16550A', '16750'];
+ self.asUarts = self.asUartsDef;
+ self.oLoopback = None;
+ self.sLocation = None;
+ self.fVerboseTest = False;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdSerial1 Options:');
+ reporter.log(' --serial-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asSerialModesDef)));
+ reporter.log(' --serial-tests <t1[:t2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asSerialTestsDef)));
+ reporter.log(' --uarts <u1[:u2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asUartsDef)));
+ reporter.log(' --verbose-test');
+ reporter.log(' Whether to enable verbose output when running the');
+ reporter.log(' test utility inside the VM');
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--serial-modes':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--serial-modes" takes a colon separated list of serial port modes to test');
+ self.asSerialModes = asArgs[iArg].split(':');
+ for s in self.asSerialModes:
+ if s not in self.asSerialModesDef:
+ reporter.log('warning: The "--serial-modes" value "%s" is not a valid serial port mode.' % (s));
+ elif asArgs[iArg] == '--serial-tests':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--serial-tests" takes a colon separated list of serial port tests');
+ self.asSerialTests = asArgs[iArg].split(':');
+ for s in self.asSerialTests:
+ if s not in self.asSerialTestsDef:
+ reporter.log('warning: The "--serial-tests" value "%s" is not a valid serial port test.' % (s));
+ elif asArgs[iArg] == '--aurts':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--uarts" takes a colon separated list of uarts to test');
+ self.asUarts = asArgs[iArg].split(':');
+ for s in self.asUarts:
+ if s not in self.asUartsDef:
+ reporter.log('warning: The "--uarts" value "%s" is not a valid uart.' % (s));
+ elif asArgs[iArg] == '--verbose-test':
+ iArg += 1;
+ self.fVerboseTest = True;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+
+ return iArg + 1;
+
+ def actionVerify(self):
+ if self.sVBoxValidationKitIso is None or not os.path.isfile(self.sVBoxValidationKitIso):
+ reporter.error('Cannot find the VBoxValidationKit.iso! (%s)'
+ 'Please unzip a Validation Kit build in the current directory or in some parent one.'
+ % (self.sVBoxValidationKitIso,) );
+ return False;
+ return vbox.TestDriver.actionVerify(self);
+
+ def actionConfig(self):
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ assert self.sVBoxValidationKitIso is not None;
+ return self.oTestVmSet.actionConfig(self, sDvdImage = self.sVBoxValidationKitIso);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def _generateRawPortFilename(self, oTestDrv, oTestVm, sInfix, sSuffix):
+ """ Generates a raw port filename. """
+ random.seed();
+ sRandom = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10));
+ return os.path.join(oTestDrv.sScratchPath, oTestVm.sVmName + sInfix + sRandom + sSuffix);
+
+ def setupSerialMode(self, oSession, oTestVm, sMode):
+ """
+ Sets up the serial mode.
+ """
+ fRc = True;
+ fServer = False;
+ sLocation = None;
+ ePortMode = vboxcon.PortMode_Disconnected;
+ if sMode == 'RawFile':
+ sLocation = self._generateRawPortFilename(self, oTestVm, '-com1-', '.out');
+ ePortMode = vboxcon.PortMode_RawFile;
+ elif sMode == 'Tcp':
+ sLocation = '127.0.0.1:1234';
+ self.oLoopback = loopback.SerialLoopback(loopback.g_ksLoopbackTcpServ, sLocation);
+ ePortMode = vboxcon.PortMode_TCP;
+ elif sMode == 'TcpServ':
+ fServer = True;
+ sLocation = '1234';
+ ePortMode = vboxcon.PortMode_TCP;
+ self.oLoopback = loopback.SerialLoopback(loopback.g_ksLoopbackTcpClient, '127.0.0.1:1234');
+ elif sMode == 'NamedPipe':
+ sLocation = self._generateRawPortFilename(self, oTestVm, '-com1-', '.out');
+ ePortMode = vboxcon.PortMode_HostPipe;
+ self.oLoopback = loopback.SerialLoopback(loopback.g_ksLoopbackNamedPipeServ, sLocation);
+ elif sMode == 'NamedPipeServ':
+ fServer = True;
+ sLocation = self._generateRawPortFilename(self, oTestVm, '-com1-', '.out');
+ ePortMode = vboxcon.PortMode_HostPipe;
+ self.oLoopback = loopback.SerialLoopback(loopback.g_ksLoopbackNamedPipeClient, sLocation);
+ elif sMode == 'HostDev':
+ sLocation = '/dev/ttyUSB0';
+ ePortMode = vboxcon.PortMode_HostDevice;
+ else:
+ reporter.log('warning, invalid mode %s given' % (sMode, ));
+ fRc = False;
+
+ if fRc:
+ fRc = oSession.changeSerialPortAttachment(0, ePortMode, sLocation, fServer);
+ if fRc and (sMode == 'TcpServ' or sMode == 'NamedPipeServ'):
+ self.sleep(2); # Fudge to allow the TCP server to get started.
+ fRc = self.oLoopback.connect();
+ if not fRc:
+ reporter.log('Failed to connect to %s' % (sLocation, ));
+ self.sLocation = sLocation;
+
+ return fRc;
+
+ def testWrite(self, oSession, oTxsSession, oTestVm, sMode):
+ """
+ Does a simple write test verifying the output.
+ """
+ _ = oSession;
+
+ reporter.testStart('Write');
+ tupCmdLine = ('SerialTest', '--tests', 'write', '--txbytes', '1048576');
+ if self.fVerboseTest:
+ tupCmdLine += ('--verbose',);
+ if oTestVm.isWindows():
+ tupCmdLine += ('--device', r'\\.\COM1',);
+ elif oTestVm.isLinux():
+ tupCmdLine += ('--device', r'/dev/ttyS0',);
+
+ fRc = self.txsRunTest(oTxsSession, 'SerialTest', 3600 * 1000, \
+ '${CDROM}/${OS/ARCH}/SerialTest${EXESUFF}', tupCmdLine);
+ if not fRc:
+ reporter.testFailure('Running serial test utility failed');
+ elif sMode == 'RawFile':
+ # Open serial port and verify
+ cLast = 0;
+ try:
+ oFile = open(self.sLocation, 'rb');
+ sFmt = '=I';
+ cBytes = 4;
+ for i in xrange(1048576 // 4):
+ _ = i;
+ sData = oFile.read(cBytes);
+ tupUnpacked = struct.unpack(sFmt, sData);
+ cLast = cLast + 1;
+ if tupUnpacked[0] != cLast:
+ reporter.testFailure('Corruption detected, expected counter value %s, got %s'
+ % (cLast + 1, tupUnpacked[0]));
+ break;
+ oFile.close();
+ except:
+ reporter.logXcpt();
+ reporter.testFailure('Verifying the written data failed');
+ reporter.testDone();
+ return fRc;
+
+ def testReadWrite(self, oSession, oTxsSession, oTestVm):
+ """
+ Does a simple write test verifying the output.
+ """
+ _ = oSession;
+
+ reporter.testStart('ReadWrite');
+ tupCmdLine = ('SerialTest', '--tests', 'readwrite', '--txbytes', '1048576');
+ if self.fVerboseTest:
+ tupCmdLine += ('--verbose',);
+ if oTestVm.isWindows():
+ tupCmdLine += ('--device', r'\\.\COM1',);
+ elif oTestVm.isLinux():
+ tupCmdLine += ('--device', r'/dev/ttyS0',);
+
+ fRc = self.txsRunTest(oTxsSession, 'SerialTest', 600 * 1000, \
+ '${CDROM}/${OS/ARCH}/SerialTest${EXESUFF}', tupCmdLine);
+ if not fRc:
+ reporter.testFailure('Running serial test utility failed');
+
+ reporter.testDone();
+ return fRc;
+
+ def isModeCompatibleWithTest(self, sMode, sTest):
+ """
+ Returns whether the given port mode and test combination is
+ supported for testing.
+ """
+ if sMode == 'RawFile' and sTest == 'ReadWrite':
+ return False;
+ elif sMode != 'RawFile' and sTest == 'Write':
+ return False;
+
+ return True;
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru test #1.
+ """
+
+ for sUart in self.asUarts:
+ reporter.testStart(sUart);
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = oSession.enableSerialPort(0);
+
+ fRc = fRc and oSession.setExtraData("VBoxInternal/Devices/serial/0/Config/UartType", "string:" + sUart);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc;
+ oSession = None;
+ else:
+ fRc = False;
+
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = True);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ for sMode in self.asSerialModes:
+ reporter.testStart(sMode);
+ fRc = self.setupSerialMode(oSession, oTestVm, sMode);
+ if fRc:
+ for sTest in self.asSerialTests:
+ # Skip tests which don't work with the current mode.
+ if self.isModeCompatibleWithTest(sMode, sTest):
+ if sTest == 'Write':
+ fRc = self.testWrite(oSession, oTxsSession, oTestVm, sMode);
+ if sTest == 'ReadWrite':
+ fRc = self.testReadWrite(oSession, oTxsSession, oTestVm);
+ if self.oLoopback is not None:
+ self.oLoopback.shutdown();
+ self.oLoopback = None;
+
+ reporter.testDone();
+
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession);
+ reporter.testDone();
+
+ return fRc;
+
+if __name__ == '__main__':
+ sys.exit(tdSerial1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/shutdown/tdGuestOsShutdown1.py b/src/VBox/ValidationKit/tests/shutdown/tdGuestOsShutdown1.py
new file mode 100755
index 00000000..d1fae342
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/shutdown/tdGuestOsShutdown1.py
@@ -0,0 +1,357 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+VMM Guest OS boot tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import time
+
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0]
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from testdriver import vbox
+from testdriver import base
+from testdriver import reporter
+from testdriver import vboxcon
+
+
+class tdGuestOsBootTest1(vbox.TestDriver):
+ """
+ VMM Unit Tests Set.
+
+ Scenario:
+ - Create VM that corresponds to Guest OS pre-installed on selected HDD
+ - Start VM and wait for TXS server connection (which is started after Guest successfully booted)
+ """
+
+ ksSataController = 'SATA Controller'
+ ksIdeController = 'IDE Controller'
+
+ # VM parameters required to run HDD image.
+ # Format: { HDD image filename: (sKind, HDD controller type) }
+ kaoVmParams = {
+ 't-win80.vdi': ( 'Windows 8 (64 bit)', ksSataController ),
+ }
+
+ # List of platforms which are able to suspend and resume host automatically.
+ # In order to add new platform, self._SuspendResume() should be adapted.
+ kasSuspendAllowedPlatforms = ( 'darwin' )
+
+ kcMsVmStartLimit = 5 * 60000
+ kcMsVmShutdownLimit = 1 * 60000
+
+ def __init__(self):
+ """
+ Reinitialize child class instance.
+ """
+ vbox.TestDriver.__init__(self)
+
+ self.sVmName = 'TestVM'
+ self.sHddName = None
+ self.sHddPathBase = os.path.join(self.sResourcePath, '4.2', 'nat', 'win80')
+ self.oVM = None
+
+ # TODO: that should be moved to some common place
+ self.fEnableIOAPIC = True
+ self.cCpus = 1
+ self.fEnableNestedPaging = True
+ self.fEnablePAE = False
+ self.fSuspendHost = False
+ self.cSecSuspendTime = 60
+ self.cShutdownIters = 1
+ self.fExtraVm = False
+ self.sExtraVmName = "TestVM-Extra"
+ self.oExtraVM = None
+ self.fLocalCatch = False
+
+ #
+ # Overridden methods.
+ #
+
+ def showUsage(self):
+ """
+ Extend usage info
+ """
+ rc = vbox.TestDriver.showUsage(self)
+ reporter.log(' --boot-hdd <HDD image file name>')
+
+ reporter.log(' --cpus <# CPUs>')
+ reporter.log(' --no-ioapic')
+ reporter.log(' --no-nested-paging')
+ reporter.log(' --pae')
+ reporter.log(' --suspend-host')
+ reporter.log(' --suspend-time <sec>')
+ reporter.log(' --shutdown-iters <# iters>')
+ reporter.log(' --extra-vm')
+ reporter.log(' --local-catch')
+ return rc
+
+ def parseOption(self, asArgs, iArg):
+ """
+ Extend standard options set
+ """
+ if asArgs[iArg] == '--boot-hdd':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--boot-hdd" option requires an argument')
+ self.sHddName = asArgs[iArg]
+
+ elif asArgs[iArg] == '--cpus':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpus" option requires an argument')
+ self.cCpus = int(asArgs[iArg])
+ elif asArgs[iArg] == '--no-ioapic':
+ self.fEnableIOAPIC = False
+ elif asArgs[iArg] == '--no-nested-paging':
+ self.fEnableNestedPaging = False
+ elif asArgs[iArg] == '--pae':
+ self.fEnablePAE = True
+ elif asArgs[iArg] == '--suspend-host':
+ self.fSuspendHost = True
+ elif asArgs[iArg] == '--suspend-time':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--suspend-time" option requires an argument')
+ self.cSecSuspendTime = int(asArgs[iArg])
+ elif asArgs[iArg] == '--shutdown-iters':
+ iArg += 1
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--shutdown-iters" option requires an argument')
+ self.cShutdownIters = int(asArgs[iArg])
+ elif asArgs[iArg] == '--extra-vm':
+ self.fExtraVm = True
+ elif asArgs[iArg] == '--local-catch':
+ self.fLocalCatch = True
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg)
+
+ return iArg + 1
+
+ def getResourceSet(self):
+ """
+ Returns a set of file and/or directory names relative to
+ TESTBOX_PATH_RESOURCES.
+ """
+ return [os.path.join(self.sHddPathBase, sRsrc) for sRsrc in self.kaoVmParams];
+
+ def _addVM(self, sVmName, sNicTraceFile=None):
+ """
+ Create VM
+ """
+ # Get VM params specific to HDD image
+ sKind, sController = self.kaoVmParams[self.sHddName]
+
+ # Create VM itself
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT
+ sHddPath = os.path.join(self.sHddPathBase, self.sHddName)
+ assert os.path.isfile(sHddPath)
+
+ oVM = \
+ self.createTestVM(sVmName, 1, sKind=sKind, cCpus=self.cCpus,
+ eNic0AttachType=eNic0AttachType, sDvdImage = self.sVBoxValidationKitIso)
+ assert oVM is not None
+
+ oSession = self.openSession(oVM)
+
+ # Attach an HDD
+ fRc = oSession.attachHd(sHddPath, sController, fImmutable=True)
+
+ # Enable HW virt
+ fRc = fRc and oSession.enableVirtEx(True)
+
+ # Enable I/O APIC
+ fRc = fRc and oSession.enableIoApic(self.fEnableIOAPIC)
+
+ # Enable Nested Paging
+ fRc = fRc and oSession.enableNestedPaging(self.fEnableNestedPaging)
+
+ # Enable PAE
+ fRc = fRc and oSession.enablePae(self.fEnablePAE)
+
+ if (sNicTraceFile is not None):
+ fRc = fRc and oSession.setNicTraceEnabled(True, sNicTraceFile)
+
+ # Remote desktop
+ oSession.setupVrdp(True)
+
+ fRc = fRc and oSession.saveSettings()
+ fRc = fRc and oSession.close()
+ assert fRc is True
+
+ return oVM
+
+ def actionConfig(self):
+ """
+ Configure pre-conditions.
+ """
+
+ if not self.importVBoxApi():
+ return False
+
+ # Save time: do not start VM if there is no way to suspend host
+ if (self.fSuspendHost is True and sys.platform not in self.kasSuspendAllowedPlatforms):
+ reporter.log('Platform [%s] is not in the list of supported platforms' % sys.platform)
+ return False
+
+ assert self.sHddName is not None
+ if self.sHddName not in self.kaoVmParams:
+ reporter.log('Error: unknown HDD image specified: %s' % self.sHddName)
+ return False
+
+ if (self.fExtraVm is True):
+ self.oExtraVM = self._addVM(self.sExtraVmName)
+
+ self.oVM = self._addVM(self.sVmName)
+
+ return vbox.TestDriver.actionConfig(self)
+
+ def _SuspendResume(self, cSecTimeout):
+ """
+ Put host into sleep and automatically resume it after specified timeout.
+ """
+ fRc = False
+
+ if (sys.platform == 'darwin'):
+ tsStart = time.time()
+ fRc = os.system("/usr/bin/pmset relative wake %d" % self.cSecSuspendTime)
+ fRc |= os.system("/usr/bin/pmset sleepnow")
+ # Wait for host to wake up
+ while ((time.time() - tsStart) < self.cSecSuspendTime):
+ self.sleep(0.1)
+
+ return fRc == 0
+
+ def _waitKeyboardInterrupt(self):
+ """
+ Idle loop until user press CTRL+C
+ """
+ reporter.log('[LOCAL CATCH]: waiting for keyboard interrupt')
+ while (True):
+ try:
+ self.sleep(1)
+ except KeyboardInterrupt:
+ reporter.log('[LOCAL CATCH]: keyboard interrupt occurred')
+ break
+
+ def actionExecute(self):
+ """
+ Execute the testcase itself.
+ """
+ #self.logVmInfo(self.oVM)
+
+ reporter.testStart('SHUTDOWN GUEST')
+
+ cIter = 0
+ fRc = True
+
+ if (self.fExtraVm is True):
+ oExtraSession, oExtraTxsSession = self.startVmAndConnectToTxsViaTcp(self.sExtraVmName,
+ fCdWait=False,
+ cMsTimeout=self.kcMsVmStartLimit)
+ if oExtraSession is None or oExtraTxsSession is None:
+ reporter.error('Unable to start extra VM.')
+ if (self.fLocalCatch is True):
+ self._waitKeyboardInterrupt()
+ reporter.testDone()
+ return False
+
+ while (cIter < self.cShutdownIters):
+
+ cIter += 1
+
+ reporter.log("Starting iteration #%d." % cIter)
+
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(self.sVmName,
+ fCdWait=False,
+ cMsTimeout=self.kcMsVmStartLimit)
+ if oSession is not None and oTxsSession is not None:
+ # Wait until guest reported success
+ reporter.log('Guest started. Connection to TXS service established.')
+
+ if (self.fSuspendHost is True):
+ reporter.log("Disconnect form TXS.")
+ fRc = fRc and self.txsDisconnect(oSession, oTxsSession)
+ if (fRc is not True):
+ reporter.log("Disconnect form TXS failed.")
+ else:
+ reporter.log('Put host to sleep and resume it automatically after %d seconds.' % self.cSecSuspendTime)
+ fRc = fRc and self._SuspendResume(self.cSecSuspendTime)
+ if (fRc is True):
+ reporter.log("Sleep/resume success.")
+ else:
+ reporter.log("Sleep/resume failed.")
+ reporter.log("Re-connect to TXS in 10 seconds.")
+ self.sleep(10)
+ (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, 2 * 60 * 10000)
+ if (fRc is not True):
+ reporter.log("Re-connect to TXS failed.")
+
+ if (fRc is True):
+ reporter.log('Attempt to shutdown guest.')
+ fRc = fRc and oTxsSession.syncShutdown(cMsTimeout=(4 * 60 * 1000))
+ if (fRc is True):
+ reporter.log('Shutdown request issued successfully.')
+ self.waitOnDirectSessionClose(self.oVM, self.kcMsVmShutdownLimit)
+ reporter.log('Shutdown %s.' % ('success' if fRc is True else 'failed'))
+ else:
+ reporter.error('Shutdown request failed.')
+
+ # Do not terminate failing VM in order to catch it.
+ if (fRc is not True and self.fLocalCatch is True):
+ self._waitKeyboardInterrupt()
+ break
+
+ fRc = fRc and self.terminateVmBySession(oSession)
+ reporter.log('VM terminated.')
+
+ else:
+ reporter.error('Guest did not start (iteration %d of %d)' % (cIter, self.cShutdownIters))
+ fRc = False
+
+ # Stop if fail
+ if (fRc is not True):
+ break
+
+ # Local catch at the end.
+ if (self.fLocalCatch is True):
+ reporter.log("Test completed. Waiting for user to press CTRL+C.")
+ self._waitKeyboardInterrupt()
+
+ if (self.fExtraVm is True):
+ fRc = fRc and self.terminateVmBySession(oExtraSession)
+
+ reporter.testDone()
+ return fRc is True
+
+if __name__ == '__main__':
+ sys.exit(tdGuestOsBootTest1().main(sys.argv))
diff --git a/src/VBox/ValidationKit/tests/smoketests/Makefile.kmk b/src/VBox/ValidationKit/tests/smoketests/Makefile.kmk
new file mode 100644
index 00000000..8714cee3
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/smoketests/Makefile.kmk
@@ -0,0 +1,42 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Smoke Tests.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsSmokeTests
+ValidationKitTestsSmokeTests_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsSmokeTests_INST = $(INST_VALIDATIONKIT)tests/smoketests/
+ValidationKitTestsSmokeTests_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdSmokeTest1.py \
+ $(PATH_SUB_CURRENT)/tdExoticOrAncient1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsSmokeTests_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/smoketests/tdExoticOrAncient1.py b/src/VBox/ValidationKit/tests/smoketests/tdExoticOrAncient1.py
new file mode 100755
index 00000000..4d2d717e
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/smoketests/tdExoticOrAncient1.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdExoticOrAncient1.py $
+
+"""
+VirtualBox Validation Kit - Exotic and/or ancient OSes #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import vbox;
+
+
+class tdExoticOrAncient1(vbox.TestDriver):
+ """
+ VBox exotic and/or ancient OSes #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oTestVmSet = self.oTestVmManager.selectSet( self.oTestVmManager.kfGrpAncient
+ | self.oTestVmManager.kfGrpExotic);
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+
+ def actionVerify(self):
+ if self.sVBoxValidationKitIso is None or not os.path.isfile(self.sVBoxValidationKitIso):
+ reporter.error('Cannot find the VBoxValidationKit.iso! (%s)'
+ 'Please unzip a Validation Kit build in the current directory or in some parent one.'
+ % (self.sVBoxValidationKitIso,) );
+ return False;
+ return vbox.TestDriver.actionVerify(self);
+
+ def actionConfig(self):
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ assert self.sVBoxValidationKitIso is not None;
+ return self.oTestVmSet.actionConfig(self, sDvdImage = self.sVBoxValidationKitIso);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru test #1.
+ """
+
+ # Simple test.
+ self.logVmInfo(oVM);
+ if oTestVm.fGrouping & self.oTestVmManager.kfGrpNoTxs:
+ sResult = self.runVmAndMonitorComRawFile(oTestVm.sVmName, oTestVm.sCom1RawFile);
+ return sResult == 'PASSED';
+ oSession, _ = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = True);
+ if oSession is not None:
+ return self.terminateVmBySession(oSession);
+ return False;
+
+if __name__ == '__main__':
+ sys.exit(tdExoticOrAncient1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/smoketests/tdSmokeTest1.py b/src/VBox/ValidationKit/tests/smoketests/tdSmokeTest1.py
new file mode 100755
index 00000000..c4e343d4
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/smoketests/tdSmokeTest1.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdSmokeTest1.py $
+
+"""
+VirtualBox Validation Kit - Smoke Test #1.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+
+class tdSmokeTest1(vbox.TestDriver):
+ """
+ VBox Smoke Test #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oTestVmSet = self.oTestVmManager.getSmokeVmSet();
+ self.sNicAttachmentDef = 'mixed';
+ self.sNicAttachment = self.sNicAttachmentDef;
+ self.fQuick = False;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('Smoke Test #1 options:');
+ reporter.log(' --nic-attachment <bridged|nat|mixed>');
+ reporter.log(' Default: %s' % (self.sNicAttachmentDef));
+ reporter.log(' --quick');
+ reporter.log(' Very selective testing.')
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--nic-attachment':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--nic-attachment" takes an argument');
+ self.sNicAttachment = asArgs[iArg];
+ if self.sNicAttachment not in ('bridged', 'nat', 'mixed'):
+ raise base.InvalidOption('The "--nic-attachment" value "%s" is not supported. Valid values are: bridged, nat' \
+ % (self.sNicAttachment));
+ elif asArgs[iArg] == '--quick':
+ # Disable all but a few VMs and configurations.
+ for oTestVm in self.oTestVmSet.aoTestVms:
+ if oTestVm.sVmName == 'tst-win2k3ent': # 32-bit paging
+ oTestVm.asVirtModesSup = [ 'hwvirt' ];
+ oTestVm.acCpusSup = range(1, 2);
+ elif oTestVm.sVmName == 'tst-rhel5': # 32-bit paging
+ oTestVm.asVirtModesSup = [ 'raw' ];
+ oTestVm.acCpusSup = range(1, 2);
+ elif oTestVm.sVmName == 'tst-win2k8': # 64-bit
+ oTestVm.asVirtModesSup = [ 'hwvirt-np' ];
+ oTestVm.acCpusSup = range(1, 2);
+ elif oTestVm.sVmName == 'tst-sol10-64': # SMP, 64-bit
+ oTestVm.asVirtModesSup = [ 'hwvirt' ];
+ oTestVm.acCpusSup = range(2, 3);
+ elif oTestVm.sVmName == 'tst-sol10': # SMP, 32-bit
+ oTestVm.asVirtModesSup = [ 'hwvirt-np' ];
+ oTestVm.acCpusSup = range(2, 3);
+ elif oTestVm.sVmName == 'tst-nsthwvirt-ubuntu-64': # Nested hw.virt, 64-bit
+ oTestVm.asVirtModesSup = [ 'hwvirt-np' ];
+ oTestVm.acCpusSup = range(1, 2);
+ else:
+ oTestVm.fSkip = True;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def actionVerify(self):
+ if self.sVBoxValidationKitIso is None or not os.path.isfile(self.sVBoxValidationKitIso):
+ reporter.error('Cannot find the VBoxValidationKit.iso! (%s)'
+ 'Please unzip a Validation Kit build in the current directory or in some parent one.'
+ % (self.sVBoxValidationKitIso,) );
+ return False;
+ return vbox.TestDriver.actionVerify(self);
+
+ def actionConfig(self):
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ # Do the configuring.
+ if self.sNicAttachment == 'nat': eNic0AttachType = vboxcon.NetworkAttachmentType_NAT;
+ elif self.sNicAttachment == 'bridged': eNic0AttachType = vboxcon.NetworkAttachmentType_Bridged;
+ else: eNic0AttachType = None;
+ assert self.sVBoxValidationKitIso is not None;
+ return self.oTestVmSet.actionConfig(self, eNic0AttachType = eNic0AttachType, sDvdImage = self.sVBoxValidationKitIso);
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def testOneVmConfig(self, oVM, oTestVm):
+ """
+ Runs the specified VM thru test #1.
+ """
+
+ # Simple test.
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = True);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ ## @todo do some quick tests: save, restore, execute some test program, shut down the guest.
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+ return True;
+ return None;
+
+if __name__ == '__main__':
+ sys.exit(tdSmokeTest1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/storage/Makefile.kmk b/src/VBox/ValidationKit/tests/storage/Makefile.kmk
new file mode 100644
index 00000000..92c3fef8
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/Makefile.kmk
@@ -0,0 +1,45 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Storage Tests.
+#
+
+#
+# Copyright (C) 2012-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsStorage
+ValidationKitTestsStorage_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsStorage_INST = $(INST_VALIDATIONKIT)tests/storage/
+ValidationKitTestsStorage_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdStorageBenchmark1.py \
+ $(PATH_SUB_CURRENT)/tdStorageSnapshotMerging1.py \
+ $(PATH_SUB_CURRENT)/tdStorageStress1.py \
+ $(PATH_SUB_CURRENT)/remoteexecutor.py \
+ $(PATH_SUB_CURRENT)/storagecfg.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsStorage_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/storage/remoteexecutor.py b/src/VBox/ValidationKit/tests/storage/remoteexecutor.py
new file mode 100755
index 00000000..e35c2fc7
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/remoteexecutor.py
@@ -0,0 +1,277 @@
+# -*- coding: utf-8 -*-
+# $Id: remoteexecutor.py $
+
+"""
+VirtualBox Validation Kit - Storage benchmark, test execution helpers.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2016-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import array;
+import os;
+import shutil;
+import sys;
+if sys.version_info[0] >= 3:
+ from io import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module
+else:
+ from StringIO import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module
+import subprocess;
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import reporter;
+
+
+
+class StdInOutBuffer(object):
+ """ Standard input output buffer """
+
+ def __init__(self, sInput = None):
+ self.sInput = StringIO();
+ if sInput is not None:
+ self.sInput.write(self._toString(sInput));
+ self.sInput.seek(0);
+ self.sOutput = '';
+
+ def _toString(self, sText):
+ """
+ Converts any possible array to
+ a string.
+ """
+ if isinstance(sText, array.array):
+ try:
+ return sText.tostring();
+ except:
+ pass;
+ else:
+ return sText;
+
+ def read(self, cb):
+ """file.read"""
+ return self.sInput.read(cb);
+
+ def write(self, sText):
+ """file.write"""
+ self.sOutput += self._toString(sText);
+ return None;
+
+ def getOutput(self):
+ """
+ Returns the output of the buffer.
+ """
+ return self.sOutput;
+
+ def close(self):
+ """ file.close """
+ return;
+
+class RemoteExecutor(object):
+ """
+ Helper for executing tests remotely through TXS or locally
+ """
+
+ def __init__(self, oTxsSession = None, asBinaryPaths = None, sScratchPath = None):
+ self.oTxsSession = oTxsSession;
+ self.asPaths = asBinaryPaths;
+ self.sScratchPath = sScratchPath;
+ if self.asPaths is None:
+ self.asPaths = [ ];
+
+ def _isFile(self, sFile):
+ """
+ Checks whether a file exists.
+ """
+ if self.oTxsSession is not None:
+ return self.oTxsSession.syncIsFile(sFile);
+ return os.path.isfile(sFile);
+
+ def _getBinaryPath(self, sBinary):
+ """
+ Returns the complete path of the given binary if found
+ from the configured search path or None if not found.
+ """
+ for sPath in self.asPaths:
+ sFile = sPath + '/' + sBinary;
+ if self._isFile(sFile):
+ return sFile;
+ return None;
+
+ def _sudoExecuteSync(self, asArgs, sInput):
+ """
+ Executes a sudo child process synchronously.
+ Returns a tuple [True, 0] if the process executed successfully
+ and returned 0, otherwise [False, rc] is returned.
+ """
+ reporter.log('Executing [sudo]: %s' % (asArgs, ));
+ reporter.flushall();
+ fRc = True;
+ sOutput = '';
+ sError = '';
+ try:
+ oProcess = utils.sudoProcessPopen(asArgs, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
+ stderr=subprocess.PIPE, shell = False, close_fds = False);
+
+ sOutput, sError = oProcess.communicate(sInput);
+ iExitCode = oProcess.poll();
+
+ if iExitCode is not 0:
+ fRc = False;
+ except:
+ reporter.errorXcpt();
+ fRc = False;
+ reporter.log('Exit code [sudo]: %s (%s)' % (fRc, asArgs));
+ return (fRc, str(sOutput), str(sError));
+
+ def _execLocallyOrThroughTxs(self, sExec, asArgs, sInput, cMsTimeout):
+ """
+ Executes the given program locally or through TXS based on the
+ current config.
+ """
+ fRc = False;
+ sOutput = None;
+ if self.oTxsSession is not None:
+ reporter.log('Executing [remote]: %s %s %s' % (sExec, asArgs, sInput));
+ reporter.flushall();
+ oStdOut = StdInOutBuffer();
+ oStdErr = StdInOutBuffer();
+ oStdIn = None;
+ if sInput is not None:
+ oStdIn = StdInOutBuffer(sInput);
+ else:
+ oStdIn = '/dev/null'; # pylint: disable=R0204
+ fRc = self.oTxsSession.syncExecEx(sExec, (sExec,) + asArgs,
+ oStdIn = oStdIn, oStdOut = oStdOut,
+ oStdErr = oStdErr, cMsTimeout = cMsTimeout);
+ sOutput = oStdOut.getOutput();
+ sError = oStdErr.getOutput();
+ if fRc is False:
+ reporter.log('Exit code [remote]: %s (stdout: %s stderr: %s)' % (fRc, sOutput, sError));
+ else:
+ reporter.log('Exit code [remote]: %s' % (fRc,));
+ else:
+ fRc, sOutput, sError = self._sudoExecuteSync([sExec, ] + list(asArgs), sInput);
+ return (fRc, sOutput, sError);
+
+ def execBinary(self, sExec, asArgs, sInput = None, cMsTimeout = 3600000):
+ """
+ Executes the given binary with the given arguments
+ providing some optional input through stdin and
+ returning whether the process exited successfully and the output
+ in a string.
+ """
+
+ fRc = True;
+ sOutput = None;
+ sError = None;
+ sBinary = self._getBinaryPath(sExec);
+ if sBinary is not None:
+ fRc, sOutput, sError = self._execLocallyOrThroughTxs(sBinary, asArgs, sInput, cMsTimeout);
+ else:
+ fRc = False;
+ return (fRc, sOutput, sError);
+
+ def execBinaryNoStdOut(self, sExec, asArgs, sInput = None):
+ """
+ Executes the given binary with the given arguments
+ providing some optional input through stdin and
+ returning whether the process exited successfully.
+ """
+ fRc, _, _ = self.execBinary(sExec, asArgs, sInput);
+ return fRc;
+
+ def copyFile(self, sLocalFile, sFilename, cMsTimeout = 30000):
+ """
+ Copies the local file to the remote destination
+ if configured
+
+ Returns a file ID which can be used as an input parameter
+ to execBinary() resolving to the real filepath on the remote side
+ or locally.
+ """
+ sFileId = None;
+ if self.oTxsSession is not None:
+ sFileId = '${SCRATCH}/' + sFilename;
+ fRc = self.oTxsSession.syncUploadFile(sLocalFile, sFileId, cMsTimeout);
+ if not fRc:
+ sFileId = None;
+ else:
+ sFileId = self.sScratchPath + '/' + sFilename;
+ try:
+ shutil.copy(sLocalFile, sFileId);
+ except:
+ sFileId = None;
+
+ return sFileId;
+
+ def copyString(self, sContent, sFilename, cMsTimeout = 30000):
+ """
+ Creates a file remotely or locally with the given content.
+
+ Returns a file ID which can be used as an input parameter
+ to execBinary() resolving to the real filepath on the remote side
+ or locally.
+ """
+ sFileId = None;
+ if self.oTxsSession is not None:
+ sFileId = '${SCRATCH}/' + sFilename;
+ fRc = self.oTxsSession.syncUploadString(sContent, sFileId, cMsTimeout);
+ if not fRc:
+ sFileId = None;
+ else:
+ sFileId = self.sScratchPath + '/' + sFilename;
+ try:
+ oFile = open(sFileId, 'wb');
+ oFile.write(sContent);
+ oFile.close();
+ except:
+ sFileId = None;
+
+ return sFileId;
+
+ def mkDir(self, sDir, fMode = 0o700, cMsTimeout = 30000):
+ """
+ Creates a new directory at the given location.
+ """
+ fRc = True;
+ if self.oTxsSession is not None:
+ fRc = self.oTxsSession.syncMkDir(sDir, fMode, cMsTimeout);
+ else:
+ fRc = self.execBinaryNoStdOut('mkdir', ('-m', format(fMode, 'o'), sDir));
+
+ return fRc;
+
+ def rmDir(self, sDir, cMsTimeout = 30000):
+ """
+ Removes the given directory.
+ """
+ fRc = True;
+ if self.oTxsSession is not None:
+ fRc = self.oTxsSession.syncRmDir(sDir, cMsTimeout);
+ else:
+ fRc = self.execBinaryNoStdOut('rmdir', (sDir,));
+
+ return fRc;
+
diff --git a/src/VBox/ValidationKit/tests/storage/storagecfg.py b/src/VBox/ValidationKit/tests/storage/storagecfg.py
new file mode 100755
index 00000000..e97f57b0
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/storagecfg.py
@@ -0,0 +1,608 @@
+# -*- coding: utf-8 -*-
+# $Id: storagecfg.py $
+
+"""
+VirtualBox Validation Kit - Storage test configuration API.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2016-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+# Standard Python imports.
+import os;
+import re;
+
+# Validation Kit imports.
+from common import utils;
+
+
+class StorageDisk(object):
+ """
+ Class representing a disk for testing.
+ """
+
+ def __init__(self, sPath, fRamDisk = False):
+ self.sPath = sPath;
+ self.fUsed = False;
+ self.fRamDisk = fRamDisk;
+
+ def getPath(self):
+ """
+ Return the disk path.
+ """
+ return self.sPath;
+
+ def isUsed(self):
+ """
+ Returns whether the disk is currently in use.
+ """
+ return self.fUsed;
+
+ def isRamDisk(self):
+ """
+ Returns whether the disk objecthas a RAM backing.
+ """
+ return self.fRamDisk;
+
+ def setUsed(self, fUsed):
+ """
+ Sets the used flag for the disk.
+ """
+ if fUsed:
+ if self.fUsed:
+ return False;
+
+ self.fUsed = True;
+ else:
+ self.fUsed = fUsed;
+
+ return True;
+
+class StorageConfigOs(object):
+ """
+ Base class for a single hosts OS storage configuration.
+ """
+
+ def _getDisksMatchingRegExpWithPath(self, sPath, sRegExp):
+ """
+ Adds new disks to the config matching the given regular expression.
+ """
+
+ lstDisks = [];
+ oRegExp = re.compile(sRegExp);
+ asFiles = os.listdir(sPath);
+ for sFile in asFiles:
+ if oRegExp.match(os.path.basename(sFile)) and os.path.exists(sPath + '/' + sFile):
+ lstDisks.append(StorageDisk(sPath + '/' + sFile));
+
+ return lstDisks;
+
+class StorageConfigOsSolaris(StorageConfigOs):
+ """
+ Class implementing the Solaris specifics for a storage configuration.
+ """
+
+ def __init__(self):
+ StorageConfigOs.__init__(self);
+ self.idxRamDisk = 0;
+
+ def _getActivePoolsStartingWith(self, oExec, sPoolIdStart):
+ """
+ Returns a list of pools starting with the given ID or None on failure.
+ """
+ lstPools = None;
+ fRc, sOutput, _ = oExec.execBinary('zpool', ('list', '-H'));
+ if fRc:
+ lstPools = [];
+ asPools = sOutput.splitlines();
+ for sPool in asPools:
+ if sPool.startswith(sPoolIdStart):
+ # Extract the whole name and add it to the list.
+ asItems = sPool.split('\t');
+ lstPools.append(asItems[0]);
+ return lstPools;
+
+ def _getActiveVolumesInPoolStartingWith(self, oExec, sPool, sVolumeIdStart):
+ """
+ Returns a list of active volumes for the given pool starting with the given
+ identifier or None on failure.
+ """
+ lstVolumes = None;
+ fRc, sOutput, _ = oExec.execBinary('zfs', ('list', '-H'));
+ if fRc:
+ lstVolumes = [];
+ asVolumes = sOutput.splitlines();
+ for sVolume in asVolumes:
+ if sVolume.startswith(sPool + '/' + sVolumeIdStart):
+ # Extract the whole name and add it to the list.
+ asItems = sVolume.split('\t');
+ lstVolumes.append(asItems[0]);
+ return lstVolumes;
+
+ def getDisksMatchingRegExp(self, sRegExp):
+ """
+ Returns a list of disks matching the regular expression.
+ """
+ return self._getDisksMatchingRegExpWithPath('/dev/dsk', sRegExp);
+
+ def getMntBase(self):
+ """
+ Returns the mountpoint base for the host.
+ """
+ return '/pools';
+
+ def createStoragePool(self, oExec, sPool, asDisks, sRaidLvl):
+ """
+ Creates a new storage pool with the given disks and the given RAID level.
+ """
+ sZPoolRaid = None;
+ if len(asDisks) > 1 and (sRaidLvl == 'raid5' or sRaidLvl is None):
+ sZPoolRaid = 'raidz';
+
+ fRc = True;
+ if sZPoolRaid is not None:
+ fRc = oExec.execBinaryNoStdOut('zpool', ('create', '-f', sPool, sZPoolRaid,) + tuple(asDisks));
+ else:
+ fRc = oExec.execBinaryNoStdOut('zpool', ('create', '-f', sPool,) + tuple(asDisks));
+
+ return fRc;
+
+ def createVolume(self, oExec, sPool, sVol, sMountPoint, cbVol = None):
+ """
+ Creates and mounts a filesystem at the given mountpoint using the
+ given pool and volume IDs.
+ """
+ fRc = True;
+ if cbVol is not None:
+ fRc = oExec.execBinaryNoStdOut('zfs', ('create', '-o', 'mountpoint='+sMountPoint, '-V', cbVol, sPool + '/' + sVol));
+ else:
+ fRc = oExec.execBinaryNoStdOut('zfs', ('create', '-o', 'mountpoint='+sMountPoint, sPool + '/' + sVol));
+
+ return fRc;
+
+ def destroyVolume(self, oExec, sPool, sVol):
+ """
+ Destroys the given volume.
+ """
+ fRc = oExec.execBinaryNoStdOut('zfs', ('destroy', sPool + '/' + sVol));
+ return fRc;
+
+ def destroyPool(self, oExec, sPool):
+ """
+ Destroys the given storage pool.
+ """
+ fRc = oExec.execBinaryNoStdOut('zpool', ('destroy', sPool));
+ return fRc;
+
+ def cleanupPoolsAndVolumes(self, oExec, sPoolIdStart, sVolIdStart):
+ """
+ Cleans up any pools and volumes starting with the name in the given
+ parameters.
+ """
+ fRc = True;
+ lstPools = self._getActivePoolsStartingWith(oExec, sPoolIdStart);
+ if lstPools is not None:
+ for sPool in lstPools:
+ lstVolumes = self._getActiveVolumesInPoolStartingWith(oExec, sPool, sVolIdStart);
+ if lstVolumes is not None:
+ # Destroy all the volumes first
+ for sVolume in lstVolumes:
+ fRc2 = oExec.execBinaryNoStdOut('zfs', ('destroy', sVolume));
+ if not fRc2:
+ fRc = fRc2;
+
+ # Destroy the pool
+ fRc2 = self.destroyPool(oExec, sPool);
+ if not fRc2:
+ fRc = fRc2;
+ else:
+ fRc = False;
+ else:
+ fRc = False;
+
+ return fRc;
+
+ def createRamDisk(self, oExec, cbRamDisk):
+ """
+ Creates a RAM backed disk with the given size.
+ """
+ oDisk = None;
+ sRamDiskName = 'ramdisk%u' % (self.idxRamDisk,);
+ fRc, _ , _ = oExec.execBinary('ramdiskadm', ('-a', sRamDiskName, str(cbRamDisk)));
+ if fRc:
+ self.idxRamDisk += 1;
+ oDisk = StorageDisk('/dev/ramdisk/%s' % (sRamDiskName, ), True);
+
+ return oDisk;
+
+ def destroyRamDisk(self, oExec, oDisk):
+ """
+ Destroys the given ramdisk object.
+ """
+ sRamDiskName = os.path.basename(oDisk.getPath());
+ return oExec.execBinaryNoStdOut('ramdiskadm', ('-d', sRamDiskName));
+
+class StorageConfigOsLinux(StorageConfigOs):
+ """
+ Class implementing the Linux specifics for a storage configuration.
+ """
+
+ def __init__(self):
+ StorageConfigOs.__init__(self);
+ self.dSimplePools = { }; # Simple storage pools which don't use lvm (just one partition)
+ self.dMounts = { }; # Pool/Volume to mountpoint mapping.
+
+ def _getDmRaidLevelFromLvl(self, sRaidLvl):
+ """
+ Converts our raid level indicators to something mdadm can understand.
+ """
+ if sRaidLvl == 'raid5':
+ return '5';
+ elif sRaidLvl == 'raid1':
+ return 'mirror';
+ elif sRaidLvl == 'raid0' or sRaidLvl is None:
+ return 'stripe';
+
+ return 'stripe';
+
+ def getDisksMatchingRegExp(self, sRegExp):
+ """
+ Returns a list of disks matching the regular expression.
+ """
+ return self._getDisksMatchingRegExpWithPath('/dev/', sRegExp);
+
+ def getMntBase(self):
+ """
+ Returns the mountpoint base for the host.
+ """
+ return '/mnt';
+
+ def createStoragePool(self, oExec, sPool, asDisks, sRaidLvl):
+ """
+ Creates a new storage pool with the given disks and the given RAID level.
+ """
+ fRc = True;
+ if len(asDisks) == 1 and sRaidLvl is None:
+ # Doesn't require LVM, put into the simple pools dictionary so we can
+ # use it when creating a volume later.
+ self.dSimplePools[sPool] = asDisks[0];
+ else:
+ # If a RAID is required use dm-raid first to create one.
+ asLvmPvDisks = asDisks;
+ fRc = oExec.execBinaryNoStdOut('mdadm', ('--create', '/dev/md0', '--assume-clean',
+ '--level=' + self._getDmRaidLevelFromLvl(sRaidLvl),
+ '--raid-devices=' + str(len(asDisks))) + tuple(asDisks));
+ if fRc:
+ # /dev/md0 is the only block device to use for our volume group.
+ asLvmPvDisks = [ '/dev/md0' ];
+
+ # Create a physical volume on every disk first.
+ for sLvmPvDisk in asLvmPvDisks:
+ fRc = oExec.execBinaryNoStdOut('pvcreate', (sLvmPvDisk, ));
+ if not fRc:
+ break;
+
+ if fRc:
+ # Create volume group with all physical volumes included
+ fRc = oExec.execBinaryNoStdOut('vgcreate', (sPool, ) + tuple(asLvmPvDisks));
+ return fRc;
+
+ def createVolume(self, oExec, sPool, sVol, sMountPoint, cbVol = None):
+ """
+ Creates and mounts a filesystem at the given mountpoint using the
+ given pool and volume IDs.
+ """
+ fRc = True;
+ sBlkDev = None;
+ if sPool in self.dSimplePools:
+ sDiskPath = self.dSimplePools.get(sPool);
+ if sDiskPath.find('zram') != -1:
+ sBlkDev = sDiskPath;
+ else:
+ # Create a partition with the requested size
+ sFdiskScript = ';\n'; # Single partition filling everything
+ if cbVol is not None:
+ sFdiskScript = ',' + str(cbVol // 512) + '\n'; # Get number of sectors
+ fRc = oExec.execBinaryNoStdOut('sfdisk', ('--no-reread', '--wipe', 'always', '-q', '-f', sDiskPath), \
+ sFdiskScript);
+ if fRc:
+ if sDiskPath.find('nvme') != -1:
+ sBlkDev = sDiskPath + 'p1';
+ else:
+ sBlkDev = sDiskPath + '1';
+ else:
+ if cbVol is None:
+ fRc = oExec.execBinaryNoStdOut('lvcreate', ('-l', '100%FREE', '-n', sVol, sPool));
+ else:
+ fRc = oExec.execBinaryNoStdOut('lvcreate', ('-L', str(cbVol), '-n', sVol, sPool));
+ if fRc:
+ sBlkDev = '/dev/mapper' + sPool + '-' + sVol;
+
+ if fRc is True and sBlkDev is not None:
+ # Create a filesystem and mount it
+ fRc = oExec.execBinaryNoStdOut('mkfs.ext4', ('-F', '-F', sBlkDev,));
+ fRc = fRc and oExec.mkDir(sMountPoint);
+ fRc = fRc and oExec.execBinaryNoStdOut('mount', (sBlkDev, sMountPoint));
+ if fRc:
+ self.dMounts[sPool + '/' + sVol] = sMountPoint;
+ return fRc;
+
+ def destroyVolume(self, oExec, sPool, sVol):
+ """
+ Destroys the given volume.
+ """
+ # Unmount first
+ sMountPoint = self.dMounts[sPool + '/' + sVol];
+ fRc = oExec.execBinaryNoStdOut('umount', (sMountPoint,));
+ self.dMounts.pop(sPool + '/' + sVol);
+ oExec.rmDir(sMountPoint);
+ if sPool in self.dSimplePools:
+ # Wipe partition table
+ sDiskPath = self.dSimplePools.get(sPool);
+ if sDiskPath.find('zram') == -1:
+ fRc = oExec.execBinaryNoStdOut('sfdisk', ('--no-reread', '--wipe', 'always', '-q', '-f', '--delete', \
+ sDiskPath));
+ else:
+ fRc = oExec.execBinaryNoStdOut('lvremove', (sPool + '/' + sVol,));
+ return fRc;
+
+ def destroyPool(self, oExec, sPool):
+ """
+ Destroys the given storage pool.
+ """
+ fRc = True;
+ if sPool in self.dSimplePools:
+ self.dSimplePools.pop(sPool);
+ else:
+ fRc = oExec.execBinaryNoStdOut('vgremove', (sPool,));
+ return fRc;
+
+ def cleanupPoolsAndVolumes(self, oExec, sPoolIdStart, sVolIdStart):
+ """
+ Cleans up any pools and volumes starting with the name in the given
+ parameters.
+ """
+ # @todo: Needs implementation, for LVM based configs a similar approach can be used
+ # as for Solaris.
+ _ = oExec;
+ _ = sPoolIdStart;
+ _ = sVolIdStart;
+ return True;
+
+ def createRamDisk(self, oExec, cbRamDisk):
+ """
+ Creates a RAM backed disk with the given size.
+ """
+ # Make sure the ZRAM module is loaded.
+ oDisk = None;
+ fRc = oExec.execBinaryNoStdOut('modprobe', ('zram',));
+ if fRc:
+ fRc, sOut, _ = oExec.execBinary('zramctl', ('--raw', '-f', '-s', str(cbRamDisk)));
+ if fRc:
+ oDisk = StorageDisk(sOut.rstrip(), True);
+
+ return oDisk;
+
+ def destroyRamDisk(self, oExec, oDisk):
+ """
+ Destroys the given ramdisk object.
+ """
+ return oExec.execBinaryNoStdOut('zramctl', ('-r', oDisk.getPath()));
+
+class StorageCfg(object):
+ """
+ Storage configuration helper class taking care of the different host OS.
+ """
+
+ def __init__(self, oExec, sTargetOs, oDiskCfg):
+ self.oExec = oExec;
+ self.lstDisks = [ ]; # List of disks present in the system.
+ self.dPools = { }; # Dictionary of storage pools.
+ self.dVols = { }; # Dictionary of volumes.
+ self.iPoolId = 0;
+ self.iVolId = 0;
+
+ fRc = True;
+ oStorOs = None;
+ if sTargetOs == 'solaris':
+ oStorOs = StorageConfigOsSolaris();
+ elif sTargetOs == 'linux':
+ oStorOs = StorageConfigOsLinux(); # pylint: disable=R0204
+ else:
+ fRc = False;
+
+ if fRc:
+ self.oStorOs = oStorOs;
+ if utils.isString(oDiskCfg):
+ self.lstDisks = oStorOs.getDisksMatchingRegExp(oDiskCfg);
+ else:
+ # Assume a list of of disks and add.
+ for sDisk in oDiskCfg:
+ self.lstDisks.append(StorageDisk(sDisk));
+
+ def __del__(self):
+ self.cleanup();
+
+ def cleanup(self):
+ """
+ Cleans up any created storage configs.
+ """
+
+ # Destroy all volumes first.
+ for sMountPoint in self.dVols.keys(): # pylint: disable=C0201
+ self.destroyVolume(sMountPoint);
+
+ # Destroy all pools.
+ for sPool in self.dPools.keys(): # pylint: disable=C0201
+ self.destroyStoragePool(sPool);
+
+ self.dVols.clear();
+ self.dPools.clear();
+ self.iPoolId = 0;
+ self.iVolId = 0;
+
+ def getRawDisk(self):
+ """
+ Returns a raw disk device from the list of free devices for use.
+ """
+ for oDisk in self.lstDisks:
+ if oDisk.isUsed() is False:
+ oDisk.setUsed(True);
+ return oDisk.getPath();
+
+ return None;
+
+ def getUnusedDiskCount(self):
+ """
+ Returns the number of unused disks.
+ """
+
+ cDisksUnused = 0;
+ for oDisk in self.lstDisks:
+ if not oDisk.isUsed():
+ cDisksUnused += 1;
+
+ return cDisksUnused;
+
+ def createStoragePool(self, cDisks = 0, sRaidLvl = None,
+ cbPool = None, fRamDisk = False):
+ """
+ Create a new storage pool
+ """
+ lstDisks = [ ];
+ fRc = True;
+ sPool = None;
+
+ if fRamDisk:
+ oDisk = self.oStorOs.createRamDisk(self.oExec, cbPool);
+ if oDisk is not None:
+ lstDisks.append(oDisk);
+ cDisks = 1;
+ else:
+ if cDisks == 0:
+ cDisks = self.getUnusedDiskCount();
+
+ for oDisk in self.lstDisks:
+ if not oDisk.isUsed():
+ oDisk.setUsed(True);
+ lstDisks.append(oDisk);
+ if len(lstDisks) == cDisks:
+ break;
+
+ # Enough drives to satisfy the request?
+ if len(lstDisks) == cDisks:
+ # Create a list of all device paths
+ lstDiskPaths = [ ];
+ for oDisk in lstDisks:
+ lstDiskPaths.append(oDisk.getPath());
+
+ # Find a name for the pool
+ sPool = 'pool' + str(self.iPoolId);
+ self.iPoolId += 1;
+
+ fRc = self.oStorOs.createStoragePool(self.oExec, sPool, lstDiskPaths, sRaidLvl);
+ if fRc:
+ self.dPools[sPool] = lstDisks;
+ else:
+ self.iPoolId -= 1;
+ else:
+ fRc = False;
+
+ # Cleanup in case of error.
+ if not fRc:
+ for oDisk in lstDisks:
+ oDisk.setUsed(False);
+ if oDisk.isRamDisk():
+ self.oStorOs.destroyRamDisk(self.oExec, oDisk);
+
+ return fRc, sPool;
+
+ def destroyStoragePool(self, sPool):
+ """
+ Destroys the storage pool with the given ID.
+ """
+
+ lstDisks = self.dPools.get(sPool);
+ if lstDisks is not None:
+ fRc = self.oStorOs.destroyPool(self.oExec, sPool);
+ if fRc:
+ # Mark disks as unused
+ self.dPools.pop(sPool);
+ for oDisk in lstDisks:
+ oDisk.setUsed(False);
+ if oDisk.isRamDisk():
+ self.oStorOs.destroyRamDisk(self.oExec, oDisk);
+ else:
+ fRc = False;
+
+ return fRc;
+
+ def createVolume(self, sPool, cbVol = None):
+ """
+ Creates a new volume from the given pool returning the mountpoint.
+ """
+
+ fRc = True;
+ sMountPoint = None;
+ if sPool in self.dPools:
+ sVol = 'vol' + str(self.iVolId);
+ sMountPoint = self.oStorOs.getMntBase() + '/' + sVol;
+ self.iVolId += 1;
+ fRc = self.oStorOs.createVolume(self.oExec, sPool, sVol, sMountPoint, cbVol);
+ if fRc:
+ self.dVols[sMountPoint] = (sVol, sPool);
+ else:
+ self.iVolId -= 1;
+ else:
+ fRc = False;
+
+ return fRc, sMountPoint;
+
+ def destroyVolume(self, sMountPoint):
+ """
+ Destroy the volume at the given mount point.
+ """
+
+ sVol, sPool = self.dVols.get(sMountPoint);
+ fRc = True;
+ if sVol is not None:
+ fRc = self.oStorOs.destroyVolume(self.oExec, sPool, sVol);
+ if fRc:
+ self.dVols.pop(sMountPoint);
+ else:
+ fRc = False;
+
+ return fRc;
+
+ def mkDirOnVolume(self, sMountPoint, sDir, fMode = 0o700):
+ """
+ Creates a new directory on the volume pointed to by the given mount point.
+ """
+ return self.oExec.mkDir(sMountPoint + '/' + sDir, fMode);
+
+ def cleanupLeftovers(self):
+ """
+ Tries to cleanup any leftover pools and volumes from a failed previous run.
+ """
+ return self.oStorOs.cleanupPoolsAndVolumes(self.oExec, 'pool', 'vol');
+
diff --git a/src/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py b/src/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py
new file mode 100755
index 00000000..a9dfaa99
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py
@@ -0,0 +1,1350 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdStorageBenchmark1.py $
+
+"""
+VirtualBox Validation Kit - Storage benchmark.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import socket;
+import sys;
+if sys.version_info[0] >= 3:
+ from io import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module
+else:
+ from StringIO import StringIO as StringIO; # pylint: disable=import-error,no-name-in-module
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from common import constants;
+from common import utils;
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+from testdriver import vboxwrappers;
+
+import remoteexecutor;
+import storagecfg;
+
+
+def _ControllerTypeToName(eControllerType):
+ """ Translate a controller type to a name. """
+ if eControllerType == vboxcon.StorageControllerType_PIIX3 or eControllerType == vboxcon.StorageControllerType_PIIX4:
+ sType = "IDE Controller";
+ elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
+ sType = "SATA Controller";
+ elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
+ sType = "SAS Controller";
+ elif eControllerType == vboxcon.StorageControllerType_LsiLogic or eControllerType == vboxcon.StorageControllerType_BusLogic:
+ sType = "SCSI Controller";
+ elif eControllerType == vboxcon.StorageControllerType_NVMe:
+ sType = "NVMe Controller";
+ else:
+ sType = "Storage Controller";
+ return sType;
+
+class FioTest(object):
+ """
+ Flexible I/O tester testcase.
+ """
+
+ kdHostIoEngine = {
+ 'solaris': ('solarisaio', False),
+ 'linux': ('libaio', True)
+ };
+
+ def __init__(self, oExecutor, dCfg = None):
+ self.oExecutor = oExecutor;
+ self.sCfgFileId = None;
+ self.dCfg = dCfg;
+ self.sError = None;
+ self.sResult = None;
+
+ def prepare(self, cMsTimeout = 30000):
+ """ Prepares the testcase """
+
+ sTargetOs = self.dCfg.get('TargetOs', 'linux');
+ sIoEngine, fDirectIo = self.kdHostIoEngine.get(sTargetOs);
+ if sIoEngine is None:
+ return False;
+
+ cfgBuf = StringIO();
+ cfgBuf.write('[global]\n');
+ cfgBuf.write('bs=' + self.dCfg.get('RecordSize', '4k') + '\n');
+ cfgBuf.write('ioengine=' + sIoEngine + '\n');
+ cfgBuf.write('iodepth=' + self.dCfg.get('QueueDepth', '32') + '\n');
+ cfgBuf.write('size=' + self.dCfg.get('TestsetSize', '2g') + '\n');
+ if fDirectIo:
+ cfgBuf.write('direct=1\n');
+ else:
+ cfgBuf.write('direct=0\n');
+ cfgBuf.write('directory=' + self.dCfg.get('FilePath', '/mnt') + '\n');
+ cfgBuf.write('filename=fio.test.file');
+
+ cfgBuf.write('[seq-write]\n');
+ cfgBuf.write('rw=write\n');
+ cfgBuf.write('stonewall\n');
+
+ cfgBuf.write('[rand-write]\n');
+ cfgBuf.write('rw=randwrite\n');
+ cfgBuf.write('stonewall\n');
+
+ cfgBuf.write('[seq-read]\n');
+ cfgBuf.write('rw=read\n');
+ cfgBuf.write('stonewall\n');
+
+ cfgBuf.write('[rand-read]\n');
+ cfgBuf.write('rw=randread\n');
+ cfgBuf.write('stonewall\n');
+
+ self.sCfgFileId = self.oExecutor.copyString(cfgBuf.getvalue(), 'aio-test', cMsTimeout);
+ return self.sCfgFileId is not None;
+
+ def run(self, cMsTimeout = 30000):
+ """ Runs the testcase """
+ _ = cMsTimeout
+ fRc, sOutput, sError = self.oExecutor.execBinary('fio', (self.sCfgFileId,), cMsTimeout = cMsTimeout);
+ if fRc:
+ self.sResult = sOutput;
+ else:
+ self.sError = ('Binary: fio\n' +
+ '\nOutput:\n\n' +
+ sOutput +
+ '\nError:\n\n' +
+ sError);
+ return fRc;
+
+ def cleanup(self):
+ """ Cleans up any leftovers from the testcase. """
+
+ def reportResult(self):
+ """
+ Reports the test results to the test manager.
+ """
+ return True;
+
+ def getErrorReport(self):
+ """
+ Returns the error report in case the testcase failed.
+ """
+ return self.sError;
+
+class IozoneTest(object):
+ """
+ I/O zone testcase.
+ """
+ def __init__(self, oExecutor, dCfg = None):
+ self.oExecutor = oExecutor;
+ self.sResult = None;
+ self.sError = None;
+ self.lstTests = [ ('initial writers', 'FirstWrite'),
+ ('rewriters', 'Rewrite'),
+ ('re-readers', 'ReRead'),
+ ('stride readers', 'StrideRead'),
+ ('reverse readers', 'ReverseRead'),
+ ('random readers', 'RandomRead'),
+ ('mixed workload', 'MixedWorkload'),
+ ('random writers', 'RandomWrite'),
+ ('pwrite writers', 'PWrite'),
+ ('pread readers', 'PRead'),
+ ('fwriters', 'FWrite'),
+ ('freaders', 'FRead'),
+ ('readers', 'FirstRead')];
+ self.sRecordSize = dCfg.get('RecordSize', '4k');
+ self.sTestsetSize = dCfg.get('TestsetSize', '2g');
+ self.sQueueDepth = dCfg.get('QueueDepth', '32');
+ self.sFilePath = dCfg.get('FilePath', '/mnt/iozone');
+ self.fDirectIo = True;
+
+ sTargetOs = dCfg.get('TargetOs');
+ if sTargetOs == 'solaris':
+ self.fDirectIo = False;
+
+ def prepare(self, cMsTimeout = 30000):
+ """ Prepares the testcase """
+ _ = cMsTimeout;
+ return True; # Nothing to do.
+
+ def run(self, cMsTimeout = 30000):
+ """ Runs the testcase """
+ tupArgs = ('-r', self.sRecordSize, '-s', self.sTestsetSize, \
+ '-t', '1', '-T', '-F', self.sFilePath + '/iozone.tmp');
+ if self.fDirectIo:
+ tupArgs += ('-I',);
+ fRc, sOutput, sError = self.oExecutor.execBinary('iozone', tupArgs, cMsTimeout = cMsTimeout);
+ if fRc:
+ self.sResult = sOutput;
+ else:
+ self.sError = ('Binary: iozone\n' +
+ '\nOutput:\n\n' +
+ sOutput +
+ '\nError:\n\n' +
+ sError);
+
+ _ = cMsTimeout;
+ return fRc;
+
+ def cleanup(self):
+ """ Cleans up any leftovers from the testcase. """
+ return True;
+
+ def reportResult(self):
+ """
+ Reports the test results to the test manager.
+ """
+
+ fRc = True;
+ if self.sResult is not None:
+ try:
+ asLines = self.sResult.splitlines();
+ for sLine in asLines:
+ sLine = sLine.strip();
+ if sLine.startswith('Children') is True:
+ # Extract the value
+ idxValue = sLine.rfind('=');
+ if idxValue == -1:
+ raise Exception('IozoneTest: Invalid state');
+
+ idxValue += 1;
+ while sLine[idxValue] == ' ':
+ idxValue += 1;
+
+ # Get the reported value, cut off after the decimal point
+ # it is not supported by the testmanager yet and is not really
+ # relevant anyway.
+ idxValueEnd = idxValue;
+ while sLine[idxValueEnd].isdigit():
+ idxValueEnd += 1;
+
+ for sNeedle, sTestVal in self.lstTests:
+ if sLine.rfind(sNeedle) != -1:
+ reporter.testValue(sTestVal, sLine[idxValue:idxValueEnd],
+ constants.valueunit.g_asNames[constants.valueunit.KILOBYTES_PER_SEC]);
+ break;
+ except:
+ fRc = False;
+ else:
+ fRc = False;
+
+ return fRc;
+
+ def getErrorReport(self):
+ """
+ Returns the error report in case the testcase failed.
+ """
+ return self.sError;
+
+class StorTestCfgMgr(object):
+ """
+ Manages the different testcases.
+ """
+
+ def __init__(self, aasTestLvls, aasTestsBlacklist, fnIsCfgSupported = None):
+ self.aasTestsBlacklist = aasTestsBlacklist;
+ self.at3TestLvls = [];
+ self.iTestLvl = 0;
+ self.fnIsCfgSupported = fnIsCfgSupported;
+ for asTestLvl in aasTestLvls:
+ if isinstance(asTestLvl, tuple):
+ asTestLvl, fnTestFmt = asTestLvl;
+ self.at3TestLvls.append((0, fnTestFmt, asTestLvl));
+ else:
+ self.at3TestLvls.append((0, None, asTestLvl));
+
+ self.at3TestLvls.reverse();
+
+ # Get the first non blacklisted test.
+ asTestCfg = self.getCurrentTestCfg();
+ while asTestCfg and self.isTestCfgBlacklisted(asTestCfg):
+ asTestCfg = self.advanceTestCfg();
+
+ iLvl = 0;
+ for sCfg in asTestCfg:
+ reporter.testStart('%s' % (self.getTestIdString(sCfg, iLvl)));
+ iLvl += 1;
+
+ def __del__(self):
+ # Make sure the tests are marked as done.
+ while self.iTestLvl < len(self.at3TestLvls):
+ reporter.testDone();
+ self.iTestLvl += 1;
+
+ def getTestIdString(self, oCfg, iLvl):
+ """
+ Returns a potentially formatted string for the test name.
+ """
+
+ # The order of the test levels is reversed so get the level starting
+ # from the end.
+ _, fnTestFmt, _ = self.at3TestLvls[len(self.at3TestLvls) - 1 - iLvl];
+ if fnTestFmt is not None:
+ return fnTestFmt(oCfg);
+ return oCfg;
+
+ def isTestCfgBlacklisted(self, asTestCfg):
+ """
+ Returns whether the given test config is black listed.
+ """
+ fBlacklisted = False;
+
+ for asTestBlacklist in self.aasTestsBlacklist:
+ iLvl = 0;
+ fBlacklisted = True;
+ while iLvl < len(asTestBlacklist) and iLvl < len(asTestCfg):
+ if asTestBlacklist[iLvl] != asTestCfg[iLvl] and asTestBlacklist[iLvl] != '*':
+ fBlacklisted = False;
+ break;
+
+ iLvl += 1;
+
+ if not fBlacklisted and self.fnIsCfgSupported is not None:
+ fBlacklisted = not self.fnIsCfgSupported(asTestCfg);
+
+ return fBlacklisted;
+
+ def advanceTestCfg(self):
+ """
+ Advances to the next test config and returns it as an
+ array of strings or an empty config if there is no test left anymore.
+ """
+ iTestCfg, fnTestFmt, asTestCfg = self.at3TestLvls[self.iTestLvl];
+ iTestCfg += 1;
+ self.at3TestLvls[self.iTestLvl] = (iTestCfg, fnTestFmt, asTestCfg);
+ while iTestCfg == len(asTestCfg) and self.iTestLvl < len(self.at3TestLvls):
+ self.at3TestLvls[self.iTestLvl] = (0, fnTestFmt, asTestCfg);
+ self.iTestLvl += 1;
+ if self.iTestLvl < len(self.at3TestLvls):
+ iTestCfg, fnTestFmt, asTestCfg = self.at3TestLvls[self.iTestLvl];
+ iTestCfg += 1;
+ self.at3TestLvls[self.iTestLvl] = (iTestCfg, fnTestFmt, asTestCfg);
+ if iTestCfg < len(asTestCfg):
+ self.iTestLvl = 0;
+ break;
+ else:
+ break; # We reached the end of our tests.
+
+ return self.getCurrentTestCfg();
+
+ def getCurrentTestCfg(self):
+ """
+ Returns the current not black listed test config as an array of strings.
+ """
+ asTestCfg = [];
+
+ if self.iTestLvl < len(self.at3TestLvls):
+ for t3TestLvl in self.at3TestLvls:
+ iTestCfg, _, asTestLvl = t3TestLvl;
+ asTestCfg.append(asTestLvl[iTestCfg]);
+
+ asTestCfg.reverse()
+
+ return asTestCfg;
+
+ def getNextTestCfg(self, fSkippedLast = False):
+ """
+ Returns the next not blacklisted test config or an empty list if
+ there is no test left.
+ """
+ asTestCfgCur = self.getCurrentTestCfg();
+
+ asTestCfg = self.advanceTestCfg();
+ while asTestCfg and self.isTestCfgBlacklisted(asTestCfg):
+ asTestCfg = self.advanceTestCfg();
+
+ # Compare the current and next config and close the approriate test
+ # categories.
+ reporter.testDone(fSkippedLast);
+ if asTestCfg:
+ idxSame = 0;
+ while asTestCfgCur[idxSame] == asTestCfg[idxSame]:
+ idxSame += 1;
+
+ for i in range(idxSame, len(asTestCfg) - 1):
+ reporter.testDone();
+
+ for i in range(idxSame, len(asTestCfg)):
+ reporter.testStart('%s' % (self.getTestIdString(asTestCfg[i], i)));
+
+ else:
+ # No more tests, mark all tests as done
+ for i in range(0, len(asTestCfgCur) - 1):
+ reporter.testDone();
+
+ return asTestCfg;
+
+class tdStorageBenchmark(vbox.TestDriver): # pylint: disable=R0902
+ """
+ Storage benchmark.
+ """
+
+ # Global storage configs for the testbox
+ kdStorageCfgs = {
+ 'testboxstor1.de.oracle.com': r'c[3-9]t\dd0\Z',
+ 'adaris': [ '/dev/sda' ]
+ };
+
+ # Available test sets.
+ kdTestSets = {
+ # Mostly for developing and debugging the testcase.
+ 'Fast': {
+ 'RecordSize': '64k',
+ 'TestsetSize': '100m',
+ 'QueueDepth': '32',
+ 'DiskSizeGb': 2
+ },
+ # For quick functionality tests where benchmark results are not required.
+ 'Functionality': {
+ 'RecordSize': '64k',
+ 'TestsetSize': '2g',
+ 'QueueDepth': '32',
+ 'DiskSizeGb': 10
+ },
+ # For benchmarking the I/O stack.
+ 'Benchmark': {
+ 'RecordSize': '64k',
+ 'TestsetSize': '20g',
+ 'QueueDepth': '32',
+ 'DiskSizeGb': 30
+ },
+ # For stress testing which takes a lot of time.
+ 'Stress': {
+ 'RecordSize': '64k',
+ 'TestsetSize': '2t',
+ 'QueueDepth': '32',
+ 'DiskSizeGb': 10000
+ },
+ };
+
+ # Dictionary mapping the virtualization mode mnemonics to a little less cryptic
+ # strings used in test descriptions.
+ kdVirtModeDescs = {
+ 'raw' : 'Raw-mode',
+ 'hwvirt' : 'HwVirt',
+ 'hwvirt-np' : 'NestedPaging'
+ };
+
+ kdHostIoCacheDescs = {
+ 'default' : 'HostCacheDef',
+ 'hostiocache' : 'HostCacheOn',
+ 'no-hostiocache' : 'HostCacheOff'
+ };
+
+ # Password ID for encryption.
+ ksPwId = 'EncPwId';
+
+ # Array indexes for the test configs.
+ kiVmName = 0;
+ kiStorageCtrl = 1;
+ kiHostIoCache = 2;
+ kiDiskFmt = 3;
+ kiDiskVar = 4;
+ kiCpuCount = 5;
+ kiVirtMode = 6;
+ kiIoTest = 7;
+ kiTestSet = 8;
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.asTestVMsDef = ['tst-storage', 'tst-storage32'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef;
+ self.acCpusDef = [1, 2];
+ self.acCpus = self.acCpusDef;
+ self.asStorageCtrlsDef = ['AHCI', 'IDE', 'LsiLogicSAS', 'LsiLogic', 'BusLogic', 'NVMe'];
+ self.asStorageCtrls = self.asStorageCtrlsDef;
+ self.asHostIoCacheDef = ['default', 'hostiocache', 'no-hostiocache'];
+ self.asHostIoCache = self.asHostIoCacheDef;
+ self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD', 'QED', 'Parallels', 'QCOW', 'iSCSI'];
+ self.asDiskFormats = self.asDiskFormatsDef;
+ self.asDiskVariantsDef = ['Dynamic', 'Fixed', 'DynamicSplit2G', 'FixedSplit2G', 'Network'];
+ self.asDiskVariants = self.asDiskVariantsDef;
+ self.asTestsDef = ['iozone', 'fio'];
+ self.asTests = self.asTestsDef;
+ self.asTestSetsDef = ['Fast', 'Functionality', 'Benchmark', 'Stress'];
+ self.asTestSets = self.asTestSetsDef;
+ self.asIscsiTargetsDef = [ ]; # @todo: Configure one target for basic iSCSI testing
+ self.asIscsiTargets = self.asIscsiTargetsDef;
+ self.cDiffLvlsDef = 0;
+ self.cDiffLvls = self.cDiffLvlsDef;
+ self.fTestHost = False;
+ self.fUseScratch = False;
+ self.fRecreateStorCfg = True;
+ self.fReportBenchmarkResults = True;
+ self.oStorCfg = None;
+ self.sIoLogPathDef = self.sScratchPath;
+ self.sIoLogPath = self.sIoLogPathDef;
+ self.fIoLog = False;
+ self.fUseRamDiskDef = False;
+ self.fUseRamDisk = self.fUseRamDiskDef;
+ self.fEncryptDiskDef = False;
+ self.fEncryptDisk = self.fEncryptDiskDef;
+ self.sEncryptPwDef = 'TestTestTest';
+ self.sEncryptPw = self.sEncryptPwDef;
+ self.sEncryptAlgoDef = 'AES-XTS256-PLAIN64';
+ self.sEncryptAlgo = self.sEncryptAlgoDef;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdStorageBenchmark1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --storage-ctrls <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asStorageCtrlsDef)));
+ reporter.log(' --host-io-cache <setting1[:setting2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asHostIoCacheDef)));
+ reporter.log(' --disk-formats <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDiskFormatsDef)));
+ reporter.log(' --disk-variants <variant1[:variant2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDiskVariantsDef)));
+ reporter.log(' --iscsi-targets <target1[:target2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asIscsiTargetsDef)));
+ reporter.log(' --tests <test1[:test2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asTestsDef)));
+ reporter.log(' --test-sets <set1[:set2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asTestSetsDef)));
+ reporter.log(' --diff-levels <number of diffs>');
+ reporter.log(' Default: %s' % (self.cDiffLvlsDef));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --test-host');
+ reporter.log(' Do all configured tests on the host first and report the results');
+ reporter.log(' to get a baseline');
+ reporter.log(' --use-scratch');
+ reporter.log(' Use the scratch directory for testing instead of setting up');
+ reporter.log(' fresh volumes on dedicated disks (for development)');
+ reporter.log(' --always-wipe-storage-cfg');
+ reporter.log(' Recreate the host storage config before each test');
+ reporter.log(' --dont-wipe-storage-cfg');
+ reporter.log(' Don\'t recreate the host storage config before each test');
+ reporter.log(' --report-benchmark-results');
+ reporter.log(' Report all benchmark results');
+ reporter.log(' --dont-report-benchmark-results');
+ reporter.log(' Don\'t report any benchmark results');
+ reporter.log(' --io-log-path <path>');
+ reporter.log(' Default: %s' % (self.sIoLogPathDef));
+ reporter.log(' --enable-io-log');
+ reporter.log(' Whether to enable I/O logging for each test');
+ reporter.log(' --use-ramdisk');
+ reporter.log(' Default: %s' % (self.fUseRamDiskDef));
+ reporter.log(' --encrypt-disk');
+ reporter.log(' Default: %s' % (self.fEncryptDiskDef));
+ reporter.log(' --encrypt-password');
+ reporter.log(' Default: %s' % (self.sEncryptPwDef));
+ reporter.log(' --encrypt-algorithm');
+ reporter.log(' Default: %s' % (self.sEncryptAlgoDef));
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--storage-ctrls':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--storage-ctrls" takes a colon separated list of Storage controller types');
+ self.asStorageCtrls = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--host-io-cache':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--host-io-cache" takes a colon separated list of I/O cache settings');
+ self.asHostIoCache = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-formats':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-formats" takes a colon separated list of disk formats');
+ self.asDiskFormats = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-variants':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--disk-variants" takes a colon separated list of disk variants');
+ self.asDiskVariants = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--iscsi-targets':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--iscsi-targets" takes a colon separated list of iscsi targets');
+ self.asIscsiTargets = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of tests to run');
+ self.asTests = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--test-sets':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-sets" takes a colon separated list of test sets');
+ self.asTestSets = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--diff-levels':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--diff-levels" takes an integer');
+ try: self.cDiffLvls = int(asArgs[iArg]);
+ except: raise base.InvalidOption('The "--diff-levels" value "%s" is not an integer' % (asArgs[iArg],));
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
+ elif asArgs[iArg] == '--test-host':
+ self.fTestHost = True;
+ elif asArgs[iArg] == '--use-scratch':
+ self.fUseScratch = True;
+ elif asArgs[iArg] == '--always-wipe-storage-cfg':
+ self.fRecreateStorCfg = True;
+ elif asArgs[iArg] == '--dont-wipe-storage-cfg':
+ self.fRecreateStorCfg = False;
+ elif asArgs[iArg] == '--report-benchmark-results':
+ self.fReportBenchmarkResults = True;
+ elif asArgs[iArg] == '--dont-report-benchmark-results':
+ self.fReportBenchmarkResults = False;
+ elif asArgs[iArg] == '--io-log-path':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--io-log-path" takes a path argument');
+ self.sIoLogPath = asArgs[iArg];
+ elif asArgs[iArg] == '--enable-io-log':
+ self.fIoLog = True;
+ elif asArgs[iArg] == '--use-ramdisk':
+ self.fUseRamDisk = True;
+ elif asArgs[iArg] == '--encrypt-disk':
+ self.fEncryptDisk = True;
+ elif asArgs[iArg] == '--encrypt-password':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--encrypt-password" takes a string');
+ self.sEncryptPw = asArgs[iArg];
+ elif asArgs[iArg] == '--encrypt-algorithm':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--encrypt-algorithm" takes a string');
+ self.sEncryptAlgo = asArgs[iArg];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+ if 'tst-storage' in self.asTestVMs:
+ self.asRsrcs.append('5.0/storage/tst-storage.vdi');
+ if 'tst-storage32' in self.asTestVMs:
+ self.asRsrcs.append('5.0/storage/tst-storage32.vdi');
+
+ return self.asRsrcs;
+
+ def actionConfig(self):
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure the VMs we're going to use.
+ #
+
+ # Linux VMs
+ if 'tst-storage' in self.asTestVMs:
+ oVM = self.createTestVM('tst-storage', 1, '5.0/storage/tst-storage.vdi', sKind = 'ArchLinux_64', fIoApic = True, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ eNic0Type = vboxcon.NetworkAdapterType_Am79C973);
+ if oVM is None:
+ return False;
+
+ if 'tst-storage32' in self.asTestVMs:
+ oVM = self.createTestVM('tst-storage32', 1, '5.0/storage/tst-storage32.vdi', sKind = 'ArchLinux', fIoApic = True, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ eNic0Type = vboxcon.NetworkAdapterType_Am79C973);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.test1();
+ return fRc;
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def prepareStorage(self, oStorCfg, fRamDisk = False, cbPool = None):
+ """
+ Prepares the host storage for disk images or direct testing on the host.
+ """
+ # Create a basic pool with the default configuration.
+ sMountPoint = None;
+ fRc, sPoolId = oStorCfg.createStoragePool(cbPool = cbPool, fRamDisk = fRamDisk);
+ if fRc:
+ fRc, sMountPoint = oStorCfg.createVolume(sPoolId);
+ if not fRc:
+ sMountPoint = None;
+ oStorCfg.cleanup();
+
+ return sMountPoint;
+
+ def cleanupStorage(self, oStorCfg):
+ """
+ Cleans up any created storage space for a test.
+ """
+ return oStorCfg.cleanup();
+
+ def getGuestDisk(self, oSession, oTxsSession, eStorageController):
+ """
+ Gets the path of the disk in the guest to use for testing.
+ """
+ lstDisks = None;
+
+ # The naming scheme for NVMe is different and we don't have
+ # to query the guest for unformatted disks here because the disk with the OS
+ # is not attached to a NVMe controller.
+ if eStorageController == vboxcon.StorageControllerType_NVMe:
+ lstDisks = [ '/dev/nvme0n1' ];
+ else:
+ # Find a unformatted disk (no partition).
+ # @todo: This is a hack because LIST and STAT are not yet implemented
+ # in TXS (get to this eventually)
+ lstBlkDev = [ '/dev/sda', '/dev/sdb' ];
+ for sBlkDev in lstBlkDev:
+ fRc = oTxsSession.syncExec('/usr/bin/ls', ('ls', sBlkDev + '1'));
+ if not fRc:
+ lstDisks = [ sBlkDev ];
+ break;
+
+ _ = oSession;
+ return lstDisks;
+
+ def getDiskFormatVariantsForTesting(self, sDiskFmt, asVariants):
+ """
+ Returns a list of disk variants for testing supported by the given
+ disk format and selected for testing.
+ """
+ lstDskFmts = self.oVBoxMgr.getArray(self.oVBox.systemProperties, 'mediumFormats');
+ for oDskFmt in lstDskFmts:
+ if oDskFmt.id == sDiskFmt:
+ lstDskVariants = [];
+ lstCaps = self.oVBoxMgr.getArray(oDskFmt, 'capabilities');
+
+ if vboxcon.MediumFormatCapabilities_CreateDynamic in lstCaps \
+ and 'Dynamic' in asVariants:
+ lstDskVariants.append('Dynamic');
+
+ if vboxcon.MediumFormatCapabilities_CreateFixed in lstCaps \
+ and 'Fixed' in asVariants:
+ lstDskVariants.append('Fixed');
+
+ if vboxcon.MediumFormatCapabilities_CreateSplit2G in lstCaps \
+ and vboxcon.MediumFormatCapabilities_CreateDynamic in lstCaps \
+ and 'DynamicSplit2G' in asVariants:
+ lstDskVariants.append('DynamicSplit2G');
+
+ if vboxcon.MediumFormatCapabilities_CreateSplit2G in lstCaps \
+ and vboxcon.MediumFormatCapabilities_CreateFixed in lstCaps \
+ and 'FixedSplit2G' in asVariants:
+ lstDskVariants.append('FixedSplit2G');
+
+ if vboxcon.MediumFormatCapabilities_TcpNetworking in lstCaps \
+ and 'Network' in asVariants:
+ lstDskVariants.append('Network'); # Solely for iSCSI to get a non empty list
+
+ return lstDskVariants;
+
+ return [];
+
+ def convDiskToMediumVariant(self, sDiskVariant):
+ """
+ Returns a tuple of medium variant flags matching the given disk variant.
+ """
+ tMediumVariant = None;
+ if sDiskVariant == 'Dynamic':
+ tMediumVariant = (vboxcon.MediumVariant_Standard, );
+ elif sDiskVariant == 'Fixed':
+ tMediumVariant = (vboxcon.MediumVariant_Fixed, );
+ elif sDiskVariant == 'DynamicSplit2G':
+ tMediumVariant = (vboxcon.MediumVariant_Standard, vboxcon.MediumVariant_VmdkSplit2G);
+ elif sDiskVariant == 'FixedSplit2G':
+ tMediumVariant = (vboxcon.MediumVariant_Fixed, vboxcon.MediumVariant_VmdkSplit2G);
+
+ return tMediumVariant;
+
+ def getStorageCtrlFromName(self, sStorageCtrl):
+ """
+ Resolves the storage controller string to the matching constant.
+ """
+ eStorageCtrl = None;
+
+ if sStorageCtrl == 'AHCI':
+ eStorageCtrl = vboxcon.StorageControllerType_IntelAhci;
+ elif sStorageCtrl == 'IDE':
+ eStorageCtrl = vboxcon.StorageControllerType_PIIX4;
+ elif sStorageCtrl == 'LsiLogicSAS':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogicSas;
+ elif sStorageCtrl == 'LsiLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogic;
+ elif sStorageCtrl == 'BusLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_BusLogic;
+ elif sStorageCtrl == 'NVMe':
+ eStorageCtrl = vboxcon.StorageControllerType_NVMe;
+
+ return eStorageCtrl;
+
+ def getStorageDriverFromEnum(self, eStorageCtrl, fHardDisk):
+ """
+ Returns the appropriate driver name for the given storage controller
+ and a flag whether the driver has the generic SCSI driver attached.
+ """
+ if eStorageCtrl == vboxcon.StorageControllerType_IntelAhci:
+ if fHardDisk:
+ return ('ahci', False);
+ return ('ahci', True);
+ if eStorageCtrl == vboxcon.StorageControllerType_PIIX4:
+ return ('piix3ide', False);
+ if eStorageCtrl == vboxcon.StorageControllerType_LsiLogicSas:
+ return ('lsilogicsas', True);
+ if eStorageCtrl == vboxcon.StorageControllerType_LsiLogic:
+ return ('lsilogicscsi', True);
+ if eStorageCtrl == vboxcon.StorageControllerType_BusLogic:
+ return ('buslogic', True);
+ if eStorageCtrl == vboxcon.StorageControllerType_NVMe:
+ return ('nvme', False);
+
+ return ('<invalid>', False);
+
+ def isTestCfgSupported(self, asTestCfg):
+ """
+ Returns whether a specific test config is supported.
+ """
+
+ # Check whether the disk variant is supported by the selected format.
+ asVariants = self.getDiskFormatVariantsForTesting(asTestCfg[self.kiDiskFmt], [ asTestCfg[self.kiDiskVar] ]);
+ if not asVariants:
+ return False;
+
+ # For iSCSI check whether we have targets configured.
+ if asTestCfg[self.kiDiskFmt] == 'iSCSI' and not self.asIscsiTargets:
+ return False;
+
+ # Check for virt mode, CPU count and selected VM.
+ if asTestCfg[self.kiVirtMode] == 'raw' \
+ and (asTestCfg[self.kiCpuCount] > 1 or asTestCfg[self.kiVmName] == 'tst-storage'):
+ return False;
+
+ # IDE does not support the no host I/O cache setting
+ if asTestCfg[self.kiHostIoCache] == 'no-hostiocache' \
+ and asTestCfg[self.kiStorageCtrl] == 'IDE':
+ return False;
+
+ return True;
+
+ def fnFormatCpuString(self, cCpus):
+ """
+ Formats the CPU count to be readable.
+ """
+ if cCpus == 1:
+ return '1 cpu';
+ return '%u cpus' % (cCpus);
+
+ def fnFormatVirtMode(self, sVirtMode):
+ """
+ Formats the virtualization mode to be a little less cryptic for use in test
+ descriptions.
+ """
+ return self.kdVirtModeDescs[sVirtMode];
+
+ def fnFormatHostIoCache(self, sHostIoCache):
+ """
+ Formats the host I/O cache mode to be a little less cryptic for use in test
+ descriptions.
+ """
+ return self.kdHostIoCacheDescs[sHostIoCache];
+
+ def testBenchmark(self, sTargetOs, sBenchmark, sMountpoint, oExecutor, dTestSet, \
+ cMsTimeout = 3600000):
+ """
+ Runs the given benchmark on the test host.
+ """
+
+ dTestSet['FilePath'] = sMountpoint;
+ dTestSet['TargetOs'] = sTargetOs;
+
+ oTst = None;
+ if sBenchmark == 'iozone':
+ oTst = IozoneTest(oExecutor, dTestSet);
+ elif sBenchmark == 'fio':
+ oTst = FioTest(oExecutor, dTestSet); # pylint: disable=R0204
+
+ if oTst is not None:
+ fRc = oTst.prepare();
+ if fRc:
+ fRc = oTst.run(cMsTimeout);
+ if fRc:
+ if self.fReportBenchmarkResults:
+ fRc = oTst.reportResult();
+ else:
+ reporter.testFailure('Running the testcase failed');
+ reporter.addLogString(oTst.getErrorReport(), sBenchmark + '.log',
+ 'log/release/client', 'Benchmark raw output');
+ else:
+ reporter.testFailure('Preparing the testcase failed');
+
+ oTst.cleanup();
+
+ return fRc;
+
+ def createHd(self, oSession, sDiskFormat, sDiskVariant, iDiffLvl, oHdParent, \
+ sDiskPath, cbDisk):
+ """
+ Creates a new disk with the given parameters returning the medium object
+ on success.
+ """
+
+ oHd = None;
+ if sDiskFormat == "iSCSI" and iDiffLvl == 0:
+ listNames = [];
+ listValues = [];
+ listValues = self.asIscsiTargets[0].split('|');
+ listNames.append('TargetAddress');
+ listNames.append('TargetName');
+ listNames.append('LUN');
+
+ if self.fpApiVer >= 5.0:
+ oHd = oSession.oVBox.createMedium(sDiskFormat, sDiskPath, vboxcon.AccessMode_ReadWrite, \
+ vboxcon.DeviceType_HardDisk);
+ else:
+ oHd = oSession.oVBox.createHardDisk(sDiskFormat, sDiskPath);
+ oHd.type = vboxcon.MediumType_Normal;
+ oHd.setProperties(listNames, listValues);
+ else:
+ if iDiffLvl == 0:
+ tMediumVariant = self.convDiskToMediumVariant(sDiskVariant);
+ oHd = oSession.createBaseHd(sDiskPath + '/base.disk', sDiskFormat, cbDisk, \
+ cMsTimeout = 3600 * 1000, tMediumVariant = tMediumVariant);
+ else:
+ sDiskPath = sDiskPath + '/diff_%u.disk' % (iDiffLvl);
+ oHd = oSession.createDiffHd(oHdParent, sDiskPath, None);
+
+ if oHd is not None and iDiffLvl == 0 and self.fEncryptDisk:
+ try:
+ oIProgress = oHd.changeEncryption('', self.sEncryptAlgo, self.sEncryptPw, self.ksPwId);
+ oProgress = vboxwrappers.ProgressWrapper(oIProgress, self.oVBoxMgr, self, 'Encrypting "%s"' % (sDiskPath,));
+ oProgress.wait(60*60000); # Wait for up to one hour, fixed disks take longer to encrypt.
+ if oProgress.logResult() is False:
+ raise base.GenError('Encrypting disk "%s" failed' % (sDiskPath, ));
+ except:
+ reporter.errorXcpt('changeEncryption("%s","%s","%s") failed on "%s"' \
+ % ('', self.sEncryptAlgo, self.sEncryptPw, oSession.sName) );
+ self.oVBox.deleteHdByMedium(oHd);
+ oHd = None;
+ else:
+ reporter.log('Encrypted "%s"' % (sDiskPath,));
+
+ return oHd;
+
+ def startVmAndConnect(self, sVmName):
+ """
+ Our own implementation of startVmAndConnectToTxsViaTcp to make it possible
+ to add passwords to a running VM when encryption is used.
+ """
+ oSession = self.startVmByName(sVmName);
+ if oSession is not None:
+ # Add password to the session in case encryption is used.
+ fRc = True;
+ if self.fEncryptDisk:
+ try:
+ oSession.o.console.addDiskEncryptionPassword(self.ksPwId, self.sEncryptPw, False);
+ except:
+ reporter.logXcpt();
+ fRc = False;
+
+ # Connect to TXS.
+ if fRc:
+ reporter.log2('startVmAndConnect: Started(/prepared) "%s", connecting to TXS ...' % (sVmName,));
+ (fRc, oTxsSession) = self.txsDoConnectViaTcp(oSession, 15*60000, fNatForwardingForTxs = True);
+ if fRc is True:
+ if fRc is True:
+ # Success!
+ return (oSession, oTxsSession);
+ else:
+ reporter.error('startVmAndConnect: txsDoConnectViaTcp failed');
+ # If something went wrong while waiting for TXS to be started - take VM screenshot before terminate it
+
+ self.terminateVmBySession(oSession);
+
+ return (None, None);
+
+ def testOneCfg(self, sVmName, eStorageController, sHostIoCache, sDiskFormat, # pylint: disable=R0913,R0914,R0915
+ sDiskVariant, sDiskPath, cCpus, sIoTest, sVirtMode, sTestSet):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ oVM = self.getVmByName(sVmName);
+
+ dTestSet = self.kdTestSets.get(sTestSet);
+ cbDisk = dTestSet.get('DiskSizeGb') * 1024*1024*1024;
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+
+ fRc = True;
+ if sDiskFormat == 'iSCSI':
+ sDiskPath = self.asIscsiTargets[0];
+ elif self.fUseScratch:
+ sDiskPath = self.sScratchPath;
+ else:
+ # If requested recreate the storage space to start with a clean config
+ # for benchmarks
+ if self.fRecreateStorCfg:
+ sMountPoint = self.prepareStorage(self.oStorCfg, self.fUseRamDisk, 2 * cbDisk);
+ if sMountPoint is not None:
+ # Create a directory where every normal user can write to.
+ self.oStorCfg.mkDirOnVolume(sMountPoint, 'test', 0o777);
+ sDiskPath = sMountPoint + '/test';
+ else:
+ fRc = False;
+ reporter.testFailure('Failed to prepare storage for VM');
+
+ if not fRc:
+ return fRc;
+
+ lstDisks = []; # List of disks we have to delete afterwards.
+
+ for iDiffLvl in range(self.cDiffLvls + 1):
+ sIoLogFile = None;
+
+ if iDiffLvl == 0:
+ reporter.testStart('Base');
+ else:
+ reporter.testStart('Diff %u' % (iDiffLvl));
+
+ # Reconfigure the VM
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ #
+ # Disable audio controller which shares the interrupt line with the BusLogic controller and is suspected to cause
+ # rare test failures because the device initialization fails.
+ #
+ fRc = oSession.setupAudio(vboxcon.AudioControllerType_AC97, False);
+ # Attach HD
+ fRc = fRc and oSession.ensureControllerAttached(_ControllerTypeToName(eStorageController));
+ fRc = fRc and oSession.setStorageControllerType(eStorageController, _ControllerTypeToName(eStorageController));
+
+ if sHostIoCache == 'hostiocache':
+ fRc = fRc and oSession.setStorageControllerHostIoCache(_ControllerTypeToName(eStorageController), True);
+ elif sHostIoCache == 'no-hostiocache':
+ fRc = fRc and oSession.setStorageControllerHostIoCache(_ControllerTypeToName(eStorageController), False);
+
+ iDevice = 0;
+ if eStorageController == vboxcon.StorageControllerType_PIIX3 or \
+ eStorageController == vboxcon.StorageControllerType_PIIX4:
+ iDevice = 1; # Master is for the OS.
+
+ oHdParent = None;
+ if iDiffLvl > 0:
+ oHdParent = lstDisks[0];
+ oHd = self.createHd(oSession, sDiskFormat, sDiskVariant, iDiffLvl, oHdParent, sDiskPath, cbDisk);
+ if oHd is not None:
+ lstDisks.insert(0, oHd);
+ try:
+ if oSession.fpApiVer >= 4.0:
+ oSession.o.machine.attachDevice(_ControllerTypeToName(eStorageController), \
+ 0, iDevice, vboxcon.DeviceType_HardDisk, oHd);
+ else:
+ oSession.o.machine.attachDevice(_ControllerTypeToName(eStorageController), \
+ 0, iDevice, vboxcon.DeviceType_HardDisk, oHd.id);
+ except:
+ reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \
+ % (_ControllerTypeToName(eStorageController), 1, 0, oHd.id, oSession.sName) );
+ fRc = False;
+ else:
+ reporter.log('attached "%s" to %s' % (sDiskPath, oSession.sName));
+ else:
+ fRc = False;
+
+ # Set up the I/O logging config if enabled
+ if fRc and self.fIoLog:
+ try:
+ oSession.o.machine.setExtraData('VBoxInternal2/EnableDiskIntegrityDriver', '1');
+
+ iLun = 0;
+ if eStorageController == vboxcon.StorageControllerType_PIIX3 or \
+ eStorageController == vboxcon.StorageControllerType_PIIX4:
+ iLun = 1
+ sDrv, fDrvScsi = self.getStorageDriverFromEnum(eStorageController, True);
+ if fDrvScsi:
+ sCfgmPath = 'VBoxInternal/Devices/%s/0/LUN#%u/AttachedDriver/Config' % (sDrv, iLun);
+ else:
+ sCfgmPath = 'VBoxInternal/Devices/%s/0/LUN#%u/Config' % (sDrv, iLun);
+
+ sIoLogFile = '%s/%s.iolog' % (self.sIoLogPath, sDrv);
+ print(sCfgmPath);
+ print(sIoLogFile);
+ oSession.o.machine.setExtraData('%s/IoLog' % (sCfgmPath,), sIoLogFile);
+ except:
+ reporter.logXcpt();
+
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnect(sVmName);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+
+ # Prepare the storage on the guest
+ lstBinaryPaths = ['/bin', '/sbin', '/usr/bin', '/usr/sbin' ];
+ oExecVm = remoteexecutor.RemoteExecutor(oTxsSession, lstBinaryPaths, '${SCRATCH}');
+ oStorCfgVm = storagecfg.StorageCfg(oExecVm, 'linux', self.getGuestDisk(oSession, oTxsSession, \
+ eStorageController));
+
+ iTry = 0;
+ while iTry < 3:
+ sMountPoint = self.prepareStorage(oStorCfgVm);
+ if sMountPoint is not None:
+ reporter.log('Prepared storage on %s try' % (iTry + 1,));
+ break;
+ else:
+ iTry = iTry + 1;
+ self.sleep(5);
+
+ if sMountPoint is not None:
+ self.testBenchmark('linux', sIoTest, sMountPoint, oExecVm, dTestSet, \
+ cMsTimeout = 3 * 3600 * 1000); # 3 hours max (Benchmark and QED takes a lot of time)
+ self.cleanupStorage(oStorCfgVm);
+ else:
+ reporter.testFailure('Failed to prepare storage for the guest benchmark');
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession);
+
+ # Add the I/O log if it exists and the test failed
+ if reporter.testErrorCount() > 0 \
+ and sIoLogFile is not None \
+ and os.path.exists(sIoLogFile):
+ reporter.addLogFile(sIoLogFile, 'misc/other', 'I/O log');
+ os.remove(sIoLogFile);
+
+ else:
+ fRc = False;
+
+ # Remove disk
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ try:
+ oSession.o.machine.detachDevice(_ControllerTypeToName(eStorageController), 0, iDevice);
+
+ # Remove storage controller if it is not an IDE controller.
+ if eStorageController is not vboxcon.StorageControllerType_PIIX3 \
+ and eStorageController is not vboxcon.StorageControllerType_PIIX4:
+ oSession.o.machine.removeStorageController(_ControllerTypeToName(eStorageController));
+
+ oSession.saveSettings();
+ oSession.saveSettings();
+ oSession.close();
+ oSession = None;
+ except:
+ reporter.errorXcpt('failed to detach/delete disk %s from storage controller' % (sDiskPath));
+ else:
+ fRc = False;
+
+ reporter.testDone();
+
+ # Delete all disks
+ for oHd in lstDisks:
+ self.oVBox.deleteHdByMedium(oHd);
+
+ # Cleanup storage area
+ if sDiskFormat != 'iSCSI' and not self.fUseScratch and self.fRecreateStorCfg:
+ self.cleanupStorage(self.oStorCfg);
+
+ return fRc;
+
+ def testStorage(self, sDiskPath = None):
+ """
+ Runs the storage testcase through the selected configurations
+ """
+
+ aasTestCfgs = [];
+ aasTestCfgs.insert(self.kiVmName, self.asTestVMs);
+ aasTestCfgs.insert(self.kiStorageCtrl, self.asStorageCtrls);
+ aasTestCfgs.insert(self.kiHostIoCache, (self.asHostIoCache, self.fnFormatHostIoCache));
+ aasTestCfgs.insert(self.kiDiskFmt, self.asDiskFormats);
+ aasTestCfgs.insert(self.kiDiskVar, self.asDiskVariants);
+ aasTestCfgs.insert(self.kiCpuCount, (self.acCpus, self.fnFormatCpuString));
+ aasTestCfgs.insert(self.kiVirtMode, (self.asVirtModes, self.fnFormatVirtMode));
+ aasTestCfgs.insert(self.kiIoTest, self.asTests);
+ aasTestCfgs.insert(self.kiTestSet, self.asTestSets);
+
+ aasTestsBlacklist = [];
+ aasTestsBlacklist.append(['tst-storage', 'BusLogic']); # 64bit Linux is broken with BusLogic
+
+ oTstCfgMgr = StorTestCfgMgr(aasTestCfgs, aasTestsBlacklist, self.isTestCfgSupported);
+
+ fRc = True;
+ asTestCfg = oTstCfgMgr.getCurrentTestCfg();
+ while asTestCfg:
+ fRc = self.testOneCfg(asTestCfg[self.kiVmName], self.getStorageCtrlFromName(asTestCfg[self.kiStorageCtrl]), \
+ asTestCfg[self.kiHostIoCache], asTestCfg[self.kiDiskFmt], asTestCfg[self.kiDiskVar],
+ sDiskPath, asTestCfg[self.kiCpuCount], asTestCfg[self.kiIoTest], \
+ asTestCfg[self.kiVirtMode], asTestCfg[self.kiTestSet]) and fRc and True; # pychecker hack.
+
+ asTestCfg = oTstCfgMgr.getNextTestCfg();
+
+ return fRc;
+
+ def test1(self):
+ """
+ Executes test #1.
+ """
+
+ fRc = True;
+ oDiskCfg = self.kdStorageCfgs.get(socket.gethostname().lower());
+
+ # Test the host first if requested
+ if oDiskCfg is not None or self.fUseScratch:
+ lstBinaryPaths = ['/bin', '/sbin', '/usr/bin', '/usr/sbin', \
+ '/opt/csw/bin', '/usr/ccs/bin', '/usr/sfw/bin'];
+ oExecutor = remoteexecutor.RemoteExecutor(None, lstBinaryPaths, self.sScratchPath);
+ if not self.fUseScratch:
+ self.oStorCfg = storagecfg.StorageCfg(oExecutor, utils.getHostOs(), oDiskCfg);
+
+ # Try to cleanup any leftovers from a previous run first.
+ fRc = self.oStorCfg.cleanupLeftovers();
+ if not fRc:
+ reporter.error('Failed to cleanup any leftovers from a previous run');
+
+ if self.fTestHost:
+ reporter.testStart('Host');
+ if self.fUseScratch:
+ sMountPoint = self.sScratchPath;
+ else:
+ sMountPoint = self.prepareStorage(self.oStorCfg);
+ if sMountPoint is not None:
+ for sIoTest in self.asTests:
+ reporter.testStart(sIoTest);
+ for sTestSet in self.asTestSets:
+ reporter.testStart(sTestSet);
+ dTestSet = self.kdTestSets.get(sTestSet);
+ self.testBenchmark(utils.getHostOs(), sIoTest, sMountPoint, oExecutor, dTestSet);
+ reporter.testDone();
+ reporter.testDone();
+ self.cleanupStorage(self.oStorCfg);
+ else:
+ reporter.testFailure('Failed to prepare host storage');
+ fRc = False;
+ reporter.testDone();
+ else:
+ # Create the storage space first if it is not done before every test.
+ sMountPoint = None;
+ if self.fUseScratch:
+ sMountPoint = self.sScratchPath;
+ elif not self.fRecreateStorCfg:
+ reporter.testStart('Create host storage');
+ sMountPoint = self.prepareStorage(self.oStorCfg);
+ if sMountPoint is None:
+ reporter.testFailure('Failed to prepare host storage');
+ fRc = False;
+ self.oStorCfg.mkDirOnVolume(sMountPoint, 'test', 0o777);
+ sMountPoint = sMountPoint + '/test';
+ reporter.testDone();
+
+ if fRc:
+ # Run the storage tests.
+ if not self.testStorage(sMountPoint):
+ fRc = False;
+
+ if not self.fRecreateStorCfg and not self.fUseScratch:
+ self.cleanupStorage(self.oStorCfg);
+ else:
+ fRc = False;
+
+ return fRc;
+
+if __name__ == '__main__':
+ sys.exit(tdStorageBenchmark().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/storage/tdStorageSnapshotMerging1.py b/src/VBox/ValidationKit/tests/storage/tdStorageSnapshotMerging1.py
new file mode 100755
index 00000000..257de781
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/tdStorageSnapshotMerging1.py
@@ -0,0 +1,423 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdStorageSnapshotMerging1.py $
+
+"""
+VirtualBox Validation Kit - Storage snapshotting and merging testcase.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2013-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+import zlib;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+from testdriver import vboxwrappers;
+
+
+def _ControllerTypeToName(eControllerType):
+ """ Translate a controller type to a name. """
+ if eControllerType == vboxcon.StorageControllerType_PIIX3 or eControllerType == vboxcon.StorageControllerType_PIIX4:
+ sType = "IDE Controller";
+ elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
+ sType = "SATA Controller";
+ elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
+ sType = "SAS Controller";
+ elif eControllerType == vboxcon.StorageControllerType_LsiLogic or eControllerType == vboxcon.StorageControllerType_BusLogic:
+ sType = "SCSI Controller";
+ else:
+ sType = "Storage Controller";
+ return sType;
+
+
+def crc32_of_file(filepath):
+ fileobj = open(filepath,'rb');
+ current = 0;
+
+ while True:
+ buf = fileobj.read(1024 * 1024);
+ if not buf:
+ break
+ current = zlib.crc32(buf, current);
+
+ fileobj.close();
+ return current % 2**32;
+
+
+class tdStorageSnapshot(vbox.TestDriver): # pylint: disable=R0902
+ """
+ Storage benchmark.
+ """
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oGuestToGuestVM = None;
+ self.oGuestToGuestSess = None;
+ self.oGuestToGuestTxs = None;
+ self.asStorageCtrlsDef = ['AHCI', 'IDE', 'LsiLogicSAS', 'LsiLogic', 'BusLogic'];
+ self.asStorageCtrls = self.asStorageCtrlsDef;
+ #self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD', 'QED', 'Parallels', 'QCOW', 'iSCSI'];
+ self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD'];
+ self.asDiskFormats = self.asDiskFormatsDef;
+ self.sRndData = os.urandom(100*1024*1024);
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdStorageSnapshot1 Options:');
+ reporter.log(' --storage-ctrls <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asStorageCtrls)));
+ reporter.log(' --disk-formats <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDiskFormats)));
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ if asArgs[iArg] == '--storage-ctrls':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--storage-ctrls" takes a colon separated list of Storage controller types');
+ self.asStorageCtrls = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-formats':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-formats" takes a colon separated list of disk formats');
+ self.asDiskFormats = asArgs[iArg].split(':');
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = ['5.3/storage/mergeMedium/t-orig.vdi',
+ '5.3/storage/mergeMedium/t-fixed.vdi',
+ '5.3/storage/mergeMedium/t-resized.vdi'];
+ return self.asRsrcs;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.test1();
+ return fRc;
+
+ def resizeMedium(self, oMedium, cbNewSize):
+ if oMedium.deviceType is not vboxcon.DeviceType_HardDisk:
+ return False;
+
+ if oMedium.type is not vboxcon.MediumType_Normal:
+ return False;
+
+ #currently only VDI can be resizable. Medium variant is not checked, because testcase creates disks itself
+ oMediumFormat = oMedium.mediumFormat;
+ if oMediumFormat.id != 'VDI':
+ return False;
+
+ cbCurrSize = oMedium.logicalSize;
+ # currently reduce is not supported
+ if cbNewSize < cbCurrSize:
+ return False;
+
+ try:
+ oProgressCom = oMedium.resize(cbNewSize);
+ oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oVBox.oTstDrv,
+ 'Resize medium %s' % (oMedium.name));
+ oProgress.wait(cMsTimeout = 60 * 1000);
+ oProgress.logResult();
+ except:
+ reporter.logXcpt('IMedium::resize failed on %s' % (oMedium.name));
+ return False;
+
+ return True;
+
+ def getMedium(self, oVM, sController):
+ oMediumAttachments = oVM.getMediumAttachmentsOfController(sController);
+
+ for oAttachment in oMediumAttachments:
+ oMedium = oAttachment.medium;
+ if oMedium.deviceType is not vboxcon.DeviceType_HardDisk:
+ continue;
+ if oMedium.type is not vboxcon.MediumType_Normal:
+ continue;
+ return oMedium;
+
+ return None;
+
+ def getSnapshotMedium(self, oSnapshot, sController):
+ oVM = oSnapshot.machine;
+ oMedium = self.getMedium(oVM, sController);
+
+ for oChildMedium in oMedium.children:
+ for uSnapshotId in oChildMedium.getSnapshotIds(oVM.id):
+ if uSnapshotId == oVM.id:
+ return oChildMedium;
+
+ return None;
+
+ def openMedium(self, sHd, fImmutable = False):
+ """
+ Opens medium in readonly mode.
+ Returns Medium object on success and None on failure. Error information is logged.
+ """
+ sFullName = self.oVBox.oTstDrv.getFullResourceName(sHd);
+ try:
+ oHd = self.oVBox.findHardDisk(sFullName);
+ except:
+ try:
+ if self.fpApiVer >= 4.1:
+ oHd = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadOnly, False);
+ elif self.fpApiVer >= 4.0:
+ oHd = self.oVBox.openMedium(sFullName, vboxcon.DeviceType_HardDisk, vboxcon.AccessMode_ReadOnly);
+ else:
+ oHd = self.oVBox.openHardDisk(sFullName, vboxcon.AccessMode_ReadOnly, False, "", False, "");
+
+ except:
+ reporter.errorXcpt('failed to open hd "%s"' % (sFullName));
+ return None;
+
+ try:
+ if fImmutable:
+ oHd.type = vboxcon.MediumType_Immutable;
+ else:
+ oHd.type = vboxcon.MediumType_Normal;
+
+ except:
+ if fImmutable:
+ reporter.errorXcpt('failed to set hd "%s" immutable' % (sHd));
+ else:
+ reporter.errorXcpt('failed to set hd "%s" normal' % (sHd));
+
+ return None;
+
+ return oHd;
+
+ def cloneMedium(self, oSrcHd, oTgtHd):
+ """
+ Clones medium into target medium.
+ """
+ try:
+ oProgressCom = oSrcHd.cloneTo(oTgtHd, (vboxcon.MediumVariant_Standard, ), None);
+ oProgress = vboxwrappers.ProgressWrapper(oProgressCom, self.oVBoxMgr, self.oVBox.oTstDrv,
+ 'clone base disk %s to %s' % (oSrcHd.name, oTgtHd.name));
+ oProgress.wait(cMsTimeout = 60 * 1000);
+ oProgress.logResult();
+ except:
+ reporter.errorXcpt('failed to clone medium %s to %s' % (oSrcHd.name, oTgtHd.name));
+ return False;
+
+ return True;
+
+ def deleteVM(self, oVM):
+ try:
+ oVM.unregister(vboxcon.CleanupMode_DetachAllReturnNone);
+ except:
+ reporter.logXcpt();
+
+ if self.fpApiVer >= 4.0:
+ try:
+ if self.fpApiVer >= 4.3:
+ oProgress = oVM.deleteConfig([]);
+ else:
+ oProgress = oVM.delete(None);
+ self.waitOnProgress(oProgress);
+
+ except:
+ reporter.logXcpt();
+
+ else:
+ try: oVM.deleteSettings();
+ except: reporter.logXcpt();
+
+ return None;
+
+ #
+ # Test execution helpers.
+ #
+
+ def test1OneCfg(self, eStorageController, oDskFmt):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+
+ (asExts, aTypes) = oDskFmt.describeFileExtensions()
+ for i in range(0, len(asExts)): #pylint: disable=consider-using-enumerate
+ if aTypes[i] is vboxcon.DeviceType_HardDisk:
+ sExt = '.' + asExts[i]
+ break
+
+ if sExt is None:
+ return False;
+
+ oOrigBaseHd = self.openMedium('5.3/storage/mergeMedium/t-orig.vdi');
+ if oOrigBaseHd is None:
+ return False;
+
+ #currently only VDI can be resizable. Medium variant is not checked, because testcase creates disks itself
+ fFmtDynamic = oDskFmt.id == 'VDI';
+ sOrigWithDiffHd = '5.3/storage/mergeMedium/t-fixed.vdi'
+ uOrigCrc = 0x7a417cbb;
+
+ if fFmtDynamic:
+ sOrigWithDiffHd = '5.3/storage/mergeMedium/t-resized.vdi';
+ uOrigCrc = 0xa8f5daa3;
+
+ oOrigWithDiffHd = self.openMedium(sOrigWithDiffHd);
+ if oOrigWithDiffHd is None:
+ return False;
+
+ oVM = self.createTestVM('testvm', 1, None);
+ if oVM is None:
+ return False;
+
+ sController = _ControllerTypeToName(eStorageController);
+
+ # Reconfigure the VM
+ oSession = self.openSession(oVM);
+ if oSession is None:
+ return False;
+ # Attach HD
+
+ fRc = True;
+ sFile = 't-base' + sExt;
+ sHddPath = os.path.join(self.oVBox.oTstDrv.sScratchPath, sFile);
+ oHd = oSession.createBaseHd(sHddPath, sFmt=oDskFmt.id, cb=oOrigBaseHd.logicalSize);
+ #if oSession.createBaseHd can't create disk because it exists, oHd will point to some stub object anyway
+ fRc = fRc and oHd is not None and (oHd.logicalSize == oOrigBaseHd.logicalSize);
+ fRc = fRc and self.cloneMedium(oOrigBaseHd, oHd);
+
+ fRc = fRc and oSession.ensureControllerAttached(sController);
+ fRc = fRc and oSession.setStorageControllerType(eStorageController, sController);
+ fRc = fRc and oSession.saveSettings();
+ fRc = fRc and oSession.attachHd(sHddPath, sController, iPort = 0, fImmutable=False, fForceResource=False)
+
+ if fRc:
+ oSession.takeSnapshot('Base snapshot');
+ oSnapshot = oSession.findSnapshot('Base snapshot');
+
+ if oSnapshot is not None:
+ oSnapshotMedium = self.getSnapshotMedium(oSnapshot, sController);
+ fRc = oSnapshotMedium is not None;
+
+ if fFmtDynamic:
+ fRc = fRc and self.resizeMedium(oSnapshotMedium, oOrigWithDiffHd.logicalSize);
+ fRc = fRc and self.cloneMedium(oOrigWithDiffHd, oSnapshotMedium);
+ fRc = fRc and oSession.deleteSnapshot(oSnapshot.id, cMsTimeout = 120 * 1000);
+
+ if fRc:
+ # disk for result test by checksum
+ sResFilePath = os.path.join(self.oVBox.oTstDrv.sScratchPath, 't_res.vmdk');
+ sResFilePathRaw = os.path.join(self.oVBox.oTstDrv.sScratchPath, 't_res-flat.vmdk');
+ oResHd = oSession.createBaseHd(sResFilePath, sFmt='VMDK', cb=oOrigWithDiffHd.logicalSize,
+ tMediumVariant = (vboxcon.MediumVariant_Fixed, ));
+ fRc = oResHd is not None;
+ fRc = fRc and self.cloneMedium(oHd, oResHd);
+
+ uResCrc32 = 0;
+ if fRc:
+ uResCrc32 = crc32_of_file(sResFilePathRaw);
+ if uResCrc32 == uOrigCrc:
+ reporter.log('Snapshot merged successfully. Crc32 is correct');
+ fRc = True;
+ else:
+ reporter.error('Snapshot merging failed. Crc32 is invalid');
+ fRc = False;
+
+ self.oVBox.deleteHdByMedium(oResHd);
+
+ if oSession is not None:
+ if oHd is not None:
+ oSession.detachHd(sController, iPort = 0, iDevice = 0);
+
+ oSession.saveSettings(fClose = True);
+ if oHd is not None:
+ self.oVBox.deleteHdByMedium(oHd);
+
+ self.deleteVM(oVM);
+ return fRc;
+
+ def test1(self):
+ """
+ Executes test #1 thru the various configurations.
+ """
+ if not self.importVBoxApi():
+ return False;
+
+ sVmName = 'testvm';
+ reporter.testStart(sVmName);
+
+ aoDskFmts = self.oVBoxMgr.getArray(self.oVBox.systemProperties, 'mediumFormats')
+ if aoDskFmts is None or len(aoDskFmts) < 1:
+ return False;
+
+ fRc = True;
+ for sStorageCtrl in self.asStorageCtrls:
+ reporter.testStart(sStorageCtrl);
+ if sStorageCtrl == 'AHCI':
+ eStorageCtrl = vboxcon.StorageControllerType_IntelAhci;
+ elif sStorageCtrl == 'IDE':
+ eStorageCtrl = vboxcon.StorageControllerType_PIIX4;
+ elif sStorageCtrl == 'LsiLogicSAS':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogicSas;
+ elif sStorageCtrl == 'LsiLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogic;
+ elif sStorageCtrl == 'BusLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_BusLogic;
+ else:
+ eStorageCtrl = None;
+
+ for oDskFmt in aoDskFmts:
+ if oDskFmt.id in self.asDiskFormats:
+ reporter.testStart('%s' % (oDskFmt.id));
+ fRc = self.test1OneCfg(eStorageCtrl, oDskFmt);
+ reporter.testDone();
+ if not fRc:
+ break;
+
+ reporter.testDone();
+ if not fRc:
+ break;
+
+ reporter.testDone();
+ return fRc;
+
+if __name__ == '__main__':
+ sys.exit(tdStorageSnapshot().main(sys.argv));
diff --git a/src/VBox/ValidationKit/tests/storage/tdStorageStress1.py b/src/VBox/ValidationKit/tests/storage/tdStorageStress1.py
new file mode 100755
index 00000000..556aeb46
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/storage/tdStorageStress1.py
@@ -0,0 +1,517 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Storage testcase using xfstests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2012-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Id: tdStorageStress1.py $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+def _ControllerTypeToName(eControllerType):
+ """ Translate a controller type to a name. """
+ if eControllerType == vboxcon.StorageControllerType_PIIX3 or eControllerType == vboxcon.StorageControllerType_PIIX4:
+ sType = "IDE Controller";
+ elif eControllerType == vboxcon.StorageControllerType_IntelAhci:
+ sType = "SATA Controller";
+ elif eControllerType == vboxcon.StorageControllerType_LsiLogicSas:
+ sType = "SAS Controller";
+ elif eControllerType == vboxcon.StorageControllerType_LsiLogic or eControllerType == vboxcon.StorageControllerType_BusLogic:
+ sType = "SCSI Controller";
+ else:
+ sType = "Storage Controller";
+ return sType;
+
+class tdStorageStress(vbox.TestDriver): # pylint: disable=R0902
+ """
+ Storage testcase.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.oGuestToGuestVM = None;
+ self.oGuestToGuestSess = None;
+ self.oGuestToGuestTxs = None;
+ self.asTestVMsDef = ['tst-debian'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef
+ self.acCpusDef = [1, 2,]
+ self.acCpus = self.acCpusDef;
+ self.asStorageCtrlsDef = ['AHCI', 'IDE', 'LsiLogicSAS', 'LsiLogic', 'BusLogic'];
+ self.asStorageCtrls = self.asStorageCtrlsDef;
+ self.asDiskFormatsDef = ['VDI', 'VMDK', 'VHD', 'QED', 'Parallels', 'QCOW'];
+ self.asDiskFormats = self.asDiskFormatsDef;
+ self.asTestsDef = ['xfstests'];
+ self.asTests = self.asTestsDef;
+ self.asGuestFs = ['xfs', 'ext4', 'btrfs'];
+ self.asGuestFsDef = self.asGuestFs;
+ self.asIscsiTargetsDef = ['aurora|iqn.2011-03.home.aurora:aurora.storagebench|1'];
+ self.asIscsiTargets = self.asIscsiTargetsDef;
+ self.asDirsDef = ['/run/media/alexander/OWCSSD/alexander', \
+ '/run/media/alexander/CrucialSSD/alexander', \
+ '/run/media/alexander/HardDisk/alexander', \
+ '/home/alexander'];
+ self.asDirs = self.asDirsDef;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdStorageBenchmark1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --storage-ctrls <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asStorageCtrls)));
+ reporter.log(' --disk-formats <type1[:type2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDiskFormats)));
+ reporter.log(' --disk-dirs <path1[:path2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asDirs)));
+ reporter.log(' --iscsi-targets <target1[:target2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asIscsiTargets)));
+ reporter.log(' --tests <test1[:test2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asTests)));
+ reporter.log(' --guest-fs <fs1[:fs2[:...]]>');
+ reporter.log(' Default: %s' % (':'.join(self.asGuestFs)));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--storage-ctrls':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--storage-ctrls" takes a colon separated list of Storage controller types');
+ self.asStorageCtrls = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-formats':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-formats" takes a colon separated list of disk formats');
+ self.asDiskFormats = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--disk-dirs':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--disk-dirs" takes a colon separated list of directories');
+ self.asDirs = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--iscsi-targets':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--iscsi-targets" takes a colon separated list of iscsi targets');
+ self.asIscsiTargets = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes a colon separated list of disk formats');
+ self.asTests = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--guest-fs':
+ iArg += 1;
+ if iArg >= len(asArgs):
+ raise base.InvalidOption('The "--guest-fs" takes a colon separated list of filesystem identifiers');
+ self.asGuestFs = asArgs[iArg].split(':');
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+ if 'tst-debian' in self.asTestVMs:
+ self.asRsrcs.append('4.2/storage/debian.vdi');
+
+ return self.asRsrcs;
+
+ def actionConfig(self):
+ # Some stupid trickery to guess the location of the iso. ## fixme - testsuite unzip ++
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__),
+ '../../VBoxValidationKitStorIo.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__),
+ '../../VBoxValidationKitStorIo.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/validationkit/VBoxValidationKitStorIo.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/testsuite/VBoxTestSuiteStorIo.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sCur = os.getcwd();
+ for i in range(0, 10):
+ sVBoxValidationKit_iso = os.path.join(sCur, 'validationkit/VBoxValidationKitStorIo.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sVBoxValidationKit_iso = os.path.join(sCur, 'testsuite/VBoxTestSuiteStorIo.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sCur = os.path.abspath(os.path.join(sCur, '..'));
+ if i is None: pass; # shut up pychecker/pylint.
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/VirtualBox/VBoxValidationKitStorIo.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/VirtualBox/VBoxTestSuiteStorIo.iso';
+
+
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure the VMs we're going to use.
+ #
+
+ # Linux VMs
+ if 'tst-debian' in self.asTestVMs:
+ oVM = self.createTestVM('tst-debian', 1, '4.2/storage/debian.vdi', sKind = 'Debian_64', fIoApic = True, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ eNic0Type = vboxcon.NetworkAdapterType_Am79C973, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.test1();
+ return fRc;
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def test1RunTestProgs(self, oSession, oTxsSession, fRc, sTestName, sGuestFs):
+ """
+ Runs all the test programs on the test machine.
+ """
+ _ = oSession;
+
+ reporter.testStart(sTestName);
+
+ sMkfsCmd = 'mkfs.' + sGuestFs;
+
+ # Prepare test disks, just create filesystem without partition
+ reporter.testStart('Preparation');
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Create FS 1', 60000, \
+ '/sbin/' + sMkfsCmd,
+ (sMkfsCmd, '/dev/sdb'));
+
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Create FS 2', 60000, \
+ '/sbin/' + sMkfsCmd,
+ (sMkfsCmd, '/dev/sdc'));
+
+ # Create test and scratch directory
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Create /mnt/test', 10000, \
+ '/bin/mkdir',
+ ('mkdir', '/mnt/test'));
+
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Create /mnt/scratch', 10000, \
+ '/bin/mkdir',
+ ('mkdir', '/mnt/scratch'));
+
+ # Mount test and scratch directory.
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Mount /mnt/test', 10000, \
+ '/bin/mount',
+ ('mount', '/dev/sdb','/mnt/test'));
+
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Mount /mnt/scratch', 10000, \
+ '/bin/mount',
+ ('mount', '/dev/sdc','/mnt/scratch'));
+
+ fRc = fRc and self.txsRunTest(oTxsSession, 'Copying xfstests', 10000, \
+ '/bin/cp',
+ ('cp', '-r','${CDROM}/${OS.ARCH}/xfstests', '/tmp'));
+
+ reporter.testDone();
+
+ # Run xfstests (this sh + cd crap is required because the cwd for the script must be in the root
+ # of the xfstests directory...)
+ reporter.testStart('xfstests');
+ if fRc and 'xfstests' in self.asTests:
+ fRc = self.txsRunTest(oTxsSession, 'xfstests', 3600000,
+ '/bin/sh',
+ ('sh', '-c', '(cd /tmp/xfstests && ./check -g auto)'),
+ ('TEST_DIR=/mnt/test', 'TEST_DEV=/dev/sdb', 'SCRATCH_MNT=/mnt/scratch', 'SCRATCH_DEV=/dev/sdc',
+ 'FSTYP=' + sGuestFs));
+ reporter.testDone();
+ else:
+ reporter.testDone(fSkipped = True);
+
+ reporter.testDone(not fRc);
+ return fRc;
+
+ # pylint: disable=R0913
+
+ def test1OneCfg(self, sVmName, eStorageController, sDiskFormat, sDiskPath1, sDiskPath2, \
+ sGuestFs, cCpus, fHwVirt, fNestedPaging):
+ """
+ Runs the specified VM thru test #1.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ oVM = self.getVmByName(sVmName);
+
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ # Attach HD
+ fRc = oSession.ensureControllerAttached(_ControllerTypeToName(eStorageController));
+ fRc = fRc and oSession.setStorageControllerType(eStorageController, _ControllerTypeToName(eStorageController));
+
+ if sDiskFormat == "iSCSI":
+ listNames = [];
+ listValues = [];
+ listValues = sDiskPath1.split('|');
+ listNames.append('TargetAddress');
+ listNames.append('TargetName');
+ listNames.append('LUN');
+
+ if self.fpApiVer >= 5.0:
+ oHd = oSession.oVBox.createMedium(sDiskFormat, sDiskPath1, vboxcon.AccessMode_ReadWrite, \
+ vboxcon.DeviceType_HardDisk);
+ else:
+ oHd = oSession.oVBox.createHardDisk(sDiskFormat, sDiskPath1);
+ oHd.type = vboxcon.MediumType_Normal;
+ oHd.setProperties(listNames, listValues);
+
+ # Attach it.
+ if fRc is True:
+ try:
+ if oSession.fpApiVer >= 4.0:
+ oSession.o.machine.attachDevice(_ControllerTypeToName(eStorageController), \
+ 1, 0, vboxcon.DeviceType_HardDisk, oHd);
+ else:
+ oSession.o.machine.attachDevice(_ControllerTypeToName(eStorageController), \
+ 1, 0, vboxcon.DeviceType_HardDisk, oHd.id);
+ except:
+ reporter.errorXcpt('attachDevice("%s",%s,%s,HardDisk,"%s") failed on "%s"' \
+ % (_ControllerTypeToName(eStorageController), 1, 0, oHd.id, oSession.sName) );
+ fRc = False;
+ else:
+ reporter.log('attached "%s" to %s' % (sDiskPath1, oSession.sName));
+ else:
+ fRc = fRc and oSession.createAndAttachHd(sDiskPath1, sDiskFormat, _ControllerTypeToName(eStorageController), \
+ cb = 10*1024*1024*1024, iPort = 1, fImmutable = False);
+ fRc = fRc and oSession.createAndAttachHd(sDiskPath2, sDiskFormat, _ControllerTypeToName(eStorageController), \
+ cb = 10*1024*1024*1024, iPort = 2, fImmutable = False);
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(sVmName, fCdWait = False, fNatForwardingForTxs = True);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+
+ fRc = self.test1RunTestProgs(oSession, oTxsSession, fRc, 'stress testing', sGuestFs);
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+
+ # Remove disk
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ try:
+ oSession.o.machine.detachDevice(_ControllerTypeToName(eStorageController), 1, 0);
+ oSession.o.machine.detachDevice(_ControllerTypeToName(eStorageController), 2, 0);
+
+ # Remove storage controller if it is not an IDE controller.
+ if eStorageController is not vboxcon.StorageControllerType_PIIX3 \
+ and eStorageController is not vboxcon.StorageControllerType_PIIX4:
+ oSession.o.machine.removeStorageController(_ControllerTypeToName(eStorageController));
+
+ oSession.saveSettings();
+ oSession.oVBox.deleteHdByLocation(sDiskPath1);
+ oSession.oVBox.deleteHdByLocation(sDiskPath2);
+ oSession.saveSettings();
+ oSession.close();
+ oSession = None;
+ except:
+ reporter.errorXcpt('failed to detach/delete disks %s and %s from storage controller' % \
+ (sDiskPath1, sDiskPath2));
+ else:
+ fRc = False;
+ else:
+ fRc = False;
+ return fRc;
+
+ def test1OneVM(self, sVmName):
+ """
+ Runs one VM thru the various configurations.
+ """
+ reporter.testStart(sVmName);
+ fRc = True;
+ for sStorageCtrl in self.asStorageCtrls:
+ reporter.testStart(sStorageCtrl);
+
+ if sStorageCtrl == 'AHCI':
+ eStorageCtrl = vboxcon.StorageControllerType_IntelAhci;
+ elif sStorageCtrl == 'IDE':
+ eStorageCtrl = vboxcon.StorageControllerType_PIIX4;
+ elif sStorageCtrl == 'LsiLogicSAS':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogicSas;
+ elif sStorageCtrl == 'LsiLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_LsiLogic;
+ elif sStorageCtrl == 'BusLogic':
+ eStorageCtrl = vboxcon.StorageControllerType_BusLogic;
+ else:
+ eStorageCtrl = None;
+
+ for sDiskFormat in self.asDiskFormats:
+ reporter.testStart('%s' % (sDiskFormat,));
+
+ asPaths = self.asDirs;
+
+ for sDir in asPaths:
+ reporter.testStart('%s' % (sDir,));
+
+ sPathDisk1 = sDir + "/disk1.disk";
+ sPathDisk2 = sDir + "/disk2.disk";
+
+ for sGuestFs in self.asGuestFs:
+ reporter.testStart('%s' % (sGuestFs,));
+
+ for cCpus in self.acCpus:
+ if cCpus == 1: reporter.testStart('1 cpu');
+ else: reporter.testStart('%u cpus' % (cCpus,));
+
+ for sVirtMode in self.asVirtModes:
+ if sVirtMode == 'raw' and cCpus > 1:
+ continue;
+ hsVirtModeDesc = {};
+ hsVirtModeDesc['raw'] = 'Raw-mode';
+ hsVirtModeDesc['hwvirt'] = 'HwVirt';
+ hsVirtModeDesc['hwvirt-np'] = 'NestedPaging';
+ reporter.testStart(hsVirtModeDesc[sVirtMode]);
+
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+ fRc = self.test1OneCfg(sVmName, eStorageCtrl, sDiskFormat, sPathDisk1, sPathDisk2, \
+ sGuestFs, cCpus, fHwVirt, fNestedPaging) and fRc and True;
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ return fRc;
+
+ def test1(self):
+ """
+ Executes test #1.
+ """
+
+ # Loop thru the test VMs.
+ for sVM in self.asTestVMs:
+ # run test on the VM.
+ if not self.test1OneVM(sVM):
+ fRc = False;
+ else:
+ fRc = True;
+
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdStorageStress().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/teleportation/Makefile.kmk b/src/VBox/ValidationKit/tests/teleportation/Makefile.kmk
new file mode 100644
index 00000000..843391f1
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/teleportation/Makefile.kmk
@@ -0,0 +1,41 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Teleportation.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsTeleportation
+ValidationKitTestsTeleportation_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsTeleportation_INST = $(INST_VALIDATIONKIT)testcases/cpu/
+ValidationKitTestsTeleportation_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdTeleportLocal1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsTeleportation_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/teleportation/tdTeleportLocal1.py b/src/VBox/ValidationKit/tests/teleportation/tdTeleportLocal1.py
new file mode 100755
index 00000000..e55c373f
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/teleportation/tdTeleportLocal1.py
@@ -0,0 +1,953 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdTeleportLocal1.py $
+
+"""
+VirtualBox Validation Kit - Local teleportation testdriver.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+
+
+class tdTeleportLocal1(vbox.TestDriver):
+ """
+ Local Teleportation Test #1.
+ """
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+
+ self.asTestsDef = ['test1', 'test2'];
+ self.asTests = ['test1', 'test2'];
+ self.asSkipTests = [];
+ self.asTestVMsDef = ['tst-rhel5', 'tst-win2k3ent', 'tst-sol10'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw',]
+ self.asVirtModes = self.asVirtModesDef
+ self.acCpusDef = [1, 2,]
+ self.acCpus = self.acCpusDef;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdTeleportLocal1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --tests <test1[:test2[:...]]>');
+ reporter.log(' Run the specified tests.');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef)));
+ reporter.log(' --skip-tests <test1[:test2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --quick');
+ reporter.log(' Shorthand for: --virt-modes hwvirt --cpu-counts 1');
+ reporter.log(' --test-vms tst-rhel5:tst-win2k3ent:tst-sol10');
+ return rc;
+
+ def parseOption(self, asArgs, iArg):
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ if asArgs[iArg]:
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ else:
+ self.asTestVMs = [];
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--skip-vms" value "%s" does not specify any of our test VMs.' % (s));
+ elif asArgs[iArg] == '--tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--tests" takes colon separated list');
+ self.asTests = asArgs[iArg].split(':');
+ for s in self.asTests:
+ if s not in self.asTestsDef:
+ reporter.log('warning: The "--tests" value "%s" does not specify any of our tests.' % (s));
+ elif asArgs[iArg] == '--skip-tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-tests" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipTests:
+ if s not in self.asTestsDef:
+ reporter.log('warning: The "--skip-tests" value "%s" does not specify any of our tests.' % (s));
+ elif asArgs[iArg] == '--quick':
+ self.asVirtModes = ['hwvirt',];
+ self.acCpus = [1,];
+ #self.asTestVMs = ['tst-rhel5', 'tst-win2k3ent', 'tst-sol10',];
+ self.asTestVMs = ['tst-rhel5', ];
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test VM list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ # Remove skipped tests from the test list.
+ for sTest in self.asSkipTests:
+ try: self.asTests.remove(sTest);
+ except: pass;
+
+ # If no test2, then no test VMs.
+ if 'test2' not in self.asTests:
+ self.asTestVMs = [];
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+ if 'tst-rhel5' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/rhel5.vdi');
+ if 'tst-rhel5-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/rhel5-64.vdi');
+ if 'tst-sles11' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/sles11.vdi');
+ if 'tst-sles11-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/sles11-64.vdi');
+ if 'tst-oel' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/oel.vdi');
+ if 'tst-oel-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/oel-64.vdi');
+ if 'tst-win2k3ent' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k3ent-acpi.vdi');
+ if 'tst-win2k3ent-64' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k3ent-64.vdi');
+ if 'tst-win2k8' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/win2k8.vdi');
+ if 'tst-sol10' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/solaris10.vdi');
+ if 'tst-sol11' in self.asTestVMs:
+ self.asRsrcs.append('3.0/tcp/solaris11.vdi');
+ return self.asRsrcs;
+
+ def actionConfig(self):
+ ## @todo actionConfig() and getResourceSet() are working on the same
+ # set of VMs as tdNetBenchmark1, creating common base class would be
+ # a good idea.
+
+ # Some stupid trickery to guess the location of the iso. ## fixme - testsuite unzip ++
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxValidationKit.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxTestSuite.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/testsuite/VBoxTestSuite.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sCur = os.getcwd();
+ for i in range(0, 10):
+ sVBoxValidationKit_iso = os.path.join(sCur, 'validationkit/VBoxValidationKit.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sVBoxValidationKit_iso = os.path.join(sCur, 'testsuite/VBoxTestSuite.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sCur = os.path.abspath(os.path.join(sCur, '..'));
+ if i is not None: pass; # shut up pychecker/pylint.
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/testsuite/VBoxTestSuite.iso';
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure the empty VMs we're going to use for the first tests.
+ #
+
+ if 'test1' in self.asTests:
+ #oVM = self.createTestVMs('tst-empty-hwvirt', 0, sKind = 'Other', fVirtEx = True);
+ #if oVM is None:
+ # return False;
+
+ oVM = self.createTestVMs('tst-empty-raw', 2, sKind = 'Other', fVirtEx = False);
+ if oVM is None:
+ return False;
+
+ #
+ # Configure the VMs we're going to use for the last test.
+ #
+
+ # Linux VMs
+ if 'tst-rhel5' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-rhel5', 1, '3.0/tcp/rhel5.vdi', sKind = 'RedHat', fIoApic = True, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-rhel5-64' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-rhel5-64', 1, '3.0/tcp/rhel5-64.vdi', sKind = 'RedHat_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sles11' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-sles11', 1, '3.0/tcp/sles11.vdi', sKind = 'OpenSUSE', fIoApic = True, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sles11-64' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-sles11-64', 1, '3.0/tcp/sles11-64.vdi', sKind = 'OpenSUSE_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-oel' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-oel', 1, '3.0/tcp/oel.vdi', sKind = 'Oracle', fIoApic = True, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-oel-64' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-oel-64', 1, '3.0/tcp/oel-64.vdi', sKind = 'Oracle_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ # Windows VMs
+ if 'tst-win2k3ent' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-win2k3ent', 1, '3.0/tcp/win2k3ent-acpi.vdi', sKind = 'Windows2003', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-win2k3ent-64' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-win2k3ent-64', 1, '3.0/tcp/win2k3ent-64.vdi', sKind = 'Windows2003_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-win2k8' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-win2k8', 1, '3.0/tcp/win2k8.vdi', sKind = 'Windows2008_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ # Solaris VMs
+ if 'tst-sol10' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-sol10', 1, '3.0/tcp/solaris10.vdi', sKind = 'Solaris_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ if 'tst-sol11' in self.asTestVMs:
+ oVM = self.createTestVMs('tst-sol11', 1, '3.0/tcp/os2009-11.vdi', sKind = 'Solaris_64', \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = 'test1' not in self.asTests or self.test1();
+ if fRc: fRc = 'test2' not in self.asTests or self.test2();
+ return fRc;
+
+
+ #
+ # Test config helpers.
+ #
+
+ def createTestVMs(self, sName, iGroup, *tArgs, **dKeywordArgs):
+ """
+ Wrapper around vbox.createTestVM for creating two VMs, the source
+ (sName-1) and target (sName-2).
+
+ Returns the 2nd VM object on success, None on failure.
+ """
+ sName1 = sName + '-1';
+ oVM = self.createTestVM(sName1, iGroup * 2, *tArgs, **dKeywordArgs);
+ if oVM is not None:
+ sName2 = sName + '-2';
+ oVM = self.createTestVM(sName2, iGroup * 2 + 1, *tArgs, **dKeywordArgs);
+ return oVM;
+
+
+ #
+ # Test execution helpers.
+ #
+
+ def test2Teleport(self, oVmSrc, oSessionSrc, oVmDst):
+ """
+ Attempts a teleportation.
+
+ Returns the input parameters for the next test2Teleport call (source
+ and destiation are switched around). The input session is closed and
+ removed from the task list, while the return session is in the list.
+ """
+
+ # Enable the teleporter of the VM.
+ oSession = self.openSession(oVmDst);
+ fRc = oSession is not None
+ if fRc:
+ fRc = oSession.enableTeleporter();
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ if fRc:
+ # Start the destination VM.
+ oSessionDst, oProgressDst = self.startVmEx(oVmDst, fWait = False);
+ if oSessionDst is not None:
+ if oProgressDst.waitForOperation(iOperation = -3) == 0:
+
+ # Do the teleportation.
+ try:
+ uDstPort = oVmDst.teleporterPort;
+ except:
+ reporter.logXcpt();
+ uDstPort = 6502;
+ oProgressSrc = oSessionSrc.teleport('localhost', uDstPort, 'password', 1024);
+ if oProgressSrc is not None:
+ oProgressSrc.wait();
+ if oProgressSrc.isSuccess():
+ oProgressDst.wait();
+ if oProgressSrc.isSuccess() and oProgressDst.isSuccess():
+
+ # Terminate the source VM.
+ self.terminateVmBySession(oSessionSrc, oProgressSrc);
+
+ # Return with the source and destination swapped.
+ return oVmDst, oSessionDst, oVmSrc;
+
+ # Failure / bail out.
+ oProgressSrc.logResult();
+ oProgressDst.logResult();
+ self.terminateVmBySession(oSessionDst, oProgressDst);
+ return oVmSrc, oSessionSrc, oVmDst;
+
+ def test2OneCfg(self, sVmBaseName, cCpus, fHwVirt, fNestedPaging):
+ """
+ Runs the specified VM thru test #1.
+ """
+
+ # Reconfigure the source VM.
+ oVmSrc = self.getVmByName(sVmBaseName + '-1');
+ fRc = True;
+ oSession = self.openSession(oVmSrc);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.setupTeleporter(False, uPort=6501, sPassword='password');
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Reconfigure the destination VM.
+ oVmDst = self.getVmByName(sVmBaseName + '-2');
+ oSession = self.openSession(oVmDst);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(fHwVirt);
+ fRc = fRc and oSession.enableNestedPaging(fNestedPaging);
+ fRc = fRc and oSession.setCpuCount(cCpus);
+ fRc = fRc and oSession.setupTeleporter(True, uPort=6502, sPassword='password');
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Simple test.
+ if fRc is True:
+ self.logVmInfo(oVmSrc);
+ self.logVmInfo(oVmDst);
+
+ # Start the source VM.
+ oSessionSrc = self.startVm(oVmSrc);
+ if oSessionSrc is not None:
+ # Simple back and forth to test the ice...
+ cTeleportations = 0;
+ oVmSrc, oSessionSrc, oVmDst = self.test2Teleport(oVmSrc, oSessionSrc, oVmDst);
+ if reporter.testErrorCount() == 0:
+ cTeleportations += 1;
+ oVmSrc, oSessionSrc, oVmDst = self.test2Teleport(oVmSrc, oSessionSrc, oVmDst);
+
+ # Teleport back and forth for a while.
+ msStart = base.timestampMilli();
+ while reporter.testErrorCount() == 0:
+ cTeleportations += 1;
+ if oSessionSrc.txsTryConnectViaTcp(2500, 'localhost') is True:
+ break;
+ oVmSrc, oSessionSrc, oVmDst = self.test2Teleport(oVmSrc, oSessionSrc, oVmDst);
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > 5*60000:
+ reporter.testFailure('TXS did not show up after %u min of teleporting (%u)...' \
+ % (cMsElapsed / 60000.0, cTeleportations));
+ break;
+
+ # Clean up the source VM.
+ self.terminateVmBySession(oSessionSrc)
+ return None;
+
+ def test2OneVM(self, sVmBaseName, asSupVirtModes = None, rSupCpus = range(1, 256)):
+ """
+ Runs one VM (a pair really) thru the various configurations.
+ """
+ if asSupVirtModes is None:
+ asSupVirtModes = self.asVirtModes;
+
+ reporter.testStart(sVmBaseName);
+ for cCpus in self.acCpus:
+ if cCpus == 1: reporter.testStart('1 cpu');
+ else: reporter.testStart('%u cpus' % (cCpus));
+
+ for sVirtMode in self.asVirtModes:
+ if sVirtMode == 'raw' and cCpus > 1:
+ continue;
+ if cCpus not in rSupCpus:
+ continue;
+ if sVirtMode not in asSupVirtModes:
+ continue;
+ hsVirtModeDesc = {};
+ hsVirtModeDesc['raw'] = 'Raw-mode';
+ hsVirtModeDesc['hwvirt'] = 'HwVirt';
+ hsVirtModeDesc['hwvirt-np'] = 'NestedPaging';
+ reporter.testStart(hsVirtModeDesc[sVirtMode]);
+
+ fHwVirt = sVirtMode != 'raw';
+ fNestedPaging = sVirtMode == 'hwvirt-np';
+ self.test2OneCfg(sVmBaseName, cCpus, fHwVirt, fNestedPaging);
+
+ reporter.testDone();
+ reporter.testDone();
+ return reporter.testDone()[1] == 0;
+
+ def test2(self):
+ """
+ Executes test #2.
+ """
+
+ # Loop thru the test VMs.
+ fRc = True;
+ for sVM in self.asTestVMs:
+ # figure args.
+ asSupVirtModes = None;
+ if sVM in ('tst-sol11', 'tst-sol10'): # 64-bit only
+ asSupVirtModes = ['hwvirt', 'hwvirt-np',];
+
+ # run test on the VM.
+ if not self.test2OneVM(sVM, asSupVirtModes):
+ fRc = False;
+
+ return fRc;
+ #
+ # Test #1
+ #
+
+ def test1ResetVmConfig(self, oVM, fTeleporterEnabled = False):
+ """
+ Resets the teleportation config for the specified VM.
+ Returns True on success, False on failure.
+ """
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = oSession.setupTeleporter(fTeleporterEnabled, uPort=6502, sPassword='password');
+ fRc = fRc and oSession.saveSettings();
+ if not oSession.close(): fRc = False;
+ oSession = None;
+ else:
+ fRc = False;
+ return fRc;
+
+ def test1Sub7(self, oVmSrc, oVmDst):
+ """
+ Test the password check.
+ """
+ reporter.testStart('Bad password');
+ if self.test1ResetVmConfig(oVmSrc, fTeleporterEnabled = False) \
+ and self.test1ResetVmConfig(oVmDst, fTeleporterEnabled = True):
+ # Start the target VM.
+ oSessionDst, oProgressDst = self.startVmEx(oVmDst, fWait = False);
+ if oSessionDst is not None:
+ if oProgressDst.waitForOperation(iOperation = -3) == 0:
+ # Start the source VM.
+ oSessionSrc = self.startVm(oVmSrc);
+ if oSessionSrc is not None:
+ tsPasswords = ('password-bad', 'passwor', 'pass', 'p', '', 'Password', );
+ for sPassword in tsPasswords:
+ reporter.testStart(sPassword);
+ oProgressSrc = oSessionSrc.teleport('localhost', 6502, sPassword);
+ if oProgressSrc:
+ oProgressSrc.wait();
+ reporter.log('src: %s' % oProgressSrc.stringifyResult());
+ if oProgressSrc.isSuccess():
+ reporter.testFailure('IConsole::teleport succeeded with bad password "%s"' % sPassword);
+ elif oProgressSrc.getErrInfoResultCode() != vbox.ComError.E_FAIL:
+ reporter.testFailure('IConsole::teleport returns %s instead of E_FAIL' \
+ % (vbox.ComError.toString(oProgressSrc.getErrInfoResultCode()),));
+ elif oProgressSrc.getErrInfoText() != 'Invalid password':
+ reporter.testFailure('IConsole::teleport returns "%s" instead of "Invalid password"' \
+ % (oProgressSrc.getErrInfoText(),));
+ elif oProgressDst.isCompleted():
+ reporter.testFailure('Destination completed unexpectedly after bad password "%s"' \
+ % sPassword);
+ else:
+ reporter.testFailure('IConsole::teleport failed with password "%s"' % sPassword);
+ if reporter.testDone()[1] != 0:
+ break;
+ self.terminateVmBySession(oSessionSrc, oProgressSrc);
+ self.terminateVmBySession(oSessionDst, oProgressDst);
+ else:
+ reporter.testFailure('reconfig failed');
+ return reporter.testDone()[1] == 0;
+
+ def test1Sub6(self, oVmSrc, oVmDst):
+ """
+ Misconfigure the target VM and check that teleportation fails with the
+ same status and message on both ends.
+ xTracker: #4813
+ """
+ reporter.testStart('Misconfiguration & error message');
+ if self.test1ResetVmConfig(oVmSrc, fTeleporterEnabled = False) \
+ and self.test1ResetVmConfig(oVmDst, fTeleporterEnabled = True):
+ # Give the source a bit more RAM.
+ oSession = self.openSession(oVmSrc);
+ if oSession is not None:
+ try: cbMB = oVmSrc.memorySize + 4;
+ except: cbMB = 1; fRc = False;
+ fRc = oSession.setRamSize(cbMB);
+ if not oSession.saveSettings(): fRc = False;
+ if not oSession.close(): fRc = False;
+ oSession = None;
+ else:
+ fRc = False;
+ if fRc:
+ # Start the target VM.
+ oSessionDst, oProgressDst = self.startVmEx(oVmDst, fWait = False);
+ if oSessionDst is not None:
+ if oProgressDst.waitForOperation(iOperation = -3) == 0:
+ # Start the source VM.
+ oSessionSrc = self.startVm(oVmSrc);
+ if oSessionSrc is not None:
+ # Try teleport.
+ oProgressSrc = oSessionSrc.teleport('localhost', 6502, 'password');
+ if oProgressSrc:
+ oProgressSrc.wait();
+ oProgressDst.wait();
+
+ reporter.log('src: %s' % oProgressSrc.stringifyResult());
+ reporter.log('dst: %s' % oProgressDst.stringifyResult());
+
+ # Make sure it failed.
+ if oProgressSrc.isSuccess() and oProgressDst.isSuccess():
+ reporter.testFailure('The teleporation did not fail as expected');
+
+ # Compare the results.
+ if oProgressSrc.getResult() != oProgressDst.getResult():
+ reporter.testFailure('Result differs - src=%s dst=%s' \
+ % (vbox.ComError.toString(oProgressSrc.getResult()),\
+ vbox.ComError.toString(oProgressDst.getResult())));
+ elif oProgressSrc.getErrInfoResultCode() != oProgressDst.getErrInfoResultCode():
+ reporter.testFailure('ErrorInfo::resultCode differs - src=%s dst=%s' \
+ % (vbox.ComError.toString(oProgressSrc.getErrInfoResultCode()),\
+ vbox.ComError.toString(oProgressDst.getErrInfoResultCode())));
+ elif oProgressSrc.getErrInfoText() != oProgressDst.getErrInfoText():
+ reporter.testFailure('ErrorInfo::text differs - src="%s" dst="%s"' \
+ % (oProgressSrc.getErrInfoText(), oProgressDst.getErrInfoText()));
+
+ self.terminateVmBySession(oSessionSrc, oProgressSrc);
+ self.terminateVmBySession(oSessionDst, oProgressDst);
+ self.test1ResetVmConfig(oVmSrc, fTeleporterEnabled = False)
+ self.test1ResetVmConfig(oVmDst, fTeleporterEnabled = True);
+ else:
+ reporter.testFailure('reconfig #2 failed');
+ else:
+ reporter.testFailure('reconfig #1 failed');
+ return reporter.testDone()[1] == 0;
+
+ def test1Sub5(self, oVmSrc, oVmDst):
+ """
+ Test that basic teleporting works.
+ xTracker: #4749
+ """
+ reporter.testStart('Simple teleportation');
+ for cSecsX2 in range(0, 10):
+ if self.test1ResetVmConfig(oVmSrc, fTeleporterEnabled = False) \
+ and self.test1ResetVmConfig(oVmDst, fTeleporterEnabled = True):
+ # Start the target VM.
+ oSessionDst, oProgressDst = self.startVmEx(oVmDst, fWait = False);
+ if oSessionDst is not None:
+ if oProgressDst.waitForOperation(iOperation = -3) == 0:
+ # Start the source VM.
+ oSessionSrc = self.startVm(oVmSrc);
+ if oSessionSrc is not None:
+ self.sleep(cSecsX2 / 2);
+ # Try teleport.
+ oProgressSrc = oSessionSrc.teleport('localhost', 6502, 'password');
+ if oProgressSrc:
+ oProgressSrc.wait();
+ oProgressDst.wait();
+
+ self.terminateVmBySession(oSessionSrc, oProgressSrc);
+ self.terminateVmBySession(oSessionDst, oProgressDst);
+ else:
+ reporter.testFailure('reconfig failed');
+ return reporter.testDone()[1] == 0;
+
+ def test1Sub4(self, oVM):
+ """
+ Test that we can start and cancel a teleportation target.
+ (No source VM trying to connect here.)
+ xTracker: #4965
+ """
+ reporter.testStart('openRemoteSession cancel');
+ for cSecsX2 in range(0, 10):
+ if self.test1ResetVmConfig(oVM, fTeleporterEnabled = True):
+ oSession, oProgress = self.startVmEx(oVM, fWait = False);
+ if oSession is not None:
+ self.sleep(cSecsX2 / 2);
+ oProgress.cancel();
+ oProgress.wait();
+ self.terminateVmBySession(oSession, oProgress);
+ else:
+ reporter.testFailure('reconfig failed');
+ return reporter.testDone()[1] == 0;
+
+ def test1Sub3(self, oVM):
+ """
+ Test that starting a teleportation target VM will fail if we give it
+ a bad address to bind to.
+ """
+ reporter.testStart('bad IMachine::teleporterAddress');
+
+ # re-configure it with a bad bind-to address.
+ fRc = False;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = oSession.setupTeleporter(True, uPort=6502, sAddress='no.such.hostname.should.ever.exist.duh');
+ if not oSession.saveSettings(fClose=True): fRc = False;
+ oSession = None;
+ if fRc:
+ # Try start it.
+ oSession, oProgress = self.startVmEx(oVM, fWait = False);
+ if oSession is not None:
+ oProgress.wait();
+ ## TODO: exact error code and look for the IPRT right string.
+ if not oProgress.isCompleted() or oProgress.getResult() >= 0:
+ reporter.testFailure('%s' % (oProgress.stringifyResult(),));
+ self.terminateVmBySession(oSession, oProgress);
+
+ # put back the old teleporter setup.
+ self.test1ResetVmConfig(oVM, fTeleporterEnabled = True);
+ else:
+ reporter.testFailure('reconfig #1 failed');
+ return reporter.testDone()[1] == 0;
+
+ # test1Sub2 - start
+
+ def test1Sub2SetEnabled(self, oSession, fEnabled):
+ """ This should never fail."""
+ try:
+ oSession.o.machine.teleporterEnabled = fEnabled;
+ except:
+ reporter.testFailureXcpt('machine.teleporterEnabled=%s' % (fEnabled,));
+ return False;
+ try:
+ fNew = oSession.o.machine.teleporterEnabled;
+ except:
+ reporter.testFailureXcpt();
+ return False;
+ if fNew != fEnabled:
+ reporter.testFailure('machine.teleporterEnabled=%s but afterwards it is actually %s' % (fEnabled, fNew));
+ return False;
+ return True;
+
+ def test1Sub2SetPassword(self, oSession, sPassword):
+ """ This should never fail."""
+ try:
+ oSession.o.machine.teleporterPassword = sPassword;
+ except:
+ reporter.testFailureXcpt('machine.teleporterPassword=%s' % (sPassword,));
+ return False;
+ try:
+ sNew = oSession.o.machine.teleporterPassword;
+ except:
+ reporter.testFailureXcpt();
+ return False;
+ if sNew != sPassword:
+ reporter.testFailure('machine.teleporterPassword="%s" but afterwards it is actually "%s"' % (sPassword, sNew));
+ return False;
+ return True;
+
+ def test1Sub2SetPort(self, oSession, uPort, fInvalid = False):
+ """ This can fail, thus fInvalid."""
+ if not fInvalid:
+ uOld = uPort;
+ else:
+ try: uOld = oSession.o.machine.teleporterPort;
+ except: return reporter.testFailureXcpt();
+
+ try:
+ oSession.o.machine.teleporterPort = uPort;
+ except Exception as oXcpt:
+ if not fInvalid or vbox.ComError.notEqual(oXcpt, vbox.ComError.E_INVALIDARG):
+ return reporter.testFailureXcpt('machine.teleporterPort=%u' % (uPort,));
+ else:
+ if fInvalid:
+ return reporter.testFailureXcpt('machine.teleporterPort=%u succeeded unexpectedly' % (uPort,));
+
+ try: uNew = oSession.o.machine.teleporterPort;
+ except: return reporter.testFailureXcpt();
+ if uNew != uOld:
+ if not fInvalid:
+ reporter.testFailure('machine.teleporterPort=%u but afterwards it is actually %u' % (uPort, uNew));
+ else:
+ reporter.testFailure('machine.teleporterPort is %u after failure, expected %u' % (uNew, uOld));
+ return False;
+ return True;
+
+ def test1Sub2SetAddress(self, oSession, sAddress):
+ """ This should never fail."""
+ try:
+ oSession.o.machine.teleporterAddress = sAddress;
+ except:
+ reporter.testFailureXcpt('machine.teleporterAddress=%s' % (sAddress,));
+ return False;
+ try:
+ sNew = oSession.o.machine.teleporterAddress;
+ except:
+ reporter.testFailureXcpt();
+ return False;
+ if sNew != sAddress:
+ reporter.testFailure('machine.teleporterAddress="%s" but afterwards it is actually "%s"' % (sAddress, sNew));
+ return False;
+ return True;
+
+ def test1Sub2(self, oVM):
+ """
+ Test the attributes, making sure that we get exceptions on bad values.
+ """
+ reporter.testStart('IMachine::teleport*');
+
+ # Save the original teleporter attributes for the discard test.
+ try:
+ sOrgAddress = oVM.teleporterAddress;
+ uOrgPort = oVM.teleporterPort;
+ sOrgPassword = oVM.teleporterPassword;
+ fOrgEnabled = oVM.teleporterEnabled;
+ except:
+ reporter.testFailureXcpt();
+ else:
+ # Open a session and start messing with the properties.
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ # Anything goes for the address.
+ reporter.testStart('teleporterAddress');
+ self.test1Sub2SetAddress(oSession, '');
+ self.test1Sub2SetAddress(oSession, '1');
+ self.test1Sub2SetAddress(oSession, 'Anything goes! ^&$@!$%^');
+ reporter.testDone();
+
+ # The port is restricted to {0..65535}.
+ reporter.testStart('teleporterPort');
+ for uPort in range(0, 1000) + range(16000, 17000) + range(32000, 33000) + range(65000, 65536):
+ if not self.test1Sub2SetPort(oSession, uPort):
+ break;
+ self.processPendingEvents();
+ reporter.testDone();
+
+ reporter.testStart('teleporterPort negative');
+ self.test1Sub2SetPort(oSession, 65536, True);
+ self.test1Sub2SetPort(oSession, 999999, True);
+ reporter.testDone();
+
+ # Anything goes for the password.
+ reporter.testStart('teleporterPassword');
+ self.test1Sub2SetPassword(oSession, 'password');
+ self.test1Sub2SetPassword(oSession, '');
+ self.test1Sub2SetPassword(oSession, '1');
+ self.test1Sub2SetPassword(oSession, 'Anything goes! ^&$@!$%^');
+ reporter.testDone();
+
+ # Just test that it works.
+ reporter.testStart('teleporterEnabled');
+ self.test1Sub2SetEnabled(oSession, True);
+ self.test1Sub2SetEnabled(oSession, True);
+ self.test1Sub2SetEnabled(oSession, False);
+ self.test1Sub2SetEnabled(oSession, False);
+ reporter.testDone();
+
+ # Finally, discard the changes, close the session and check
+ # that we're back to the originals.
+ if not oSession.discardSettings(True):
+ reporter.testFailure('Failed to discard settings & close the session')
+ else:
+ reporter.testFailure('Failed to open VM session')
+ try:
+ if oVM.teleporterAddress != sOrgAddress: reporter.testFailure('Rollback failed for teleporterAddress');
+ if oVM.teleporterPort != uOrgPort: reporter.testFailure('Rollback failed for teleporterPort');
+ if oVM.teleporterPassword != sOrgPassword: reporter.testFailure('Rollback failed for teleporterPassword');
+ if oVM.teleporterEnabled != fOrgEnabled: reporter.testFailure('Rollback failed for teleporterEnabled');
+ except:
+ reporter.testFailureXcpt();
+ return reporter.testDone()[1] != 0;
+
+ # test1Sub1 - start
+
+ def test1Sub1DoTeleport(self, oSession, sHostname, uPort, sPassword, cMsMaxDowntime, hrcExpected, sTestName):
+ """ Do a bad IConsole::teleport call and check the result."""
+ reporter.testStart(sTestName);
+ fRc = False;
+ try:
+ oProgress = oSession.o.console.teleport(sHostname, uPort, sPassword, cMsMaxDowntime);
+ except vbox.ComException as oXcpt:
+ if vbox.ComError.equal(oXcpt, hrcExpected):
+ fRc = True;
+ else:
+ reporter.testFailure('hresult %s, expected %s' \
+ % (vbox.ComError.toString(oXcpt.hresult),
+ vbox.ComError.toString(hrcExpected)));
+ except Exception as oXcpt:
+ reporter.testFailure('Unexpected exception %s' % (oXcpt));
+ else:
+ reporter.testFailure('Unpexected success');
+ oProgress.cancel();
+ oProgress.wait();
+ reporter.testDone();
+ return fRc;
+
+ def test1Sub1(self, oVM):
+ """ Test simple IConsole::teleport() failure paths. """
+ reporter.testStart('IConsole::teleport');
+ oSession = self.startVm(oVM);
+ if oSession:
+ self.test1Sub1DoTeleport(oSession, 'localhost', 65536, 'password', 10000,
+ vbox.ComError.E_INVALIDARG, 'Bad port value 65536');
+ self.test1Sub1DoTeleport(oSession, 'localhost', 0, 'password', 10000,
+ vbox.ComError.E_INVALIDARG, 'Bad port value 0');
+ self.test1Sub1DoTeleport(oSession, 'localhost', 5000, 'password', 0,
+ vbox.ComError.E_INVALIDARG, 'Bad max downtime');
+ self.test1Sub1DoTeleport(oSession, '', 5000, 'password', 10000,
+ vbox.ComError.E_INVALIDARG, 'No hostname');
+ self.test1Sub1DoTeleport(oSession, 'no.such.hostname.should.ever.exist.duh', 5000, 'password', 0,
+ vbox.ComError.E_INVALIDARG, 'Non-existing host');
+
+ self.terminateVmBySession(oSession)
+ else:
+ reporter.testFailure('startVm');
+ return reporter.testDone()[1] == 0;
+
+
+ def test1(self):
+ """
+ Executes test #1 - Negative API testing.
+
+ ASSUMES that the VMs are
+ """
+ reporter.testStart('Test 1');
+
+ # Get the VMs.
+ #oVmHwVirt1 = self.getVmByName('tst-empty-hwvirt-1');
+ #oVmHwVirt2 = self.getVmByName('tst-empty-hwvirt-2');
+ oVmRaw1 = self.getVmByName('tst-empty-raw-1');
+ oVmRaw2 = self.getVmByName('tst-empty-raw-2');
+
+ # Reset their teleportation related configuration.
+ fRc = True;
+ #for oVM in (oVmHwVirt1, oVmHwVirt2, oVmRaw1, oVmRaw2):
+ for oVM in (oVmRaw1, oVmRaw2):
+ if not self.test1ResetVmConfig(oVM): fRc = False;
+
+ # Do the testing (don't both with fRc on the subtests).
+ if fRc:
+ self.test1Sub1(oVmRaw1);
+ self.test1Sub2(oVmRaw2);
+ self.test1Sub3(oVmRaw2);
+ self.test1Sub4(oVmRaw2);
+ self.processPendingEvents();
+ self.test1Sub5(oVmRaw1, oVmRaw2);
+ self.test1Sub6(oVmRaw1, oVmRaw2);
+ self.test1Sub7(oVmRaw1, oVmRaw2);
+ else:
+ reporter.testFailure('Failed to reset the VM configs')
+ return reporter.testDone()[1] == 0;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdTeleportLocal1().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/unittests/Makefile.kmk b/src/VBox/ValidationKit/tests/unittests/Makefile.kmk
new file mode 100644
index 00000000..b1c76696
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/unittests/Makefile.kmk
@@ -0,0 +1,41 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - Unit Tests.
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsUnitTests
+ValidationKitTestsUnitTests_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsUnitTests_INST = $(INST_VALIDATIONKIT)tests/unittests/
+ValidationKitTestsUnitTests_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdUnitTest1.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsUnitTests_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/unittests/tdUnitTest1.py b/src/VBox/ValidationKit/tests/unittests/tdUnitTest1.py
new file mode 100755
index 00000000..d4435f90
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/unittests/tdUnitTest1.py
@@ -0,0 +1,774 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdUnitTest1.py $
+
+"""
+VirtualBox Validation Kit - Unit Tests.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os
+import sys
+import re
+
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(g_ksValidationKitDir)
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import vbox
+from testdriver import reporter
+
+
+class tdUnitTest1(vbox.TestDriver):
+ """
+ Unit Tests.
+ """
+
+ ## The temporary exclude list.
+ ## @note This shall be empty before we release 4.3!
+ kdTestCasesBuggyPerOs = {
+ 'darwin': {
+ 'testcase/tstX86-1': '', # 'FSTP M32R, ST0' fails; no idea why.
+ },
+ 'linux': {
+ 'testcase/tstRTFileAio': '', # See xTracker #8035.
+ },
+ 'linux.amd64': {
+ 'testcase/tstLdr-4': '', # failed: Failed to get bits for '/home/vbox/test/tmp/bin/testcase/tstLdrObjR0.r0'/0,
+ # rc=VERR_SYMBOL_VALUE_TOO_BIG. aborting test
+ },
+ 'solaris': {
+ 'testcase/tstIntNet-1': '', # Fails opening rge0, probably a generic issue figuring which nic to use.
+ 'testcase/tstIprtList': '', # Crashes in the multithreaded test, I think.
+ 'testcase/tstRTCritSect': '', # Fairness/whatever issue here.
+ 'testcase/tstRTR0MemUserKernelDriver': '', # Failes when kernel to kernel buffers.
+ 'testcase/tstRTSemRW': '', # line 338: RTSemRWReleaseRead(hSemRW): got VERR_ACCESS_DENIED
+ 'testcase/tstRTStrAlloc': '', # VERR_NO_STR_MEMORY!
+ 'testcase/tstRTFileGetSize-1': '', # VERR_DEV_IO_ERROR on /dev/null!
+ },
+ 'solaris.amd64': {
+ 'testcase/tstLdr-4': '', # failed: Failed to get bits for '/home/vbox/test/tmp/bin/testcase/tstLdrObjR0.r0'/0,
+ # rc=VERR_SYMBOL_VALUE_TOO_BIG. aborting test
+ },
+ 'win': {
+ 'testcase/tstFile': '', # ??
+ 'testcase/tstIntNet-1': '', # possibly same issue as solaris.
+ 'testcase/tstMouseImpl': '', # STATUS_ACCESS_VIOLATION
+ 'testcase/tstRTR0ThreadPreemptionDriver': '', # ??
+ 'testcase/tstRTPath': '<4.3.51r89894',
+ 'testcase/tstRTPipe': '', # ??
+ 'testcase/tstRTR0MemUserKernelDriver': '', # ??
+ 'testcase/tstRTR0SemMutexDriver': '', # ??
+ 'testcase/tstRTStrAlloc': '', # ??
+ 'testcase/tstRTStrFormat': '', # ??
+ 'testcase/tstRTSystemQueryOsInfo': '', # ??
+ 'testcase/tstRTTemp': '', # ??
+ 'testcase/tstRTTime': '', # ??
+ 'testcase/tstTime-2': '', # Total time differs too much! ... delta=-10859859
+ 'testcase/tstUtf8': '', # ??
+ 'testcase/tstVMMR0CallHost-2': '', # STATUS_STACK_OVERFLOW
+ 'testcase/tstX86-1': '', # Fails on win.x86.
+ 'tscpasswd': '', # ??
+ 'tstVMREQ': '', # ?? Same as darwin.x86?
+ },
+ 'win.x86': {
+ 'testcase/tstRTR0TimerDriver': '', # See xTracker #8041.
+ }
+ };
+
+ kdTestCasesBuggy = {
+ 'testcase/tstGuestPropSvc': '', # GET_NOTIFICATION fails on testboxlin5.de.oracle.com and others.
+ 'testcase/tstRTProcCreateEx': '', # Seen failing on wei01-b6ka-9.de.oracle.com.
+ 'testcase/tstTimer': '', # Sometimes fails on linux, not important atm.
+ 'testcase/tstGIP-2': '', # 2015-09-10: Fails regularly. E.g. TestSetID 2744205 (testboxsh2),
+ # 2743961 (wei01-b6kc-6). The responsible engineer should reenable
+ # it once it has been fixed.
+ };
+
+ ## The permanent exclude list.
+ # @note Stripped of extensions!
+ kdTestCasesBlackList = {
+ 'testcase/tstClipboardX11Smoke': '',
+ 'testcase/tstFileLock': '',
+ 'testcase/tstDisasm-2': '', # without parameters it will disassembler 1GB starting from 0
+ 'testcase/tstFileAppendWin-1': '',
+ 'testcase/tstDir': '', # useless without parameters
+ 'testcase/tstDir-2': '', # useless without parameters
+ 'testcase/tstGlobalConfig': '',
+ 'testcase/tstHostHardwareLinux': '', # must be killed with CTRL-C
+ 'testcase/tstHttp': '', # Talks to outside servers.
+ 'testcase/tstRTHttp': '', # parameters required
+ 'testcase/tstLdr-2': '', # parameters required
+ 'testcase/tstLdr-3': '', # parameters required
+ 'testcase/tstLdr': '', # parameters required
+ 'testcase/tstLdrLoad': '', # parameters required
+ 'testcase/tstMove': '', # parameters required
+ 'testcase/tstRTR0Timer': '', # loads 'tstRTR0Timer.r0'
+ 'testcase/tstRTR0ThreadDriver': '', # loads 'tstRTR0Thread.r0'
+ 'testcase/tstRunTestcases': '', # that's a script like this one
+ 'testcase/tstRTReqPool': '', # fails sometimes, testcase buggy
+ 'testcase/tstRTS3': '', # parameters required
+ 'testcase/tstSDL': '', # graphics test
+ 'testcase/tstSupLoadModule': '', # Needs parameters and vboxdrv access. Covered elsewhere.
+ 'testcase/tstSeamlessX11': '', # graphics test
+ 'testcase/tstTime-3': '', # parameters required
+ 'testcase/tstVBoxControl': '', # works only inside a guest
+ 'testcase/tstVDCopy': '', # parameters required
+ 'testcase/tstVDFill': '', # parameters required
+ 'tstAnimate': '', # parameters required
+ 'testcase/tstAPI': '', # user interaction required
+ 'tstCollector': '', # takes forever
+ 'testcase/tstHeadless': '', # parameters required
+ 'tstHeadless': '', # parameters required
+ 'tstMicroRC': '', # required by tstMicro
+ 'tstVBoxDbg': '', # interactive test
+ 'testcase/tstTestServMgr': '', # some strange xpcom18a4 test, does not work
+ 'tstTestServMgr': '', # some strange xpcom18a4 test, does not work
+ 'tstPDMAsyncCompletion': '', # parameters required
+ 'testcase/tstXptDump': '', # parameters required
+ 'tstXptDump': '', # parameters required
+ 'testcase/tstnsIFileEnumerator': '', # some strange xpcom18a4 test, does not work
+ 'tstnsIFileEnumerator': '', # some strange xpcom18a4 test, does not work
+ 'testcase/tstSimpleTypeLib': '', # parameters required
+ 'tstSimpleTypeLib': '', # parameters required
+ 'testcase/tstTestAtoms': '', # additional test file (words.txt) required
+ 'tstTestAtoms': '', # additional test file (words.txt) required
+ 'testcase/tstXptLink': '', # parameters required
+ 'tstXptLink': '', # parameters required
+ 'tstXPCOMCGlue': '', # user interaction required
+ 'testcase/tstXPCOMCGlue': '', # user interaction required
+ 'testcase/tstCAPIGlue': '', # user interaction required
+ 'testcase/tstTestCallTemplates': '', # some strange xpcom18a4 test, segfaults
+ 'tstTestCallTemplates': '', # some strange xpcom18a4 test, segfaults
+ 'testcase/tstRTFilesystem': '', # parameters required
+ 'testcase/tstRTDvm': '', # parameters required
+ # later
+ 'testcase/tstIntNetR0': '', # RTSPINLOCK_FLAGS_INTERRUPT_SAFE == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE
+ # slow stuff
+ 'testcase/tstAvl': '', # SLOW!
+ 'testcase/tstRTAvl': '', # SLOW! (new name)
+ 'testcase/tstVD': '', # 8GB fixed-sized vmdk
+ # failed or hang
+ 'testcase/tstCryptoPkcs7Verify': '', # hang
+ 'tstOVF': '', # hang (only ancient version, now in new place)
+ 'testcase/tstOVF': '', # Creates mess when fails, needs to be run in a separate test.
+ 'testcase/tstRTLockValidator': '', # Lock validation is not enabled for critical sections
+ 'testcase/tstGuestControlSvc': '', # failed: line 288: testHost(&svcTable): expected VINF_SUCCESS, got VERR_NOT_FOUND
+ 'testcase/tstRTMemEf': '', # failed w/o error message
+ 'testcase/tstSupSem': '', # failed: SRE Timeout Accuracy (ms) : FAILED (1 errors)
+ 'testcase/tstCryptoPkcs7Sign': '',# failed: 29330:error:02001002:lib(2):func(1):reason(2):NA:0:fopen('server.pem': '','r')
+ 'testcase/tstCompressionBenchmark': '', # failed: error: RTZipBlockCompress failed
+ # for 'RTZipBlock/LZJB' (#4): VERR_NOT_SUPPORTED
+ 'tstPDMAsyncCompletionStress': '', # VERR_INVALID_PARAMETER (cbSize = 0)
+ 'tstMicro': '', # doesn't work on solaris, fix later if we care.
+ 'tstVMM-HwAccm': '', # failed: Only checked AMD-V on linux
+ 'tstVMM-HM': '', # failed: Only checked AMD-V on linux
+ 'tstVMMFork': '', # failed: xtracker 6171
+ 'tstTestFactory': '', # some strange xpcom18a4 test, does not work
+ 'testcase/tstRTSemXRoads': '', # sporadically failed: Traffic - 8 threads per direction, 10 sec : FAILED (8 errors)
+ 'tstVBoxAPILinux': '', # creates VirtualBox directories for root user because of sudo (should be in vbox)
+ 'testcase/tstVMStructDTrace': '', # This is a D-script generator.
+ 'tstVMStructRC': '', # This is a C-code generator.
+ 'tstDeviceStructSizeRC': '', # This is a C-code generator.
+ 'testcase/tstTSC': '', # Doesn't test anything and might fail with HT or/and too many cores.
+ 'testcase/tstOpenUSBDev': '', # Not a useful testcase.
+ 'testcase/tstX86-1': '', # Really more guest side.
+ 'testcase/tstX86-FpuSaveRestore': '', # Experiments, could be useful for the guest not the host.
+ 'tstAsmStructsRC': '', # Testcase run during build time (fails to find libstdc++.so.6 on some
+ # Solaris testboxes).
+ };
+
+ # Suffix exclude list.
+ kasSuffixBlackList = [
+ '.r0',
+ '.gc',
+ '.debug',
+ '.rel',
+ '.sys',
+ '.ko',
+ '.o',
+ '.obj',
+ '.lib',
+ '.a',
+ '.so',
+ '.dll',
+ '.dylib',
+ '.tmp',
+ '.log',
+ '.py',
+ '.pyc',
+ '.pyo',
+ '.pdb',
+ '.dSYM',
+ '.sym',
+ ];
+
+ ## The exclude list.
+ # @note Stripped extensions!
+ kasHardened = [
+ "testcase/tstIntNet-1",
+ "testcase/tstR0ThreadPreemptionDriver", # VBox 4.3
+ "testcase/tstRTR0ThreadPreemptionDriver",
+ "testcase/tstRTR0MemUserKernelDriver",
+ "testcase/tstRTR0SemMutexDriver",
+ "testcase/tstRTR0TimerDriver",
+ "testcase/tstRTR0ThreadDriver",
+ 'testcase/tstRTR0DbgKrnlInfoDriver',
+ "tstInt",
+ "tstVMM",
+ "tstVMMFork",
+ "tstVMREQ",
+ 'testcase/tstCFGM',
+ 'testcase/tstContiguous',
+ 'testcase/tstGetPagingMode',
+ 'testcase/tstGIP-2',
+ 'testcase/tstInit',
+ 'testcase/tstLow',
+ 'testcase/tstMMHyperHeap',
+ 'testcase/tstPage',
+ 'testcase/tstPin',
+ 'testcase/tstRTTime', 'testcase/tstTime', # GIP test case.
+ 'testcase/tstSSM',
+ 'testcase/tstSupSem-Zombie',
+ ]
+
+ ## Argument lists
+ kdArguments = {
+ 'testcase/tstbntest': [ '-out', os.devnull, ], # Very noisy.
+ };
+
+
+ ## Status code translations.
+ ## @{
+ kdExitCodeNames = {
+ 0: 'RTEXITCODE_SUCCESS',
+ 1: 'RTEXITCODE_FAILURE',
+ 2: 'RTEXITCODE_SYNTAX',
+ 3: 'RTEXITCODE_INIT',
+ 4: 'RTEXITCODE_SKIPPED',
+ };
+ kdExitCodeNamesWin = {
+ -1073741515: 'STATUS_DLL_NOT_FOUND',
+ -1073741512: 'STATUS_ORDINAL_NOT_FOUND',
+ -1073741511: 'STATUS_ENTRYPOINT_NOT_FOUND',
+ -1073741502: 'STATUS_DLL_INIT_FAILED',
+ -1073741500: 'STATUS_UNHANDLED_EXCEPTION',
+ -1073741499: 'STATUS_APP_INIT_FAILURE',
+ -1073741819: 'STATUS_ACCESS_VIOLATION',
+ -1073741571: 'STATUS_STACK_OVERFLOW',
+ };
+ ## @}
+
+ def __init__(self):
+ """
+ Reinitialize child class instance.
+ """
+ vbox.TestDriver.__init__(self)
+ self.oTestVmSet = None;
+
+ self.sVBoxInstallRoot = None
+
+ self.cSkipped = 0
+ self.cPassed = 0
+ self.cFailed = 0
+
+ self.sUnitTestsPathBase = None
+ self.sExeSuff = '.exe' if utils.getHostOs() in [ 'win', 'dos', 'os2' ] else '';
+
+ self.aiVBoxVer = (4, 3, 0, 0);
+
+ # For testing testcase logic.
+ self.fDryRun = False;
+
+
+ def _detectPaths(self):
+ """
+ Internal worker for actionVerify and actionExecute that detects paths.
+
+ This sets sVBoxInstallRoot and sUnitTestsPathBase and returns True/False.
+ """
+
+ #
+ # We need a VBox install (/ build) to test.
+ #
+ if False is True:
+ if not self.importVBoxApi():
+ reporter.error('Unabled to import the VBox Python API.')
+ return False
+ else:
+ self._detectBuild();
+ if self.oBuild is None:
+ reporter.error('Unabled to detect the VBox build.');
+ return False;
+
+ #
+ # Where are the files installed?
+ # Solaris requires special handling because of it's multi arch subdirs.
+ #
+ self.sVBoxInstallRoot = self.oBuild.sInstallPath
+ if not self.oBuild.isDevBuild() and utils.getHostOs() == 'solaris':
+ sArchDir = utils.getHostArch();
+ if sArchDir == 'x86': sArchDir = 'i386';
+ self.sVBoxInstallRoot = os.path.join(self.sVBoxInstallRoot, sArchDir);
+
+ # Add the installation root to the PATH on windows so we can get DLLs from it.
+ if utils.getHostOs() == 'win':
+ sPathName = 'PATH';
+ if not sPathName in os.environ:
+ sPathName = 'Path';
+ sPath = os.environ.get(sPathName, '.');
+ if sPath and sPath[-1] != ';':
+ sPath += ';';
+ os.environ[sPathName] = sPath + self.sVBoxInstallRoot + ';';
+
+ #
+ # The unittests are generally not installed, so look for them.
+ #
+ sBinOrDist = 'dist' if utils.getHostOs() in [ 'darwin', ] else 'bin';
+ asCandidates = [
+ self.oBuild.sInstallPath,
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), self.oBuild.sType, sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'release', sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'debug', sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'strict', sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'dbgopt', sBinOrDist),
+ os.path.join(self.sScratchPath, utils.getHostOsDotArch(), 'profile', sBinOrDist),
+ os.path.join(self.sScratchPath, sBinOrDist + '.' + utils.getHostArch()),
+ os.path.join(self.sScratchPath, sBinOrDist, utils.getHostArch()),
+ os.path.join(self.sScratchPath, sBinOrDist),
+ ];
+ if utils.getHostOs() == 'darwin':
+ for i in range(1, len(asCandidates)):
+ asCandidates[i] = os.path.join(asCandidates[i], 'VirtualBox.app', 'Contents', 'MacOS');
+
+ for sCandidat in asCandidates:
+ if os.path.exists(os.path.join(sCandidat, 'testcase', 'tstVMStructSize' + self.sExeSuff)):
+ self.sUnitTestsPathBase = sCandidat;
+ return True;
+
+ reporter.error('Unable to find unit test dir. Candidates: %s' % (asCandidates,))
+ return False;
+
+ #
+ # Overridden methods.
+ #
+
+ def actionVerify(self):
+ return self._detectPaths();
+
+ def actionExecute(self):
+
+ if self.sUnitTestsPathBase is None and self._detectPaths():
+ return False;
+
+ self._figureVersion();
+ self._makeEnvironmentChanges();
+
+ self.testRunUnitTestsSet(r'^tst*', 'testcase')
+ self.testRunUnitTestsSet(r'^tst*', '.')
+
+ reporter.log('')
+ reporter.log('********************')
+ reporter.log('*** PASSED: %d' % self.cPassed)
+ reporter.log('*** FAILED: %d' % self.cFailed)
+ reporter.log('*** SKIPPED: %d' % self.cSkipped)
+ reporter.log('*** TOTAL: %d' % (self.cPassed + self.cFailed + self.cSkipped))
+
+ return self.cFailed == 0
+
+ #
+ # Test execution helpers.
+ #
+
+ def _figureVersion(self):
+ """ Tries to figure which VBox version this is, setting self.aiVBoxVer. """
+ try:
+ sVer = utils.processOutputChecked(['VBoxManage', '--version'])
+
+ sVer = sVer.strip();
+ sVer = re.sub(r'_BETA.*r', '.', sVer);
+ sVer = re.sub(r'_ALPHA.*r', '.', sVer);
+ sVer = re.sub(r'_RC.*r', '.', sVer);
+ sVer = sVer.replace('r', '.');
+
+ self.aiVBoxVer = [int(sComp) for sComp in sVer.split('.')];
+
+ reporter.log('VBox version: %s' % (self.aiVBoxVer,));
+ except:
+ reporter.logXcpt();
+ return False;
+ return True;
+
+ def _compareVersion(self, aiVer):
+ """
+ Compares the give version string with the vbox version string,
+ returning a result similar to C strcmp(). aiVer is on the right side.
+ """
+ cComponents = min(len(self.aiVBoxVer), len(aiVer));
+ for i in range(cComponents):
+ if self.aiVBoxVer[i] < aiVer[i]:
+ return -1;
+ if self.aiVBoxVer[i] > aiVer[i]:
+ return 1;
+ return len(self.aiVBoxVer) - len(aiVer);
+
+ def _isExcluded(self, sTest, dExclList):
+ """ Checks if the testcase is excluded or not. """
+ if sTest in dExclList:
+ sFullExpr = dExclList[sTest].replace(' ', '').strip();
+ if sFullExpr == '':
+ return True;
+
+ # Consider each exclusion expression. These are generally ranges,
+ # either open ended or closed: "<4.3.51r12345", ">=4.3.0 && <=4.3.4".
+ asExprs = sFullExpr.split(';');
+ for sExpr in asExprs:
+
+ # Split it on the and operator and process each sub expression.
+ fResult = True;
+ for sSubExpr in sExpr.split('&&'):
+ # Split out the comparison operator and the version value.
+ if sSubExpr.startswith('<=') or sSubExpr.startswith('>='):
+ sOp = sSubExpr[:2];
+ sValue = sSubExpr[2:];
+ elif sSubExpr.startswith('<') or sSubExpr.startswith('>') or sSubExpr.startswith('='):
+ sOp = sSubExpr[:1];
+ sValue = sSubExpr[1:];
+ else:
+ sOp = sValue = '';
+
+ # Convert the version value, making sure we've got a valid one.
+ try: aiValue = [int(sComp) for sComp in sValue.replace('r', '.').split('.')];
+ except: aiValue = ();
+ if not aiValue or len(aiValue) > 4:
+ reporter.error('Invalid exclusion expression for %s: "%s" [%s]' % (sTest, sSubExpr, dExclList[sTest]));
+ return True;
+
+ # Do the compare.
+ iCmp = self._compareVersion(aiValue);
+ if sOp == '>=' and iCmp < 0:
+ fResult = False;
+ elif sOp == '>' and iCmp <= 0:
+ fResult = False;
+ elif sOp == '<' and iCmp >= 0:
+ fResult = False;
+ elif sOp == '>=' and iCmp < 0:
+ fResult = False;
+ reporter.log2('iCmp=%s; %s %s %s -> %s' % (iCmp, self.aiVBoxVer, sOp, aiValue, fResult));
+
+ # Did the expression match?
+ if fResult:
+ return True;
+
+ return False;
+
+ def _sudoExecuteSync(self, asArgs):
+ """
+ Executes a sudo child process synchronously.
+ Returns True if the process executed successfully and returned 0,
+ otherwise False is returned.
+ """
+ reporter.log('Executing [sudo]: %s' % (asArgs, ));
+ try:
+ iRc = utils.sudoProcessCall(asArgs, shell = False, close_fds = False);
+ except:
+ reporter.errorXcpt();
+ return False;
+ reporter.log('Exit code [sudo]: %s (%s)' % (iRc, asArgs));
+ return iRc is 0;
+
+ def _hardenedMkDir(self, sPath):
+ """
+ Creates the directory specified sPath (including parents).
+ """
+ reporter.log('_hardenedMkDir: %s' % (sPath,));
+ if utils.getHostOs() in [ 'win', 'os2' ]:
+ os.makedirs(sPath, 0o755);
+ else:
+ fRc = self._sudoExecuteSync(['/bin/mkdir', '-p', '-m', '0755', sPath]);
+ if fRc is not True:
+ raise Exception('Failed to create dir "%s".' % (sPath,));
+ return True;
+
+ def _hardenedCopyFile(self, sSrc, sDst, iMode):
+ """
+ Copies a file.
+ """
+ reporter.log('_hardenedCopyFile: %s -> %s (mode: %o)' % (sSrc, sDst, iMode,));
+ if utils.getHostOs() in [ 'win', 'os2' ]:
+ utils.copyFileSimple(sSrc, sDst);
+ os.chmod(sDst, iMode);
+ else:
+ fRc = self._sudoExecuteSync(['/bin/cp', sSrc, sDst]);
+ if fRc is not True:
+ raise Exception('Failed to copy "%s" to "%s".' % (sSrc, sDst,));
+ fRc = self._sudoExecuteSync(['/bin/chmod', '%o' % (iMode,), sDst]);
+ if fRc is not True:
+ raise Exception('Failed to chmod "%s".' % (sDst,));
+ return True;
+
+ def _hardenedDeleteFile(self, sPath):
+ """
+ Deletes a file.
+ """
+ reporter.log('_hardenedDeleteFile: %s' % (sPath,));
+ if os.path.exists(sPath):
+ if utils.getHostOs() in [ 'win', 'os2' ]:
+ os.remove(sPath);
+ else:
+ fRc = self._sudoExecuteSync(['/bin/rm', sPath]);
+ if fRc is not True:
+ raise Exception('Failed to remove "%s".' % (sPath,));
+ return True;
+
+ def _hardenedRemoveDir(self, sPath):
+ """
+ Removes a directory.
+ """
+ reporter.log('_hardenedRemoveDir: %s' % (sPath,));
+ if os.path.exists(sPath):
+ if utils.getHostOs() in [ 'win', 'os2' ]:
+ os.rmdir(sPath);
+ else:
+ fRc = self._sudoExecuteSync(['/bin/rmdir', sPath]);
+ if fRc is not True:
+ raise Exception('Failed to remove "%s".' % (sPath,));
+ return True;
+
+ def _executeTestCase(self, sName, sFullPath, sTestCaseSubDir, oDevNull): # pylint: disable=R0914
+ """
+ Executes a test case.
+ """
+
+ fSkipped = False;
+
+ #
+ # If hardening is enabled, some test cases and their dependencies
+ # needs to be copied to and execute from the sVBoxInstallRoot
+ # directory in order to work. They also have to be executed as
+ # root, i.e. via sudo.
+ #
+ fHardened = False;
+ asFilesToRemove = []; # Stuff to clean up.
+ asDirsToRemove = []; # Ditto.
+ if sName in self.kasHardened \
+ and self.sUnitTestsPathBase != self.sVBoxInstallRoot:
+
+ sDstDir = os.path.join(self.sVBoxInstallRoot, sTestCaseSubDir);
+ if not os.path.exists(sDstDir):
+ self._hardenedMkDir(sDstDir);
+ asDirsToRemove.append(sDstDir);
+
+ sDst = os.path.join(sDstDir, os.path.basename(sFullPath));
+ self._hardenedCopyFile(sFullPath, sDst, 0o755);
+ asFilesToRemove.append(sDst);
+
+ # Copy any associated .dll/.so/.dylib.
+ for sSuff in [ '.dll', '.so', '.dylib' ]:
+ sSrc = os.path.splitext(sFullPath)[0] + sSuff;
+ if os.path.exists(sSrc):
+ sDst = os.path.join(sDstDir, os.path.basename(sSrc));
+ self._hardenedCopyFile(sSrc, sDst, 0o644);
+ asFilesToRemove.append(sDst);
+
+ # Copy any associated .r0, .rc and .gc modules.
+ offDriver = sFullPath.rfind('Driver')
+ if offDriver > 0:
+ for sSuff in [ '.r0', 'RC.rc', 'RC.gc' ]:
+ sSrc = sFullPath[:offDriver] + sSuff;
+ if os.path.exists(sSrc):
+ sDst = os.path.join(sDstDir, os.path.basename(sSrc));
+ self._hardenedCopyFile(sSrc, sDst, 0o644);
+ asFilesToRemove.append(sDst);
+
+ sFullPath = os.path.join(sDstDir, os.path.basename(sFullPath));
+ fHardened = True;
+
+ #
+ # Set up arguments and environment.
+ #
+ asArgs = [sFullPath,]
+ if sName in self.kdArguments:
+ asArgs.extend(self.kdArguments[sName]);
+
+ os.environ['IPRT_TEST_OMIT_TOP_TEST'] = '1';
+ os.environ['IPRT_TEST_FILE'] = sXmlFile = os.path.join(self.sScratchPath, 'result.xml');
+ if os.path.exists(sXmlFile):
+ try: os.unlink(sXmlFile);
+ except: self._hardenedDeleteFile(sXmlFile);
+
+ #
+ # Execute the test case.
+ #
+ # Windows is confusing output. Trying a few things to get rid of this.
+ # First, flush both stderr and stdout before running the child. Second,
+ # assign the child stderr to stdout. If this doesn't help, we'll have
+ # to capture the child output.
+ #
+ reporter.log('*** Executing %s%s...' % (asArgs, ' [hardened]' if fHardened else ''));
+ try: sys.stdout.flush();
+ except: pass;
+ try: sys.stderr.flush();
+ except: pass;
+ if not self.fDryRun:
+ try:
+ if fHardened:
+ oChild = utils.sudoProcessPopen(asArgs, stdin = oDevNull, stdout = sys.stdout, stderr = sys.stdout);
+ else:
+ oChild = utils.processPopenSafe(asArgs, stdin = oDevNull, stdout = sys.stdout, stderr = sys.stdout);
+ except:
+ if sName in [ 'tstAsmStructsRC', # 32-bit, may fail to start on 64-bit linux. Just ignore.
+ ]:
+ reporter.logXcpt();
+ fSkipped = True;
+ else:
+ reporter.errorXcpt();
+ iRc = 1023;
+ oChild = None;
+
+ if oChild is not None:
+ self.pidFileAdd(oChild.pid, sName, fSudo = fHardened);
+ iRc = oChild.wait();
+ self.pidFileRemove(oChild.pid);
+ else:
+ iRc = 0;
+
+ #
+ # Clean up
+ #
+ for sPath in asFilesToRemove:
+ self._hardenedDeleteFile(sPath);
+ for sPath in asDirsToRemove:
+ self._hardenedRemoveDir(sPath);
+
+ #
+ # Report.
+ #
+ if os.path.exists(sXmlFile):
+ reporter.addSubXmlFile(sXmlFile);
+ if fHardened:
+ self._hardenedDeleteFile(sXmlFile);
+ else:
+ os.unlink(sXmlFile);
+
+ if iRc == 0:
+ reporter.log('*** %s: exit code %d' % (sFullPath, iRc));
+ self.cPassed += 1
+
+ elif iRc == 4: # RTEXITCODE_SKIPPED
+ reporter.log('*** %s: exit code %d (RTEXITCODE_SKIPPED)' % (sFullPath, iRc));
+ fSkipped = True;
+ self.cSkipped += 1;
+
+ elif fSkipped:
+ reporter.log('*** %s: exit code %d (Skipped)' % (sFullPath, iRc));
+ self.cSkipped += 1;
+
+ else:
+ sName = self.kdExitCodeNames.get(iRc, '');
+ if iRc in self.kdExitCodeNamesWin and utils.getHostOs() == 'win':
+ sName = self.kdExitCodeNamesWin[iRc];
+ if sName != '':
+ sName = ' (%s)' % (sName);
+
+ if iRc != 1:
+ reporter.testFailure('Exit status: %d%s' % (iRc, sName));
+ reporter.log( '!*! %s: exit code %d%s' % (sFullPath, iRc, sName));
+ else:
+ reporter.error('!*! %s: exit code %d%s' % (sFullPath, iRc, sName));
+ self.cFailed += 1
+
+ return fSkipped;
+
+ def testRunUnitTestsSet(self, sTestCasePattern, sTestCaseSubDir):
+ """
+ Run subset of the unit tests set.
+ """
+
+ # Open /dev/null for use as stdin further down.
+ try:
+ oDevNull = open(os.path.devnull, 'w+');
+ except:
+ oDevNull = None;
+
+ # Determin the host OS specific exclusion lists.
+ dTestCasesBuggyForHostOs = self.kdTestCasesBuggyPerOs.get(utils.getHostOs(), []);
+ dTestCasesBuggyForHostOs.update(self.kdTestCasesBuggyPerOs.get(utils.getHostOsDotArch(), []));
+
+ #
+ # Process the file list and run everything looking like a testcase.
+ #
+ for sFilename in sorted(os.listdir(os.path.join(self.sUnitTestsPathBase, sTestCaseSubDir))):
+ # Separate base and suffix and morph the base into something we
+ # can use for reporting and array lookups.
+ sName, sSuffix = os.path.splitext(sFilename);
+ if sTestCaseSubDir != '.':
+ sName = sTestCaseSubDir + '/' + sName;
+
+ # Basic exclusion.
+ if not re.match(sTestCasePattern, sFilename) \
+ or sSuffix in self.kasSuffixBlackList:
+ reporter.log('"%s" is not a test case.' % (sFilename,))
+ continue
+
+ # Check if the testcase is black listed or buggy before executing it.
+ if self._isExcluded(sName, self.kdTestCasesBlackList):
+ # (No testStart/Done or accounting here!)
+ reporter.log('%s: SKIPPED (blacklisted)' % (sName,));
+
+ elif self._isExcluded(sName, self.kdTestCasesBuggy):
+ reporter.testStart(sName);
+ reporter.log('%s: Skipping, buggy in general.' % (sName,));
+ reporter.testDone(fSkipped = True);
+ self.cSkipped += 1;
+
+ elif self._isExcluded(sName, dTestCasesBuggyForHostOs):
+ reporter.testStart(sName);
+ reporter.log('%s: Skipping, buggy on %s.' % (sName, utils.getHostOs(),));
+ reporter.testDone(fSkipped = True);
+ self.cSkipped += 1;
+
+ else:
+ sFullPath = os.path.normpath(os.path.join(self.sUnitTestsPathBase, os.path.join(sTestCaseSubDir, sFilename)));
+ reporter.testStart(sName);
+ try:
+ fSkipped = self._executeTestCase(sName, sFullPath, sTestCaseSubDir, oDevNull);
+ except:
+ reporter.errorXcpt('!*!');
+ self.cFailed += 1;
+ fSkipped = False;
+ reporter.testDone(fSkipped);
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdUnitTest1().main(sys.argv))
diff --git a/src/VBox/ValidationKit/tests/usb/Makefile.kmk b/src/VBox/ValidationKit/tests/usb/Makefile.kmk
new file mode 100644
index 00000000..d4e2697a
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/usb/Makefile.kmk
@@ -0,0 +1,43 @@
+# $Id: Makefile.kmk $
+## @file
+# VirtualBox Validation Kit - USB.
+#
+
+#
+# Copyright (C) 2014-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+# The contents of this file may alternatively be used under the terms
+# of the Common Development and Distribution License Version 1.0
+# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+# VirtualBox OSE distribution, in which case the provisions of the
+# CDDL are applicable instead of those of the GPL.
+#
+# You may elect to license modified versions of this file under the
+# terms and conditions of either the GPL or the CDDL or both.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+INSTALLS += ValidationKitTestsUsb
+ValidationKitTestsUsb_TEMPLATE = VBoxValidationKitR3
+ValidationKitTestsUsb_INST = $(INST_VALIDATIONKIT)tests/usb/
+ValidationKitTestsUsb_EXEC_SOURCES := \
+ $(PATH_SUB_CURRENT)/tdUsb1.py \
+ $(PATH_SUB_CURRENT)/usbgadget.py \
+ $(PATH_SUB_CURRENT)/tst-utsgadget.py
+
+VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(ValidationKitTestsUsb_EXEC_SOURCES)
+
+$(evalcall def_vbox_validationkit_process_python_sources)
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/ValidationKit/tests/usb/tdUsb1.py b/src/VBox/ValidationKit/tests/usb/tdUsb1.py
new file mode 100755
index 00000000..51a5b978
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/usb/tdUsb1.py
@@ -0,0 +1,580 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# $Id: tdUsb1.py $
+
+"""
+VirtualBox Validation Kit - USB testcase and benchmark.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2014-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+
+# Standard Python imports.
+import os;
+import sys;
+import socket;
+
+# Only the main script needs to modify the path.
+try: __file__
+except: __file__ = sys.argv[0];
+g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))));
+sys.path.append(g_ksValidationKitDir);
+
+# Validation Kit imports.
+from testdriver import reporter;
+from testdriver import base;
+from testdriver import vbox;
+from testdriver import vboxcon;
+
+# USB gadget control import
+import usbgadget;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ xrange = range; # pylint: disable=redefined-builtin,invalid-name
+
+
+class tdUsbBenchmark(vbox.TestDriver): # pylint: disable=R0902
+ """
+ USB benchmark.
+ """
+
+ # The available test devices
+ #
+ # The first key is the hostname of the host the test is running on.
+ # It contains a new dictionary with the attached gadgets based on the
+ # USB speed we want to test (Low, Full, High, Super).
+ # The parameters consist of the hostname of the gadget in the network
+ # and the hardware type.
+ kdGadgetParams = {
+ 'adaris': {
+ 'Low': ('usbtest.de.oracle.com', None),
+ 'Full': ('usbtest.de.oracle.com', None),
+ 'High': ('usbtest.de.oracle.com', None),
+ 'Super': ('usbtest.de.oracle.com', None)
+ },
+ };
+
+ # Mappings of USB controllers to supported USB device speeds.
+ kdUsbSpeedMappings = {
+ 'OHCI': ['Low', 'Full'],
+ 'EHCI': ['High'],
+ 'XHCI': ['Low', 'Full', 'High', 'Super']
+ };
+
+ # Tests currently disabled because they fail, need investigation.
+ kdUsbTestsDisabled = {
+ 'Low': [24],
+ 'Full': [24],
+ 'High': [24],
+ 'Super': [24]
+ };
+
+ def __init__(self):
+ vbox.TestDriver.__init__(self);
+ self.asRsrcs = None;
+ self.asTestVMsDef = ['tst-arch'];
+ self.asTestVMs = self.asTestVMsDef;
+ self.asSkipVMs = [];
+ self.asVirtModesDef = ['hwvirt', 'hwvirt-np', 'raw'];
+ self.asVirtModes = self.asVirtModesDef;
+ self.acCpusDef = [1, 2,];
+ self.acCpus = self.acCpusDef;
+ self.asUsbCtrlsDef = ['OHCI', 'EHCI', 'XHCI'];
+ self.asUsbCtrls = self.asUsbCtrlsDef;
+ self.asUsbSpeedDef = ['Low', 'Full', 'High', 'Super'];
+ self.asUsbSpeed = self.asUsbSpeedDef;
+ self.asUsbTestsDef = ['Compliance', 'Reattach'];
+ self.asUsbTests = self.asUsbTestsDef;
+ self.cUsbReattachCyclesDef = 100;
+ self.cUsbReattachCycles = self.cUsbReattachCyclesDef;
+ self.sHostname = socket.gethostname().lower();
+ self.sGadgetHostnameDef = 'usbtest.de.oracle.com';
+ self.uGadgetPortDef = None;
+ self.sUsbCapturePathDef = self.sScratchPath;
+ self.sUsbCapturePath = self.sUsbCapturePathDef;
+ self.fUsbCapture = False;
+
+ #
+ # Overridden methods.
+ #
+ def showUsage(self):
+ rc = vbox.TestDriver.showUsage(self);
+ reporter.log('');
+ reporter.log('tdUsb1 Options:');
+ reporter.log(' --virt-modes <m1[:m2[:]]');
+ reporter.log(' Default: %s' % (':'.join(self.asVirtModesDef)));
+ reporter.log(' --cpu-counts <c1[:c2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.acCpusDef)));
+ reporter.log(' --test-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Test the specified VMs in the given order. Use this to change');
+ reporter.log(' the execution order or limit the choice of VMs');
+ reporter.log(' Default: %s (all)' % (':'.join(self.asTestVMsDef)));
+ reporter.log(' --skip-vms <vm1[:vm2[:...]]>');
+ reporter.log(' Skip the specified VMs when testing.');
+ reporter.log(' --usb-ctrls <u1[:u2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.asUsbCtrlsDef)));
+ reporter.log(' --usb-speed <s1[:s2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.asUsbSpeedDef)));
+ reporter.log(' --usb-tests <s1[:s2[:]]');
+ reporter.log(' Default: %s' % (':'.join(str(c) for c in self.asUsbTestsDef)));
+ reporter.log(' --usb-reattach-cycles <cycles>');
+ reporter.log(' Default: %s' % (self.cUsbReattachCyclesDef));
+ reporter.log(' --hostname: <hostname>');
+ reporter.log(' Default: %s' % (self.sHostname));
+ reporter.log(' --default-gadget-host <hostname>');
+ reporter.log(' Default: %s' % (self.sGadgetHostnameDef));
+ reporter.log(' --default-gadget-port <port>');
+ reporter.log(' Default: %s' % (6042));
+ reporter.log(' --usb-capture-path <path>');
+ reporter.log(' Default: %s' % (self.sUsbCapturePathDef));
+ reporter.log(' --usb-capture');
+ reporter.log(' Whether to capture the USB traffic for each test');
+ return rc;
+
+ def parseOption(self, asArgs, iArg): # pylint: disable=R0912,R0915
+ if asArgs[iArg] == '--virt-modes':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--virt-modes" takes a colon separated list of modes');
+ self.asVirtModes = asArgs[iArg].split(':');
+ for s in self.asVirtModes:
+ if s not in self.asVirtModesDef:
+ raise base.InvalidOption('The "--virt-modes" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asVirtModesDef)));
+ elif asArgs[iArg] == '--cpu-counts':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--cpu-counts" takes a colon separated list of cpu counts');
+ self.acCpus = [];
+ for s in asArgs[iArg].split(':'):
+ try: c = int(s);
+ except: raise base.InvalidOption('The "--cpu-counts" value "%s" is not an integer' % (s,));
+ if c <= 0: raise base.InvalidOption('The "--cpu-counts" value "%s" is zero or negative' % (s,));
+ self.acCpus.append(c);
+ elif asArgs[iArg] == '--test-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--test-vms" takes colon separated list');
+ self.asTestVMs = asArgs[iArg].split(':');
+ for s in self.asTestVMs:
+ if s not in self.asTestVMsDef:
+ raise base.InvalidOption('The "--test-vms" value "%s" is not valid; valid values are: %s' \
+ % (s, ' '.join(self.asTestVMsDef)));
+ elif asArgs[iArg] == '--skip-vms':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--skip-vms" takes colon separated list');
+ self.asSkipVMs = asArgs[iArg].split(':');
+ for s in self.asSkipVMs:
+ if s not in self.asTestVMsDef:
+ reporter.log('warning: The "--test-vms" value "%s" does not specify any of our test VMs.' % (s));
+ elif asArgs[iArg] == '--usb-ctrls':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-ctrls" takes a colon separated list of USB controllers');
+ self.asUsbCtrls = asArgs[iArg].split(':');
+ for s in self.asUsbCtrls:
+ if s not in self.asUsbCtrlsDef:
+ reporter.log('warning: The "--usb-ctrls" value "%s" is not a valid USB controller.' % (s));
+ elif asArgs[iArg] == '--usb-speed':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-speed" takes a colon separated list of USB speeds');
+ self.asUsbSpeed = asArgs[iArg].split(':');
+ for s in self.asUsbSpeed:
+ if s not in self.asUsbSpeedDef:
+ reporter.log('warning: The "--usb-speed" value "%s" is not a valid USB speed.' % (s));
+ elif asArgs[iArg] == '--usb-tests':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-tests" takes a colon separated list of USB tests');
+ self.asUsbTests = asArgs[iArg].split(':');
+ for s in self.asUsbTests:
+ if s not in self.asUsbTestsDef:
+ reporter.log('warning: The "--usb-tests" value "%s" is not a valid USB test.' % (s));
+ elif asArgs[iArg] == '--usb-reattach-cycles':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-reattach-cycles" takes cycle count');
+ try: self.cUsbReattachCycles = int(asArgs[iArg]);
+ except: raise base.InvalidOption('The "--usb-reattach-cycles" value "%s" is not an integer' \
+ % (asArgs[iArg],));
+ if self.cUsbReattachCycles <= 0:
+ raise base.InvalidOption('The "--usb-reattach-cycles" value "%s" is zero or negative.' \
+ % (self.cUsbReattachCycles,));
+ elif asArgs[iArg] == '--hostname':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--hostname" takes a hostname');
+ self.sHostname = asArgs[iArg];
+ elif asArgs[iArg] == '--default-gadget-host':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--default-gadget-host" takes a hostname');
+ self.sGadgetHostnameDef = asArgs[iArg];
+ elif asArgs[iArg] == '--default-gadget-port':
+ iArg += 1;
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--default-gadget-port" takes port number');
+ try: self.uGadgetPortDef = int(asArgs[iArg]);
+ except: raise base.InvalidOption('The "--default-gadget-port" value "%s" is not an integer' \
+ % (asArgs[iArg],));
+ if self.uGadgetPortDef <= 0:
+ raise base.InvalidOption('The "--default-gadget-port" value "%s" is zero or negative.' \
+ % (self.uGadgetPortDef,));
+ elif asArgs[iArg] == '--usb-capture-path':
+ if iArg >= len(asArgs): raise base.InvalidOption('The "--usb-capture-path" takes a path argument');
+ self.sUsbCapturePath = asArgs[iArg];
+ elif asArgs[iArg] == '--usb-capture':
+ self.fUsbCapture = True;
+ else:
+ return vbox.TestDriver.parseOption(self, asArgs, iArg);
+ return iArg + 1;
+
+ def completeOptions(self):
+ # Remove skipped VMs from the test list.
+ for sVM in self.asSkipVMs:
+ try: self.asTestVMs.remove(sVM);
+ except: pass;
+
+ return vbox.TestDriver.completeOptions(self);
+
+ def getResourceSet(self):
+ # Construct the resource list the first time it's queried.
+ if self.asRsrcs is None:
+ self.asRsrcs = [];
+
+ if 'tst-arch' in self.asTestVMs:
+ self.asRsrcs.append('4.2/usb/tst-arch.vdi');
+
+ return self.asRsrcs;
+
+ def actionConfig(self):
+
+ # Some stupid trickery to guess the location of the iso. ## fixme - testsuite unzip ++
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxValidationKit.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../VBoxTestSuite.iso'));
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/mnt/ramdisk/vbox/svn/trunk/testsuite/VBoxTestSuite.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sCur = os.getcwd();
+ for i in range(0, 10):
+ sVBoxValidationKit_iso = os.path.join(sCur, 'validationkit/VBoxValidationKit.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sVBoxValidationKit_iso = os.path.join(sCur, 'testsuite/VBoxTestSuite.iso');
+ if os.path.isfile(sVBoxValidationKit_iso):
+ break;
+ sCur = os.path.abspath(os.path.join(sCur, '..'));
+ if i is None: pass; # shut up pychecker/pylint.
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/validationkit/VBoxValidationKit.iso';
+ if not os.path.isfile(sVBoxValidationKit_iso):
+ sVBoxValidationKit_iso = '/home/bird/testsuite/VBoxTestSuite.iso';
+
+ # Make sure vboxapi has been imported so we can use the constants.
+ if not self.importVBoxApi():
+ return False;
+
+ #
+ # Configure the VMs we're going to use.
+ #
+
+ # Linux VMs
+ if 'tst-arch' in self.asTestVMs:
+ oVM = self.createTestVM('tst-arch', 1, '4.2/usb/tst-arch.vdi', sKind = 'ArchLinux_64', fIoApic = True, \
+ eNic0AttachType = vboxcon.NetworkAttachmentType_NAT, \
+ sDvdImage = sVBoxValidationKit_iso);
+ if oVM is None:
+ return False;
+
+ return True;
+
+ def actionExecute(self):
+ """
+ Execute the testcase.
+ """
+ fRc = self.testUsb();
+ return fRc;
+
+ def getGadgetParams(self, sHostname, sSpeed):
+ """
+ Returns the gadget hostname and port from the
+ given hostname the test is running on and device speed we want to test.
+ """
+ kdGadgetsConfigured = self.kdGadgetParams.get(sHostname);
+ if kdGadgetsConfigured is not None:
+ return kdGadgetsConfigured.get(sSpeed);
+
+ return (self.sGadgetHostnameDef, self.uGadgetPortDef);
+
+ def getCaptureFilePath(self, sUsbCtrl, sSpeed):
+ """
+ Returns capture filename from the given data.
+ """
+
+ return '%s%s%s-%s.pcap' % (self.sUsbCapturePath, os.sep, sUsbCtrl, sSpeed);
+
+ def attachUsbDeviceToVm(self, oSession, sVendorId, sProductId, iBusId,
+ sCaptureFile = None):
+ """
+ Attaches the given USB device to the VM either via a filter
+ or directly if capturing the USB traffic is enabled.
+
+ Returns True on success, False on failure.
+ """
+ fRc = False;
+ if sCaptureFile is None:
+ fRc = oSession.addUsbDeviceFilter('Compliance device', sVendorId = sVendorId, sProductId = sProductId, \
+ sPort = format(iBusId, 'x'));
+ else:
+ # Search for the correct device in the USB device list waiting for some time
+ # to let it appear.
+ iVendorId = int(sVendorId, 16);
+ iProductId = int(sProductId, 16);
+
+ # Try a few times to give VBoxSVC a chance to detect the new device.
+ for _ in xrange(5):
+ fFound = False;
+ aoUsbDevs = self.oVBoxMgr.getArray(self.oVBox.host, 'USBDevices');
+ for oUsbDev in aoUsbDevs:
+ if oUsbDev.vendorId == iVendorId \
+ and oUsbDev.productId == iProductId \
+ and oUsbDev.port == iBusId:
+ fFound = True;
+ fRc = oSession.attachUsbDevice(oUsbDev.id, sCaptureFile);
+ break;
+
+ if fFound:
+ break;
+
+ # Wait a moment until the next try.
+ self.sleep(1);
+
+ if fRc:
+ # Wait a moment to let the USB device appear
+ self.sleep(9);
+
+ return fRc;
+
+ #
+ # Test execution helpers.
+ #
+ def testUsbCompliance(self, oSession, oTxsSession, sUsbCtrl, sSpeed, sCaptureFile = None):
+ """
+ Test VirtualBoxs USB stack in a VM.
+ """
+ # Get configured USB test devices from hostname we are running on
+ sGadgetHost, uGadgetPort = self.getGadgetParams(self.sHostname, sSpeed);
+
+ oUsbGadget = usbgadget.UsbGadget();
+ reporter.log('Connecting to UTS: ' + sGadgetHost);
+ fRc = oUsbGadget.connectTo(30 * 1000, sGadgetHost, uPort = uGadgetPort, fTryConnect = True);
+ if fRc is True:
+ reporter.log('Connect succeeded');
+ self.oVBox.host.addUSBDeviceSource('USBIP', sGadgetHost, sGadgetHost + (':%s' % oUsbGadget.getUsbIpPort()), [], []);
+
+ fSuperSpeed = False;
+ if sSpeed == 'Super':
+ fSuperSpeed = True;
+
+ # Create test device gadget and a filter to attach the device automatically.
+ fRc = oUsbGadget.impersonate(usbgadget.g_ksGadgetImpersonationTest, fSuperSpeed);
+ if fRc is True:
+ iBusId, _ = oUsbGadget.getGadgetBusAndDevId();
+ fRc = self.attachUsbDeviceToVm(oSession, '0525', 'a4a0', iBusId, sCaptureFile);
+ if fRc is True:
+ tupCmdLine = ('UsbTest', );
+ # Exclude a few tests which hang and cause a timeout, need investigation.
+ lstTestsExclude = self.kdUsbTestsDisabled.get(sSpeed);
+ for iTestExclude in lstTestsExclude:
+ tupCmdLine = tupCmdLine + ('--exclude', str(iTestExclude));
+
+ fRc = self.txsRunTest(oTxsSession, 'UsbTest', 3600 * 1000, \
+ '${CDROM}/${OS/ARCH}/UsbTest${EXESUFF}', tupCmdLine);
+ if not fRc:
+ reporter.testFailure('Running USB test utility failed');
+ else:
+ reporter.testFailure('Failed to attach USB device to VM');
+ oUsbGadget.disconnectFrom();
+ else:
+ reporter.testFailure('Failed to impersonate test device');
+
+ self.oVBox.host.removeUSBDeviceSource(sGadgetHost);
+ else:
+ reporter.log('warning: Failed to connect to USB gadget');
+ fRc = None
+
+ _ = sUsbCtrl;
+ return fRc;
+
+ def testUsbReattach(self, oSession, oTxsSession, sUsbCtrl, sSpeed, sCaptureFile = None): # pylint: disable=W0613
+ """
+ Tests that rapid connect/disconnect cycles work.
+ """
+ # Get configured USB test devices from hostname we are running on
+ sGadgetHost, uGadgetPort = self.getGadgetParams(self.sHostname, sSpeed);
+
+ oUsbGadget = usbgadget.UsbGadget();
+ reporter.log('Connecting to UTS: ' + sGadgetHost);
+ fRc = oUsbGadget.connectTo(30 * 1000, sGadgetHost, uPort = uGadgetPort, fTryConnect = True);
+ if fRc is True:
+ self.oVBox.host.addUSBDeviceSource('USBIP', sGadgetHost, sGadgetHost + (':%s' % oUsbGadget.getUsbIpPort()), [], []);
+
+ fSuperSpeed = False;
+ if sSpeed == 'Super':
+ fSuperSpeed = True;
+
+ # Create test device gadget and a filter to attach the device automatically.
+ fRc = oUsbGadget.impersonate(usbgadget.g_ksGadgetImpersonationTest, fSuperSpeed);
+ if fRc is True:
+ iBusId, _ = oUsbGadget.getGadgetBusAndDevId();
+ fRc = self.attachUsbDeviceToVm(oSession, '0525', 'a4a0', iBusId, sCaptureFile);
+ if fRc is True:
+
+ # Wait a moment to let the USB device appear
+ self.sleep(3);
+
+ # Do a rapid disconnect reconnect cycle. Wait a second before disconnecting
+ # again or it will happen so fast that the VM can't attach the new device.
+ # @todo: Get rid of the constant wait and use an event to get notified when
+ # the device was attached.
+ for iCycle in xrange (0, self.cUsbReattachCycles):
+ fRc = oUsbGadget.disconnectUsb();
+ fRc = fRc and oUsbGadget.connectUsb();
+ if not fRc:
+ reporter.testFailure('Reattach cycle %s failed on the gadget device' % (iCycle));
+ break;
+ self.sleep(1);
+
+ else:
+ reporter.testFailure('Failed to create USB device filter');
+
+ oUsbGadget.disconnectFrom();
+ else:
+ reporter.testFailure('Failed to impersonate test device');
+ else:
+ reporter.log('warning: Failed to connect to USB gadget');
+ fRc = None
+
+ return fRc;
+
+ def testUsbOneCfg(self, sVmName, sUsbCtrl, sSpeed, sUsbTest):
+ """
+ Runs the specified VM thru one specified test.
+
+ Returns a success indicator on the general test execution. This is not
+ the actual test result.
+ """
+ oVM = self.getVmByName(sVmName);
+
+ # Reconfigure the VM
+ fRc = True;
+ oSession = self.openSession(oVM);
+ if oSession is not None:
+ fRc = fRc and oSession.enableVirtEx(True);
+ fRc = fRc and oSession.enableNestedPaging(True);
+
+ # Make sure controllers are disabled initially.
+ fRc = fRc and oSession.enableUsbOhci(False);
+ fRc = fRc and oSession.enableUsbEhci(False);
+ fRc = fRc and oSession.enableUsbXhci(False);
+
+ if sUsbCtrl == 'OHCI':
+ fRc = fRc and oSession.enableUsbOhci(True);
+ elif sUsbCtrl == 'EHCI':
+ fRc = fRc and oSession.enableUsbEhci(True);
+ elif sUsbCtrl == 'XHCI':
+ fRc = fRc and oSession.enableUsbXhci(True);
+ fRc = fRc and oSession.saveSettings();
+ fRc = oSession.close() and fRc and True; # pychecker hack.
+ oSession = None;
+ else:
+ fRc = False;
+
+ # Start up.
+ if fRc is True:
+ self.logVmInfo(oVM);
+ oSession, oTxsSession = self.startVmAndConnectToTxsViaTcp(sVmName, fCdWait = False, fNatForwardingForTxs = False);
+ if oSession is not None:
+ self.addTask(oTxsSession);
+
+ # Fudge factor - Allow the guest to finish starting up.
+ self.sleep(5);
+
+ sCaptureFile = None;
+ if self.fUsbCapture:
+ sCaptureFile = self.getCaptureFilePath(sUsbCtrl, sSpeed);
+
+ if sUsbTest == 'Compliance':
+ fRc = self.testUsbCompliance(oSession, oTxsSession, sUsbCtrl, sSpeed, sCaptureFile);
+ elif sUsbTest == 'Reattach':
+ fRc = self.testUsbReattach(oSession, oTxsSession, sUsbCtrl, sSpeed, sCaptureFile);
+
+ # cleanup.
+ self.removeTask(oTxsSession);
+ self.terminateVmBySession(oSession)
+
+ # Add the traffic dump if it exists and the test failed
+ if reporter.testErrorCount() > 0 \
+ and sCaptureFile is not None \
+ and os.path.exists(sCaptureFile):
+ reporter.addLogFile(sCaptureFile, 'misc/other', 'USB traffic dump');
+ else:
+ fRc = False;
+ return fRc;
+
+ def testUsbForOneVM(self, sVmName):
+ """
+ Runs one VM thru the various configurations.
+ """
+ fRc = False;
+ reporter.testStart(sVmName);
+ for sUsbCtrl in self.asUsbCtrls:
+ reporter.testStart(sUsbCtrl)
+ for sUsbSpeed in self.asUsbSpeed:
+ asSupportedSpeeds = self.kdUsbSpeedMappings.get(sUsbCtrl);
+ if sUsbSpeed in asSupportedSpeeds:
+ reporter.testStart(sUsbSpeed)
+ for sUsbTest in self.asUsbTests:
+ reporter.testStart(sUsbTest)
+ fRc = self.testUsbOneCfg(sVmName, sUsbCtrl, sUsbSpeed, sUsbTest);
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ reporter.testDone();
+ return fRc;
+
+ def testUsb(self):
+ """
+ Executes USB test.
+ """
+
+ reporter.log("Running on host: " + self.sHostname);
+
+ # Loop thru the test VMs.
+ for sVM in self.asTestVMs:
+ # run test on the VM.
+ fRc = self.testUsbForOneVM(sVM);
+
+ return fRc;
+
+
+
+if __name__ == '__main__':
+ sys.exit(tdUsbBenchmark().main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/usb/tst-utsgadget.py b/src/VBox/ValidationKit/tests/usb/tst-utsgadget.py
new file mode 100755
index 00000000..d9d2d73c
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/usb/tst-utsgadget.py
@@ -0,0 +1,144 @@
+# -*- coding: utf-8 -*-
+# $Id: tst-utsgadget.py $
+
+"""
+Simple testcase for usbgadget2.py.
+"""
+
+__copyright__ = \
+"""
+Copyright (C) 2016-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+# Standard python imports.
+import sys
+
+# Validation Kit imports.
+sys.path.insert(0, '.');
+sys.path.insert(0, '..');
+sys.path.insert(0, '../..');
+import usbgadget;
+import testdriver.reporter as reporter
+from common import utils;
+
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+g_cTests = 0;
+g_cFailures = 0
+
+def boolRes(rc, fExpect = True):
+ """Checks a boolean result."""
+ global g_cTests, g_cFailures;
+ g_cTests = g_cTests + 1;
+ if isinstance(rc, bool):
+ if rc == fExpect:
+ return 'PASSED';
+ g_cFailures = g_cFailures + 1;
+ return 'FAILED';
+
+def stringRes(rc, sExpect):
+ """Checks a string result."""
+ global g_cTests, g_cFailures;
+ g_cTests = g_cTests + 1;
+ if utils.isString(rc):
+ if rc == sExpect:
+ return 'PASSED';
+ g_cFailures = g_cFailures + 1;
+ return 'FAILED';
+
+def main(asArgs): # pylint: disable=C0111,R0914,R0915
+ cMsTimeout = long(30*1000);
+ sAddress = 'localhost';
+ uPort = None;
+ fStdTests = True;
+
+ i = 1;
+ while i < len(asArgs):
+ if asArgs[i] == '--hostname':
+ sAddress = asArgs[i + 1];
+ i = i + 2;
+ elif asArgs[i] == '--port':
+ uPort = int(asArgs[i + 1]);
+ i = i + 2;
+ elif asArgs[i] == '--timeout':
+ cMsTimeout = long(asArgs[i + 1]);
+ i = i + 2;
+ elif asArgs[i] == '--help':
+ print('tst-utsgadget.py [--hostname <addr|name>] [--port <num>] [--timeout <cMS>]');
+ return 0;
+ else:
+ print('Unknown argument: %s' % (asArgs[i],));
+ return 2;
+
+ oGadget = usbgadget.UsbGadget();
+ if uPort is None:
+ rc = oGadget.connectTo(cMsTimeout, sAddress);
+ else:
+ rc = oGadget.connectTo(cMsTimeout, sAddress, uPort = uPort);
+ if rc is False:
+ print('connectTo failed');
+ return 1;
+
+ if fStdTests:
+ rc = oGadget.getUsbIpPort() is not None;
+ print('%s: getUsbIpPort() -> %s' % (boolRes(rc), oGadget.getUsbIpPort(),));
+
+ rc = oGadget.impersonate(usbgadget.g_ksGadgetImpersonationTest);
+ print('%s: impersonate()' % (boolRes(rc),));
+
+ rc = oGadget.disconnectUsb();
+ print('%s: disconnectUsb()' % (boolRes(rc),));
+
+ rc = oGadget.connectUsb();
+ print('%s: connectUsb()' % (boolRes(rc),));
+
+ rc = oGadget.clearImpersonation();
+ print('%s: clearImpersonation()' % (boolRes(rc),));
+
+ # Test super speed (and therefore passing configuration items)
+ rc = oGadget.impersonate(usbgadget.g_ksGadgetImpersonationTest, True);
+ print('%s: impersonate(, True)' % (boolRes(rc),));
+
+ rc = oGadget.clearImpersonation();
+ print('%s: clearImpersonation()' % (boolRes(rc),));
+
+ # Done
+ rc = oGadget.disconnectFrom();
+ print('%s: disconnectFrom() -> %s' % (boolRes(rc), rc,));
+
+ if g_cFailures != 0:
+ print('tst-utsgadget.py: %u out of %u test failed' % (g_cFailures, g_cTests,));
+ return 1;
+ print('tst-utsgadget.py: all %u tests passed!' % (g_cTests,));
+ return 0;
+
+
+if __name__ == '__main__':
+ reporter.incVerbosity();
+ reporter.incVerbosity();
+ reporter.incVerbosity();
+ reporter.incVerbosity();
+ sys.exit(main(sys.argv));
+
diff --git a/src/VBox/ValidationKit/tests/usb/usbgadget.py b/src/VBox/ValidationKit/tests/usb/usbgadget.py
new file mode 100755
index 00000000..ba009848
--- /dev/null
+++ b/src/VBox/ValidationKit/tests/usb/usbgadget.py
@@ -0,0 +1,1470 @@
+# -*- coding: utf-8 -*-
+# $Id: usbgadget.py $
+# pylint: disable=C0302
+
+"""
+UTS (USB Test Service) client.
+"""
+__copyright__ = \
+"""
+Copyright (C) 2010-2019 Oracle Corporation
+
+This file is part of VirtualBox Open Source Edition (OSE), as
+available from http://www.virtualbox.org. This file is free software;
+you can redistribute it and/or modify it under the terms of the GNU
+General Public License (GPL) as published by the Free Software
+Foundation, in version 2 as it comes in the "COPYING" file of the
+VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+
+The contents of this file may alternatively be used under the terms
+of the Common Development and Distribution License Version 1.0
+(CDDL) only, as it comes in the "COPYING.CDDL" file of the
+VirtualBox OSE distribution, in which case the provisions of the
+CDDL are applicable instead of those of the GPL.
+
+You may elect to license modified versions of this file under the
+terms and conditions of either the GPL or the CDDL or both.
+"""
+__version__ = "$Revision: 127855 $"
+
+# Standard Python imports.
+import array
+import errno
+import select
+import socket
+import sys;
+import threading
+import time
+import zlib
+
+# Validation Kit imports.
+from common import utils;
+from testdriver import base;
+from testdriver import reporter;
+from testdriver.base import TdTaskBase;
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int; # pylint: disable=redefined-builtin,invalid-name
+
+
+## @name USB gadget impersonation string constants.
+## @{
+g_ksGadgetImpersonationInvalid = 'Invalid';
+g_ksGadgetImpersonationTest = 'Test';
+g_ksGadgetImpersonationMsd = 'Msd';
+g_ksGadgetImpersonationWebcam = 'Webcam';
+g_ksGadgetImpersonationEther = 'Ether';
+## @}
+
+## @name USB gadget type used in the UTS protocol.
+## @{
+g_kiGadgetTypeTest = 1;
+## @}
+
+## @name USB gadget access methods used in the UTS protocol.
+## @{
+g_kiGadgetAccessUsbIp = 1;
+## @}
+
+## @name USB gadget config types.
+## @{
+g_kiGadgetCfgTypeBool = 1;
+g_kiGadgetCfgTypeString = 2;
+g_kiGadgetCfgTypeUInt8 = 3;
+g_kiGadgetCfgTypeUInt16 = 4;
+g_kiGadgetCfgTypeUInt32 = 5;
+g_kiGadgetCfgTypeUInt64 = 6;
+g_kiGadgetCfgTypeInt8 = 7;
+g_kiGadgetCfgTypeInt16 = 8;
+g_kiGadgetCfgTypeInt32 = 9;
+g_kiGadgetCfgTypeInt64 = 10;
+## @}
+
+#
+# Helpers for decoding data received from the UTS.
+# These are used both the Session and Transport classes.
+#
+
+def getU64(abData, off):
+ """Get a U64 field."""
+ return abData[off] \
+ + abData[off + 1] * 256 \
+ + abData[off + 2] * 65536 \
+ + abData[off + 3] * 16777216 \
+ + abData[off + 4] * 4294967296 \
+ + abData[off + 5] * 1099511627776 \
+ + abData[off + 6] * 281474976710656 \
+ + abData[off + 7] * 72057594037927936;
+
+def getU32(abData, off):
+ """Get a U32 field."""
+ return abData[off] \
+ + abData[off + 1] * 256 \
+ + abData[off + 2] * 65536 \
+ + abData[off + 3] * 16777216;
+
+def getU16(abData, off):
+ """Get a U16 field."""
+ return abData[off] \
+ + abData[off + 1] * 256;
+
+def getU8(abData, off):
+ """Get a U8 field."""
+ return abData[off];
+
+def getSZ(abData, off, sDefault = None):
+ """
+ Get a zero-terminated string field.
+ Returns sDefault if the string is invalid.
+ """
+ cchStr = getSZLen(abData, off);
+ if cchStr >= 0:
+ abStr = abData[off:(off + cchStr)];
+ try:
+ return abStr.tostring().decode('utf_8');
+ except:
+ reporter.errorXcpt('getSZ(,%u)' % (off));
+ return sDefault;
+
+def getSZLen(abData, off):
+ """
+ Get the length of a zero-terminated string field, in bytes.
+ Returns -1 if off is beyond the data packet or not properly terminated.
+ """
+ cbData = len(abData);
+ if off >= cbData:
+ return -1;
+
+ offCur = off;
+ while abData[offCur] != 0:
+ offCur = offCur + 1;
+ if offCur >= cbData:
+ return -1;
+
+ return offCur - off;
+
+def isValidOpcodeEncoding(sOpcode):
+ """
+ Checks if the specified opcode is valid or not.
+ Returns True on success.
+ Returns False if it is invalid, details in the log.
+ """
+ sSet1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ sSet2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ ";
+ if len(sOpcode) != 8:
+ reporter.error("invalid opcode length: %s" % (len(sOpcode)));
+ return False;
+ for i in range(0, 1):
+ if sSet1.find(sOpcode[i]) < 0:
+ reporter.error("invalid opcode char #%u: %s" % (i, sOpcode));
+ return False;
+ for i in range(2, 7):
+ if sSet2.find(sOpcode[i]) < 0:
+ reporter.error("invalid opcode char #%u: %s" % (i, sOpcode));
+ return False;
+ return True;
+
+#
+# Helper for encoding data sent to the UTS.
+#
+
+def u32ToByteArray(u32):
+ """Encodes the u32 value as a little endian byte (B) array."""
+ return array.array('B', \
+ ( u32 % 256, \
+ (u32 // 256) % 256, \
+ (u32 // 65536) % 256, \
+ (u32 // 16777216) % 256) );
+
+def u16ToByteArray(u16):
+ """Encodes the u16 value as a little endian byte (B) array."""
+ return array.array('B', \
+ ( u16 % 256, \
+ (u16 // 256) % 256) );
+
+def u8ToByteArray(uint8):
+ """Encodes the u8 value as a little endian byte (B) array."""
+ return array.array('B', (uint8 % 256));
+
+def zeroByteArray(cb):
+ """Returns an array with the given size containing 0."""
+ abArray = array.array('B', (0, ));
+ cb = cb - 1;
+ for i in range(cb): # pylint: disable=W0612
+ abArray.append(0);
+ return abArray;
+
+def strToByteArry(sStr):
+ """Encodes the string as a little endian byte (B) array including the terminator."""
+ abArray = array.array('B');
+ sUtf8 = sStr.encode('utf_8');
+ for ch in sUtf8:
+ abArray.append(ord(ch));
+ abArray.append(0);
+ return abArray;
+
+def cfgListToByteArray(lst):
+ """Encodes the given config list as a little endian byte (B) array."""
+ abArray = array.array('B');
+ if lst is not None:
+ for t3Item in lst:
+ # Encode they key size
+ abArray.extend(u32ToByteArray(len(t3Item[0]) + 1)); # Include terminator
+ abArray.extend(u32ToByteArray(t3Item[1])) # Config type
+ abArray.extend(u32ToByteArray(len(t3Item[2]) + 1)); # Value size including temrinator.
+ abArray.extend(u32ToByteArray(0)); # Reserved field.
+
+ abArray.extend(strToByteArry(t3Item[0]));
+ abArray.extend(strToByteArry(t3Item[2]));
+
+ return abArray;
+
+class TransportBase(object):
+ """
+ Base class for the transport layer.
+ """
+
+ def __init__(self, sCaller):
+ self.sDbgCreated = '%s: %s' % (utils.getTimePrefix(), sCaller);
+ self.fDummy = 0;
+ self.abReadAheadHdr = array.array('B');
+
+ def toString(self):
+ """
+ Stringify the instance for logging and debugging.
+ """
+ return '<%s: abReadAheadHdr=%s, sDbgCreated=%s>' % (type(self).__name__, self.abReadAheadHdr, self.sDbgCreated);
+
+ def __str__(self):
+ return self.toString();
+
+ def cancelConnect(self):
+ """
+ Cancels any pending connect() call.
+ Returns None;
+ """
+ return None;
+
+ def connect(self, cMsTimeout):
+ """
+ Quietly attempts to connect to the UTS.
+
+ Returns True on success.
+ Returns False on retryable errors (no logging).
+ Returns None on fatal errors with details in the log.
+
+ Override this method, don't call super.
+ """
+ _ = cMsTimeout;
+ return False;
+
+ def disconnect(self, fQuiet = False):
+ """
+ Disconnect from the UTS.
+
+ Returns True.
+
+ Override this method, don't call super.
+ """
+ _ = fQuiet;
+ return True;
+
+ def sendBytes(self, abBuf, cMsTimeout):
+ """
+ Sends the bytes in the buffer abBuf to the UTS.
+
+ Returns True on success.
+ Returns False on failure and error details in the log.
+
+ Override this method, don't call super.
+
+ Remarks: len(abBuf) is always a multiple of 16.
+ """
+ _ = abBuf; _ = cMsTimeout;
+ return False;
+
+ def recvBytes(self, cb, cMsTimeout, fNoDataOk):
+ """
+ Receive cb number of bytes from the UTS.
+
+ Returns the bytes (array('B')) on success.
+ Returns None on failure and error details in the log.
+
+ Override this method, don't call super.
+
+ Remarks: cb is always a multiple of 16.
+ """
+ _ = cb; _ = cMsTimeout; _ = fNoDataOk;
+ return None;
+
+ def isConnectionOk(self):
+ """
+ Checks if the connection is OK.
+
+ Returns True if it is.
+ Returns False if it isn't (caller should call diconnect).
+
+ Override this method, don't call super.
+ """
+ return True;
+
+ def isRecvPending(self, cMsTimeout = 0):
+ """
+ Checks if there is incoming bytes, optionally waiting cMsTimeout
+ milliseconds for something to arrive.
+
+ Returns True if there is, False if there isn't.
+
+ Override this method, don't call super.
+ """
+ _ = cMsTimeout;
+ return False;
+
+ def sendMsgInt(self, sOpcode, cMsTimeout, abPayload = array.array('B')):
+ """
+ Sends a message (opcode + encoded payload).
+
+ Returns True on success.
+ Returns False on failure and error details in the log.
+ """
+ # Fix + check the opcode.
+ if len(sOpcode) < 2:
+ reporter.fatal('sendMsgInt: invalid opcode length: %d (\"%s\")' % (len(sOpcode), sOpcode));
+ return False;
+ sOpcode = sOpcode.ljust(8);
+ if not isValidOpcodeEncoding(sOpcode):
+ reporter.fatal('sendMsgInt: invalid opcode encoding: \"%s\"' % (sOpcode));
+ return False;
+
+ # Start construct the message.
+ cbMsg = 16 + len(abPayload);
+ abMsg = array.array('B');
+ abMsg.extend(u32ToByteArray(cbMsg));
+ abMsg.extend((0, 0, 0, 0)); # uCrc32
+ try:
+ abMsg.extend(array.array('B', \
+ ( ord(sOpcode[0]), \
+ ord(sOpcode[1]), \
+ ord(sOpcode[2]), \
+ ord(sOpcode[3]), \
+ ord(sOpcode[4]), \
+ ord(sOpcode[5]), \
+ ord(sOpcode[6]), \
+ ord(sOpcode[7]) ) ) );
+ if abPayload:
+ abMsg.extend(abPayload);
+ except:
+ reporter.fatalXcpt('sendMsgInt: packing problem...');
+ return False;
+
+ # checksum it, padd it and send it off.
+ uCrc32 = zlib.crc32(abMsg[8:]);
+ abMsg[4:8] = u32ToByteArray(uCrc32);
+
+ while len(abMsg) % 16:
+ abMsg.append(0);
+
+ reporter.log2('sendMsgInt: op=%s len=%d to=%d' % (sOpcode, len(abMsg), cMsTimeout));
+ return self.sendBytes(abMsg, cMsTimeout);
+
+ def recvMsg(self, cMsTimeout, fNoDataOk = False):
+ """
+ Receives a message from the UTS.
+
+ Returns the message three-tuple: length, opcode, payload.
+ Returns (None, None, None) on failure and error details in the log.
+ """
+
+ # Read the header.
+ if self.abReadAheadHdr:
+ assert(len(self.abReadAheadHdr) == 16);
+ abHdr = self.abReadAheadHdr;
+ self.abReadAheadHdr = array.array('B');
+ else:
+ abHdr = self.recvBytes(16, cMsTimeout, fNoDataOk);
+ if abHdr is None:
+ return (None, None, None);
+ if len(abHdr) != 16:
+ reporter.fatal('recvBytes(16) returns %d bytes!' % (len(abHdr)));
+ return (None, None, None);
+
+ # Unpack and validate the header.
+ cbMsg = getU32(abHdr, 0);
+ uCrc32 = getU32(abHdr, 4);
+ sOpcode = abHdr[8:16].tostring().decode('ascii');
+
+ if cbMsg < 16:
+ reporter.fatal('recvMsg: message length is out of range: %s (min 16 bytes)' % (cbMsg));
+ return (None, None, None);
+ if cbMsg > 1024*1024:
+ reporter.fatal('recvMsg: message length is out of range: %s (max 1MB)' % (cbMsg));
+ return (None, None, None);
+ if not isValidOpcodeEncoding(sOpcode):
+ reporter.fatal('recvMsg: invalid opcode \"%s\"' % (sOpcode));
+ return (None, None, None);
+
+ # Get the payload (if any), dropping the padding.
+ abPayload = array.array('B');
+ if cbMsg > 16:
+ if cbMsg % 16:
+ cbPadding = 16 - (cbMsg % 16);
+ else:
+ cbPadding = 0;
+ abPayload = self.recvBytes(cbMsg - 16 + cbPadding, cMsTimeout, False);
+ if abPayload is None:
+ self.abReadAheadHdr = abHdr;
+ if not fNoDataOk :
+ reporter.log('recvMsg: failed to recv payload bytes!');
+ return (None, None, None);
+
+ while cbPadding > 0:
+ abPayload.pop();
+ cbPadding = cbPadding - 1;
+
+ # Check the CRC-32.
+ if uCrc32 != 0:
+ uActualCrc32 = zlib.crc32(abHdr[8:]);
+ if cbMsg > 16:
+ uActualCrc32 = zlib.crc32(abPayload, uActualCrc32);
+ uActualCrc32 = uActualCrc32 & 0xffffffff;
+ if uCrc32 != uActualCrc32:
+ reporter.fatal('recvMsg: crc error: expected %s, got %s' % (hex(uCrc32), hex(uActualCrc32)));
+ return (None, None, None);
+
+ reporter.log2('recvMsg: op=%s len=%d' % (sOpcode, len(abPayload)));
+ return (cbMsg, sOpcode, abPayload);
+
+ def sendMsg(self, sOpcode, cMsTimeout, aoPayload = ()):
+ """
+ Sends a message (opcode + payload tuple).
+
+ Returns True on success.
+ Returns False on failure and error details in the log.
+ Returns None if you pass the incorrectly typed parameters.
+ """
+ # Encode the payload.
+ abPayload = array.array('B');
+ for o in aoPayload:
+ try:
+ if utils.isString(o):
+ # the primitive approach...
+ sUtf8 = o.encode('utf_8');
+ for ch in sUtf8:
+ abPayload.append(ord(ch))
+ abPayload.append(0);
+ elif isinstance(o, long):
+ if o < 0 or o > 0xffffffff:
+ reporter.fatal('sendMsg: uint32_t payload is out of range: %s' % (hex(o)));
+ return None;
+ abPayload.extend(u32ToByteArray(o));
+ elif isinstance(o, int):
+ if o < 0 or o > 0xffffffff:
+ reporter.fatal('sendMsg: uint32_t payload is out of range: %s' % (hex(o)));
+ return None;
+ abPayload.extend(u32ToByteArray(o));
+ elif isinstance(o, array.array):
+ abPayload.extend(o);
+ else:
+ reporter.fatal('sendMsg: unexpected payload type: %s (%s) (aoPayload=%s)' % (type(o), o, aoPayload));
+ return None;
+ except:
+ reporter.fatalXcpt('sendMsg: screwed up the encoding code...');
+ return None;
+ return self.sendMsgInt(sOpcode, cMsTimeout, abPayload);
+
+
+class Session(TdTaskBase):
+ """
+ A USB Test Service (UTS) client session.
+ """
+
+ def __init__(self, oTransport, cMsTimeout, cMsIdleFudge, fTryConnect = False):
+ """
+ Construct a UTS session.
+
+ This starts by connecting to the UTS and will enter the signalled state
+ when connected or the timeout has been reached.
+ """
+ TdTaskBase.__init__(self, utils.getCallerName());
+ self.oTransport = oTransport;
+ self.sStatus = "";
+ self.cMsTimeout = 0;
+ self.fErr = True; # Whether to report errors as error.
+ self.msStart = 0;
+ self.oThread = None;
+ self.fnTask = self.taskDummy;
+ self.aTaskArgs = None;
+ self.oTaskRc = None;
+ self.t3oReply = (None, None, None);
+ self.fScrewedUpMsgState = False;
+ self.fTryConnect = fTryConnect;
+
+ if not self.startTask(cMsTimeout, False, "connecting", self.taskConnect, (cMsIdleFudge,)):
+ raise base.GenError("startTask failed");
+
+ def __del__(self):
+ """Make sure to cancel the task when deleted."""
+ self.cancelTask();
+
+ def toString(self):
+ return '<%s fnTask=%s, aTaskArgs=%s, sStatus=%s, oTaskRc=%s, cMsTimeout=%s,' \
+ ' msStart=%s, fTryConnect=%s, fErr=%s, fScrewedUpMsgState=%s, t3oReply=%s oTransport=%s, oThread=%s>' \
+ % (TdTaskBase.toString(self), self.fnTask, self.aTaskArgs, self.sStatus, self.oTaskRc, self.cMsTimeout,
+ self.msStart, self.fTryConnect, self.fErr, self.fScrewedUpMsgState, self.t3oReply, self.oTransport, self.oThread);
+
+ def taskDummy(self):
+ """Place holder to catch broken state handling."""
+ raise Exception();
+
+ def startTask(self, cMsTimeout, fIgnoreErrors, sStatus, fnTask, aArgs = ()):
+ """
+ Kicks of a new task.
+
+ cMsTimeout: The task timeout in milliseconds. Values less than
+ 500 ms will be adjusted to 500 ms. This means it is
+ OK to use negative value.
+ sStatus: The task status.
+ fnTask: The method that'll execute the task.
+ aArgs: Arguments to pass to fnTask.
+
+ Returns True on success, False + error in log on failure.
+ """
+ if not self.cancelTask():
+ reporter.maybeErr(not fIgnoreErrors, 'utsclient.Session.startTask: failed to cancel previous task.');
+ return False;
+
+ # Change status and make sure we're the
+ self.lockTask();
+ if self.sStatus != "":
+ self.unlockTask();
+ reporter.maybeErr(not fIgnoreErrors, 'utsclient.Session.startTask: race.');
+ return False;
+ self.sStatus = "setup";
+ self.oTaskRc = None;
+ self.t3oReply = (None, None, None);
+ self.resetTaskLocked();
+ self.unlockTask();
+
+ self.cMsTimeout = max(cMsTimeout, 500);
+ self.fErr = not fIgnoreErrors;
+ self.fnTask = fnTask;
+ self.aTaskArgs = aArgs;
+ self.oThread = threading.Thread(target=self.taskThread, args=(), name=('UTS-%s' % (sStatus)));
+ self.oThread.setDaemon(True);
+ self.msStart = base.timestampMilli();
+
+ self.lockTask();
+ self.sStatus = sStatus;
+ self.unlockTask();
+ self.oThread.start();
+
+ return True;
+
+ def cancelTask(self, fSync = True):
+ """
+ Attempts to cancel any pending tasks.
+ Returns success indicator (True/False).
+ """
+ self.lockTask();
+
+ if self.sStatus == "":
+ self.unlockTask();
+ return True;
+ if self.sStatus == "setup":
+ self.unlockTask();
+ return False;
+ if self.sStatus == "cancelled":
+ self.unlockTask();
+ return False;
+
+ reporter.log('utsclient: cancelling "%s"...' % (self.sStatus));
+ if self.sStatus == 'connecting':
+ self.oTransport.cancelConnect();
+
+ self.sStatus = "cancelled";
+ oThread = self.oThread;
+ self.unlockTask();
+
+ if not fSync:
+ return False;
+
+ oThread.join(61.0);
+ return oThread.isAlive();
+
+ def taskThread(self):
+ """
+ The task thread function.
+ This does some housekeeping activities around the real task method call.
+ """
+ if not self.isCancelled():
+ try:
+ fnTask = self.fnTask;
+ oTaskRc = fnTask(*self.aTaskArgs);
+ except:
+ reporter.fatalXcpt('taskThread', 15);
+ oTaskRc = None;
+ else:
+ reporter.log('taskThread: cancelled already');
+
+ self.lockTask();
+
+ reporter.log('taskThread: signalling task with status "%s", oTaskRc=%s' % (self.sStatus, oTaskRc));
+ self.oTaskRc = oTaskRc;
+ self.oThread = None;
+ self.sStatus = '';
+ self.signalTaskLocked();
+
+ self.unlockTask();
+ return None;
+
+ def isCancelled(self):
+ """Internal method for checking if the task has been cancelled."""
+ self.lockTask();
+ sStatus = self.sStatus;
+ self.unlockTask();
+ if sStatus == "cancelled":
+ return True;
+ return False;
+
+ def hasTimedOut(self):
+ """Internal method for checking if the task has timed out or not."""
+ cMsLeft = self.getMsLeft();
+ if cMsLeft <= 0:
+ return True;
+ return False;
+
+ def getMsLeft(self, cMsMin = 0, cMsMax = -1):
+ """Gets the time left until the timeout."""
+ cMsElapsed = base.timestampMilli() - self.msStart;
+ if cMsElapsed < 0:
+ return cMsMin;
+ cMsLeft = self.cMsTimeout - cMsElapsed;
+ if cMsLeft <= cMsMin:
+ return cMsMin;
+ if cMsLeft > cMsMax and cMsMax > 0:
+ return cMsMax
+ return cMsLeft;
+
+ def recvReply(self, cMsTimeout = None, fNoDataOk = False):
+ """
+ Wrapper for TransportBase.recvMsg that stashes the response away
+ so the client can inspect it later on.
+ """
+ if cMsTimeout is None:
+ cMsTimeout = self.getMsLeft(500);
+ cbMsg, sOpcode, abPayload = self.oTransport.recvMsg(cMsTimeout, fNoDataOk);
+ self.lockTask();
+ self.t3oReply = (cbMsg, sOpcode, abPayload);
+ self.unlockTask();
+ return (cbMsg, sOpcode, abPayload);
+
+ def recvAck(self, fNoDataOk = False):
+ """
+ Receives an ACK or error response from the UTS.
+
+ Returns True on success.
+ Returns False on timeout or transport error.
+ Returns (sOpcode, sDetails) tuple on failure. The opcode is stripped
+ and there are always details of some sort or another.
+ """
+ cbMsg, sOpcode, abPayload = self.recvReply(None, fNoDataOk);
+ if cbMsg is None:
+ return False;
+ sOpcode = sOpcode.strip()
+ if sOpcode == "ACK":
+ return True;
+ return (sOpcode, getSZ(abPayload, 16, sOpcode));
+
+ def recvAckLogged(self, sCommand, fNoDataOk = False):
+ """
+ Wrapper for recvAck and logging.
+ Returns True on success (ACK).
+ Returns False on time, transport error and errors signalled by UTS.
+ """
+ rc = self.recvAck(fNoDataOk);
+ if rc is not True and not fNoDataOk:
+ if rc is False:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
+ else:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % (sCommand, rc[0], rc[1]));
+ rc = False;
+ return rc;
+
+ def recvTrueFalse(self, sCommand):
+ """
+ Receives a TRUE/FALSE response from the UTS.
+ Returns True on TRUE, False on FALSE and None on error/other (logged).
+ """
+ cbMsg, sOpcode, abPayload = self.recvReply();
+ if cbMsg is None:
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s transport error' % (sCommand));
+ return None;
+
+ sOpcode = sOpcode.strip()
+ if sOpcode == "TRUE":
+ return True;
+ if sOpcode == "FALSE":
+ return False;
+ reporter.maybeErr(self.fErr, 'recvAckLogged: %s response was %s: %s' % \
+ (sCommand, sOpcode, getSZ(abPayload, 16, sOpcode)));
+ return None;
+
+ def sendMsg(self, sOpcode, aoPayload = (), cMsTimeout = None):
+ """
+ Wrapper for TransportBase.sendMsg that inserts the correct timeout.
+ """
+ if cMsTimeout is None:
+ cMsTimeout = self.getMsLeft(500);
+ return self.oTransport.sendMsg(sOpcode, cMsTimeout, aoPayload);
+
+ def asyncToSync(self, fnAsync, *aArgs):
+ """
+ Wraps an asynchronous task into a synchronous operation.
+
+ Returns False on failure, task return status on success.
+ """
+ rc = fnAsync(*aArgs);
+ if rc is False:
+ reporter.log2('asyncToSync(%s): returns False (#1)' % (fnAsync));
+ return rc;
+
+ rc = self.waitForTask(self.cMsTimeout + 5000);
+ if rc is False:
+ reporter.maybeErrXcpt(self.fErr, 'asyncToSync: waitForTask failed...');
+ self.cancelTask();
+ #reporter.log2('asyncToSync(%s): returns False (#2)' % (fnAsync, rc));
+ return False;
+
+ rc = self.getResult();
+ #reporter.log2('asyncToSync(%s): returns %s' % (fnAsync, rc));
+ return rc;
+
+ #
+ # Connection tasks.
+ #
+
+ def taskConnect(self, cMsIdleFudge):
+ """Tries to connect to the UTS"""
+ while not self.isCancelled():
+ reporter.log2('taskConnect: connecting ...');
+ rc = self.oTransport.connect(self.getMsLeft(500));
+ if rc is True:
+ reporter.log('taskConnect: succeeded');
+ return self.taskGreet(cMsIdleFudge);
+ if rc is None:
+ reporter.log2('taskConnect: unable to connect');
+ return None;
+ if self.hasTimedOut():
+ reporter.log2('taskConnect: timed out');
+ if not self.fTryConnect:
+ reporter.maybeErr(self.fErr, 'taskConnect: timed out');
+ return False;
+ time.sleep(self.getMsLeft(1, 1000) / 1000.0);
+ if not self.fTryConnect:
+ reporter.maybeErr(self.fErr, 'taskConnect: cancelled');
+ return False;
+
+ def taskGreet(self, cMsIdleFudge):
+ """Greets the UTS"""
+ sHostname = socket.gethostname().lower();
+ cbFill = 68 - len(sHostname) - 1;
+ rc = self.sendMsg("HOWDY", ((1 << 16) | 0, 0x1, len(sHostname), sHostname, zeroByteArray(cbFill)));
+ if rc is True:
+ rc = self.recvAckLogged("HOWDY", self.fTryConnect);
+ if rc is True:
+ while cMsIdleFudge > 0:
+ cMsIdleFudge -= 1000;
+ time.sleep(1);
+ else:
+ self.oTransport.disconnect(self.fTryConnect);
+ return rc;
+
+ def taskBye(self):
+ """Says goodbye to the UTS"""
+ rc = self.sendMsg("BYE");
+ if rc is True:
+ rc = self.recvAckLogged("BYE");
+ self.oTransport.disconnect();
+ return rc;
+
+ #
+ # Gadget tasks.
+ #
+
+ def taskGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None):
+ """Creates a new gadget on UTS"""
+ cCfgItems = 0;
+ if lstCfg is not None:
+ cCfgItems = len(lstCfg);
+ fRc = self.sendMsg("GDGTCRT", (iGadgetType, iGadgetAccess, cCfgItems, 0, cfgListToByteArray(lstCfg)));
+ if fRc is True:
+ fRc = self.recvAckLogged("GDGTCRT");
+ return fRc;
+
+ def taskGadgetDestroy(self, iGadgetId):
+ """Destroys the given gadget handle on UTS"""
+ fRc = self.sendMsg("GDGTDTOR", (iGadgetId, zeroByteArray(12)));
+ if fRc is True:
+ fRc = self.recvAckLogged("GDGTDTOR");
+ return fRc;
+
+ def taskGadgetConnect(self, iGadgetId):
+ """Connects the given gadget handle on UTS"""
+ fRc = self.sendMsg("GDGTCNCT", (iGadgetId, zeroByteArray(12)));
+ if fRc is True:
+ fRc = self.recvAckLogged("GDGTCNCT");
+ return fRc;
+
+ def taskGadgetDisconnect(self, iGadgetId):
+ """Disconnects the given gadget handle from UTS"""
+ fRc = self.sendMsg("GDGTDCNT", (iGadgetId, zeroByteArray(12)));
+ if fRc is True:
+ fRc = self.recvAckLogged("GDGTDCNT");
+ return fRc;
+
+ #
+ # Public methods - generic task queries
+ #
+
+ def isSuccess(self):
+ """Returns True if the task completed successfully, otherwise False."""
+ self.lockTask();
+ sStatus = self.sStatus;
+ oTaskRc = self.oTaskRc;
+ self.unlockTask();
+ if sStatus != "":
+ return False;
+ if oTaskRc is False or oTaskRc is None:
+ return False;
+ return True;
+
+ def getResult(self):
+ """
+ Returns the result of a completed task.
+ Returns None if not completed yet or no previous task.
+ """
+ self.lockTask();
+ sStatus = self.sStatus;
+ oTaskRc = self.oTaskRc;
+ self.unlockTask();
+ if sStatus != "":
+ return None;
+ return oTaskRc;
+
+ def getLastReply(self):
+ """
+ Returns the last reply three-tuple: cbMsg, sOpcode, abPayload.
+ Returns a None, None, None three-tuple if there was no last reply.
+ """
+ self.lockTask();
+ t3oReply = self.t3oReply;
+ self.unlockTask();
+ return t3oReply;
+
+ #
+ # Public methods - connection.
+ #
+
+ def asyncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a disconnect task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "bye", self.taskBye);
+
+ def syncDisconnect(self, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncDisconnect, cMsTimeout, fIgnoreErrors);
+
+ #
+ # Public methods - gadget API
+ #
+
+ def asyncGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a gadget create task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetCreate", self.taskGadgetCreate, \
+ (iGadgetType, iGadgetAccess, lstCfg));
+
+ def syncGadgetCreate(self, iGadgetType, iGadgetAccess, lstCfg = None, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncGadgetCreate, iGadgetType, iGadgetAccess, lstCfg, cMsTimeout, fIgnoreErrors);
+
+ def asyncGadgetDestroy(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a gadget destroy task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetDestroy", self.taskGadgetDestroy, \
+ (iGadgetId, ));
+
+ def syncGadgetDestroy(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncGadgetDestroy, iGadgetId, cMsTimeout, fIgnoreErrors);
+
+ def asyncGadgetConnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a gadget connect task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetConnect", self.taskGadgetConnect, \
+ (iGadgetId, ));
+
+ def syncGadgetConnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncGadgetConnect, iGadgetId, cMsTimeout, fIgnoreErrors);
+
+ def asyncGadgetDisconnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """
+ Initiates a gadget disconnect task.
+
+ Returns True on success, False on failure (logged).
+
+ The task returns True on success and False on failure.
+ """
+ return self.startTask(cMsTimeout, fIgnoreErrors, "GadgetDisconnect", self.taskGadgetDisconnect, \
+ (iGadgetId, ));
+
+ def syncGadgetDisconnect(self, iGadgetId, cMsTimeout = 30000, fIgnoreErrors = False):
+ """Synchronous version."""
+ return self.asyncToSync(self.asyncGadgetDisconnect, iGadgetId, cMsTimeout, fIgnoreErrors);
+
+
+class TransportTcp(TransportBase):
+ """
+ TCP transport layer for the UTS client session class.
+ """
+
+ def __init__(self, sHostname, uPort):
+ """
+ Save the parameters. The session will call us back to make the
+ connection later on its worker thread.
+ """
+ TransportBase.__init__(self, utils.getCallerName());
+ self.sHostname = sHostname;
+ self.uPort = uPort if uPort is not None else 6042;
+ self.oSocket = None;
+ self.oWakeupW = None;
+ self.oWakeupR = None;
+ self.fConnectCanceled = False;
+ self.fIsConnecting = False;
+ self.oCv = threading.Condition();
+ self.abReadAhead = array.array('B');
+
+ def toString(self):
+ return '<%s sHostname=%s, uPort=%s, oSocket=%s,'\
+ ' fConnectCanceled=%s, fIsConnecting=%s, oCv=%s, abReadAhead=%s>' \
+ % (TransportBase.toString(self), self.sHostname, self.uPort, self.oSocket,
+ self.fConnectCanceled, self.fIsConnecting, self.oCv, self.abReadAhead);
+
+ def __isInProgressXcpt(self, oXcpt):
+ """ In progress exception? """
+ try:
+ if isinstance(oXcpt, socket.error):
+ try:
+ if oXcpt[0] == errno.EINPROGRESS:
+ return True;
+ except: pass;
+ try:
+ if oXcpt[0] == errno.EWOULDBLOCK:
+ return True;
+ if utils.getHostOs == 'win' and oXcpt[0] == errno.WSAEWOULDBLOCK: # pylint: disable=E1101
+ return True;
+ except: pass;
+ except:
+ pass;
+ return False;
+
+ def __isWouldBlockXcpt(self, oXcpt):
+ """ Would block exception? """
+ try:
+ if isinstance(oXcpt, socket.error):
+ try:
+ if oXcpt[0] == errno.EWOULDBLOCK:
+ return True;
+ except: pass;
+ try:
+ if oXcpt[0] == errno.EAGAIN:
+ return True;
+ except: pass;
+ except:
+ pass;
+ return False;
+
+ def __isConnectionReset(self, oXcpt):
+ """ Connection reset by Peer or others. """
+ try:
+ if isinstance(oXcpt, socket.error):
+ try:
+ if oXcpt[0] == errno.ECONNRESET:
+ return True;
+ except: pass;
+ try:
+ if oXcpt[0] == errno.ENETRESET:
+ return True;
+ except: pass;
+ except:
+ pass;
+ return False;
+
+ def _closeWakeupSockets(self):
+ """ Closes the wakup sockets. Caller should own the CV. """
+ oWakeupR = self.oWakeupR;
+ self.oWakeupR = None;
+ if oWakeupR is not None:
+ oWakeupR.close();
+
+ oWakeupW = self.oWakeupW;
+ self.oWakeupW = None;
+ if oWakeupW is not None:
+ oWakeupW.close();
+
+ return None;
+
+ def cancelConnect(self):
+ # This is bad stuff.
+ self.oCv.acquire();
+ reporter.log2('TransportTcp::cancelConnect: fIsConnecting=%s oSocket=%s' % (self.fIsConnecting, self.oSocket));
+ self.fConnectCanceled = True;
+ if self.fIsConnecting:
+ oSocket = self.oSocket;
+ self.oSocket = None;
+ if oSocket is not None:
+ reporter.log2('TransportTcp::cancelConnect: closing the socket');
+ oSocket.close();
+
+ oWakeupW = self.oWakeupW;
+ self.oWakeupW = None;
+ if oWakeupW is not None:
+ reporter.log2('TransportTcp::cancelConnect: wakeup call');
+ try: oWakeupW.send('cancelled!\n');
+ except: reporter.logXcpt();
+ try: oWakeupW.shutdown(socket.SHUT_WR);
+ except: reporter.logXcpt();
+ oWakeupW.close();
+ self.oCv.release();
+
+ def _connectAsClient(self, oSocket, oWakeupR, cMsTimeout):
+ """ Connects to the UTS server as client. """
+
+ # Connect w/ timeouts.
+ rc = None;
+ try:
+ oSocket.connect((self.sHostname, self.uPort));
+ rc = True;
+ except socket.error as oXcpt:
+ iRc = oXcpt.errno;
+ if self.__isInProgressXcpt(oXcpt):
+ # Do the actual waiting.
+ reporter.log2('TransportTcp::connect: operation in progress (%s)...' % (oXcpt,));
+ try:
+ ttRc = select.select([oWakeupR], [oSocket], [oSocket, oWakeupR], cMsTimeout / 1000.0);
+ if len(ttRc[1]) + len(ttRc[2]) == 0:
+ raise socket.error(errno.ETIMEDOUT, 'select timed out');
+ iRc = oSocket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR);
+ rc = iRc == 0;
+ except socket.error as oXcpt2:
+ iRc = oXcpt2.errno;
+ except:
+ iRc = -42;
+ reporter.fatalXcpt('socket.select() on connect failed');
+
+ if rc is True:
+ pass;
+ elif iRc == errno.ECONNREFUSED \
+ or iRc == errno.EHOSTUNREACH \
+ or iRc == errno.EINTR \
+ or iRc == errno.ENETDOWN \
+ or iRc == errno.ENETUNREACH \
+ or iRc == errno.ETIMEDOUT:
+ rc = False; # try again.
+ else:
+ if iRc != errno.EBADF or not self.fConnectCanceled:
+ reporter.fatalXcpt('socket.connect((%s,%s)) failed; iRc=%s' % (self.sHostname, self.uPort, iRc));
+ reporter.log2('TransportTcp::connect: rc=%s iRc=%s' % (rc, iRc));
+ except:
+ reporter.fatalXcpt('socket.connect((%s,%s)) failed' % (self.sHostname, self.uPort));
+ return rc;
+
+
+ def connect(self, cMsTimeout):
+ # Create a non-blocking socket.
+ reporter.log2('TransportTcp::connect: cMsTimeout=%s sHostname=%s uPort=%s' % (cMsTimeout, self.sHostname, self.uPort));
+ try:
+ oSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
+ except:
+ reporter.fatalXcpt('socket.socket() failed');
+ return None;
+ try:
+ oSocket.setblocking(0);
+ except:
+ oSocket.close();
+ reporter.fatalXcpt('socket.socket() failed');
+ return None;
+
+ # Create wakeup socket pair for unix (select doesn't wake up on socket close on Linux).
+ oWakeupR = None;
+ oWakeupW = None;
+ if hasattr(socket, 'socketpair'):
+ try: (oWakeupR, oWakeupW) = socket.socketpair(); # pylint: disable=E1101
+ except: reporter.logXcpt('socket.socketpair() failed');
+
+ # Update the state.
+ self.oCv.acquire();
+ rc = None;
+ if not self.fConnectCanceled:
+ self.oSocket = oSocket;
+ self.oWakeupW = oWakeupW;
+ self.oWakeupR = oWakeupR;
+ self.fIsConnecting = True;
+ self.oCv.release();
+
+ # Try connect.
+ if oWakeupR is None:
+ oWakeupR = oSocket; # Avoid select failure.
+ rc = self._connectAsClient(oSocket, oWakeupR, cMsTimeout);
+ oSocket = None;
+
+ # Update the state and cleanup on failure/cancel.
+ self.oCv.acquire();
+ if rc is True and self.fConnectCanceled:
+ rc = False;
+ self.fIsConnecting = False;
+
+ if rc is not True:
+ if self.oSocket is not None:
+ self.oSocket.close();
+ self.oSocket = None;
+ self._closeWakeupSockets();
+ self.oCv.release();
+
+ reporter.log2('TransportTcp::connect: returning %s' % (rc,));
+ return rc;
+
+ def disconnect(self, fQuiet = False):
+ if self.oSocket is not None:
+ self.abReadAhead = array.array('B');
+
+ # Try a shutting down the socket gracefully (draining it).
+ try:
+ self.oSocket.shutdown(socket.SHUT_WR);
+ except:
+ if not fQuiet:
+ reporter.error('shutdown(SHUT_WR)');
+ try:
+ self.oSocket.setblocking(0); # just in case it's not set.
+ sData = "1";
+ while sData:
+ sData = self.oSocket.recv(16384);
+ except:
+ pass;
+
+ # Close it.
+ self.oCv.acquire();
+ try: self.oSocket.setblocking(1);
+ except: pass;
+ self.oSocket.close();
+ self.oSocket = None;
+ else:
+ self.oCv.acquire();
+ self._closeWakeupSockets();
+ self.oCv.release();
+
+ def sendBytes(self, abBuf, cMsTimeout):
+ if self.oSocket is None:
+ reporter.error('TransportTcp.sendBytes: No connection.');
+ return False;
+
+ # Try send it all.
+ try:
+ cbSent = self.oSocket.send(abBuf);
+ if cbSent == len(abBuf):
+ return True;
+ except Exception as oXcpt:
+ if not self.__isWouldBlockXcpt(oXcpt):
+ reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
+ return False;
+ cbSent = 0;
+
+ # Do a timed send.
+ msStart = base.timestampMilli();
+ while True:
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ reporter.error('TranportTcp.sendBytes: %s bytes timed out (1)' % (len(abBuf)));
+ break;
+
+ # wait.
+ try:
+ ttRc = select.select([], [self.oSocket], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
+ if ttRc[2] and not ttRc[1]:
+ reporter.error('TranportTcp.sendBytes: select returned with exception');
+ break;
+ if not ttRc[1]:
+ reporter.error('TranportTcp.sendBytes: %s bytes timed out (2)' % (len(abBuf)));
+ break;
+ except:
+ reporter.errorXcpt('TranportTcp.sendBytes: select failed');
+ break;
+
+ # Try send more.
+ try:
+ cbSent += self.oSocket.send(abBuf[cbSent:]);
+ if cbSent == len(abBuf):
+ return True;
+ except Exception as oXcpt:
+ if not self.__isWouldBlockXcpt(oXcpt):
+ reporter.errorXcpt('TranportTcp.sendBytes: %s bytes' % (len(abBuf)));
+ break;
+
+ return False;
+
+ def __returnReadAheadBytes(self, cb):
+ """ Internal worker for recvBytes. """
+ assert(len(self.abReadAhead) >= cb);
+ abRet = self.abReadAhead[:cb];
+ self.abReadAhead = self.abReadAhead[cb:];
+ return abRet;
+
+ def recvBytes(self, cb, cMsTimeout, fNoDataOk):
+ if self.oSocket is None:
+ reporter.error('TransportTcp.recvBytes(%s,%s): No connection.' % (cb, cMsTimeout));
+ return None;
+
+ # Try read in some more data without bothering with timeout handling first.
+ if len(self.abReadAhead) < cb:
+ try:
+ abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
+ if abBuf:
+ self.abReadAhead.extend(array.array('B', abBuf));
+ except Exception as oXcpt:
+ if not self.__isWouldBlockXcpt(oXcpt):
+ reporter.errorXcpt('TranportTcp.recvBytes: 0/%s bytes' % (cb,));
+ return None;
+
+ if len(self.abReadAhead) >= cb:
+ return self.__returnReadAheadBytes(cb);
+
+ # Timeout loop.
+ msStart = base.timestampMilli();
+ while True:
+ cMsElapsed = base.timestampMilli() - msStart;
+ if cMsElapsed > cMsTimeout:
+ if not fNoDataOk or self.abReadAhead:
+ reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (1)' % (len(self.abReadAhead), cb));
+ break;
+
+ # Wait.
+ try:
+ ttRc = select.select([self.oSocket], [], [self.oSocket], (cMsTimeout - cMsElapsed) / 1000.0);
+ if ttRc[2] and not ttRc[0]:
+ reporter.error('TranportTcp.recvBytes: select returned with exception');
+ break;
+ if not ttRc[0]:
+ if not fNoDataOk or self.abReadAhead:
+ reporter.error('TranportTcp.recvBytes: %s/%s bytes timed out (2) fNoDataOk=%s'
+ % (len(self.abReadAhead), cb, fNoDataOk));
+ break;
+ except:
+ reporter.errorXcpt('TranportTcp.recvBytes: select failed');
+ break;
+
+ # Try read more.
+ try:
+ abBuf = self.oSocket.recv(cb - len(self.abReadAhead));
+ if not abBuf:
+ reporter.error('TranportTcp.recvBytes: %s/%s bytes (%s) - connection has been shut down'
+ % (len(self.abReadAhead), cb, fNoDataOk));
+ self.disconnect();
+ return None;
+
+ self.abReadAhead.extend(array.array('B', abBuf));
+
+ except Exception as oXcpt:
+ reporter.log('recv => exception %s' % (oXcpt,));
+ if not self.__isWouldBlockXcpt(oXcpt):
+ if not fNoDataOk or not self.__isConnectionReset(oXcpt) or self.abReadAhead:
+ reporter.errorXcpt('TranportTcp.recvBytes: %s/%s bytes (%s)' % (len(self.abReadAhead), cb, fNoDataOk));
+ break;
+
+ # Done?
+ if len(self.abReadAhead) >= cb:
+ return self.__returnReadAheadBytes(cb);
+
+ #reporter.log('recv => None len(self.abReadAhead) -> %d' % (len(self.abReadAhead), ));
+ return None;
+
+ def isConnectionOk(self):
+ if self.oSocket is None:
+ return False;
+ try:
+ ttRc = select.select([], [], [self.oSocket], 0.0);
+ if ttRc[2]:
+ return False;
+
+ self.oSocket.send(array.array('B')); # send zero bytes.
+ except:
+ return False;
+ return True;
+
+ def isRecvPending(self, cMsTimeout = 0):
+ try:
+ ttRc = select.select([self.oSocket], [], [], cMsTimeout / 1000.0);
+ if not ttRc[0]:
+ return False;
+ except:
+ pass;
+ return True;
+
+
+class UsbGadget(object):
+ """
+ USB Gadget control class using the USBT Test Service to talk to the external
+ board behaving like a USB device.
+ """
+
+ def __init__(self):
+ self.oUtsSession = None;
+ self.sImpersonation = g_ksGadgetImpersonationInvalid;
+ self.idGadget = None;
+ self.iBusId = None;
+ self.iDevId = None;
+ self.iUsbIpPort = None;
+
+ def clearImpersonation(self):
+ """
+ Removes the current impersonation of the gadget.
+ """
+ fRc = True;
+
+ if self.idGadget is not None:
+ fRc = self.oUtsSession.syncGadgetDestroy(self.idGadget);
+ self.idGadget = None;
+ self.iBusId = None;
+ self.iDevId = None;
+
+ return fRc;
+
+ def disconnectUsb(self):
+ """
+ Disconnects the USB gadget from the host. (USB connection not network
+ connection used for control)
+ """
+ return self.oUtsSession.syncGadgetDisconnect(self.idGadget);
+
+ def connectUsb(self):
+ """
+ Connect the USB gadget to the host.
+ """
+ return self.oUtsSession.syncGadgetConnect(self.idGadget);
+
+ def impersonate(self, sImpersonation, fSuperSpeed = False):
+ """
+ Impersonate a given device.
+ """
+
+ # Clear any previous impersonation
+ self.clearImpersonation();
+ self.sImpersonation = sImpersonation;
+
+ fRc = False;
+ if sImpersonation == g_ksGadgetImpersonationTest:
+ lstCfg = [];
+ if fSuperSpeed is True:
+ lstCfg.append( ('Gadget/SuperSpeed', g_kiGadgetCfgTypeBool, 'true') );
+ fDone = self.oUtsSession.syncGadgetCreate(g_kiGadgetTypeTest, g_kiGadgetAccessUsbIp, lstCfg);
+ if fDone is True and self.oUtsSession.isSuccess():
+ # Get the gadget ID.
+ _, _, abPayload = self.oUtsSession.getLastReply();
+
+ fRc = True;
+ self.idGadget = getU32(abPayload, 16);
+ self.iBusId = getU32(abPayload, 20);
+ self.iDevId = getU32(abPayload, 24);
+ else:
+ reporter.log('Invalid or unsupported impersonation');
+
+ return fRc;
+
+ def getUsbIpPort(self):
+ """
+ Returns the port the USB/IP server is listening on if requested,
+ None if USB/IP is not supported.
+ """
+ return self.iUsbIpPort;
+
+ def getGadgetBusAndDevId(self):
+ """
+ Returns the bus ad device ID of the gadget as a tuple.
+ """
+ return (self.iBusId, self.iDevId);
+
+ def connectTo(self, cMsTimeout, sHostname, uPort = None, fUsbIpSupport = True, cMsIdleFudge = 0, fTryConnect = False):
+ """
+ Connects to the specified target device.
+ Returns True on Success.
+ Returns False otherwise.
+ """
+ fRc = True;
+
+ # @todo
+ if fUsbIpSupport is False:
+ return False;
+
+ reporter.log2('openTcpSession(%s, %s, %s, %s)' % \
+ (cMsTimeout, sHostname, uPort, cMsIdleFudge));
+ try:
+ oTransport = TransportTcp(sHostname, uPort);
+ self.oUtsSession = Session(oTransport, cMsTimeout, cMsIdleFudge, fTryConnect);
+
+ if self.oUtsSession is not None:
+ fDone = self.oUtsSession.waitForTask(30*1000);
+ reporter.log('connect: waitForTask -> %s, result %s' % (fDone, self.oUtsSession.getResult()));
+ if fDone is True and self.oUtsSession.isSuccess():
+ # Parse the reply.
+ _, _, abPayload = self.oUtsSession.getLastReply();
+
+ if getU32(abPayload, 20) is g_kiGadgetAccessUsbIp:
+ fRc = True;
+ self.iUsbIpPort = getU32(abPayload, 24);
+ else:
+ reporter.log('Gadget doesn\'t support access over USB/IP despite being requested');
+ fRc = False;
+ else:
+ fRc = False;
+ else:
+ fRc = False;
+ except:
+ reporter.errorXcpt(None, 15);
+ return False;
+
+ return fRc;
+
+ def disconnectFrom(self):
+ """
+ Disconnects from the target device.
+ """
+ fRc = True;
+
+ self.clearImpersonation();
+ if self.oUtsSession is not None:
+ fRc = self.oUtsSession.syncDisconnect();
+
+ return fRc;
+