From f8fe689a81f906d1b91bb3220acde2a4ecb14c5b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 6 May 2024 05:01:46 +0200 Subject: Adding upstream version 6.0.4-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/ValidationKit/tests/Makefile.kmk | 49 + src/VBox/ValidationKit/tests/__init__.py | 30 + .../ValidationKit/tests/additions/Makefile.kmk | 43 + .../ValidationKit/tests/additions/tdAddBasic1.py | 340 ++ .../tests/additions/tdAddGuestCtrl.py | 3753 ++++++++++++++++++++ src/VBox/ValidationKit/tests/api/Makefile.kmk | 50 + src/VBox/ValidationKit/tests/api/__init__.py | 30 + src/VBox/ValidationKit/tests/api/tdApi1.py | 88 + .../ValidationKit/tests/api/tdAppliance1-t1.ova | Bin 0 -> 9216 bytes .../tests/api/tdAppliance1-t2-ovftool-4.1.0.ova | Bin 0 -> 77312 bytes .../ValidationKit/tests/api/tdAppliance1-t2.ova | Bin 0 -> 77312 bytes .../tests/api/tdAppliance1-t3-ovftool-4.1.0.ova | Bin 0 -> 9728 bytes .../ValidationKit/tests/api/tdAppliance1-t3.ova | Bin 0 -> 9728 bytes .../tests/api/tdAppliance1-t4-ovftool-4.1.0.ova | Bin 0 -> 116224 bytes .../ValidationKit/tests/api/tdAppliance1-t4.ova | Bin 0 -> 116224 bytes .../ValidationKit/tests/api/tdAppliance1-t4.pem | 74 + .../tests/api/tdAppliance1-t5-ovftool-4.1.0.ova | Bin 0 -> 112640 bytes .../ValidationKit/tests/api/tdAppliance1-t5.ova | Bin 0 -> 112640 bytes .../tests/api/tdAppliance1-t6-ovftool-4.1.0.ova | Bin 0 -> 218624 bytes .../ValidationKit/tests/api/tdAppliance1-t6.ova | Bin 0 -> 218624 bytes .../ValidationKit/tests/api/tdAppliance1-t6.pem | 134 + .../tests/api/tdAppliance1-t7-bad-instance.ova | Bin 0 -> 78336 bytes src/VBox/ValidationKit/tests/api/tdAppliance1.py | 206 ++ src/VBox/ValidationKit/tests/api/tdMoveMedium1.py | 209 ++ src/VBox/ValidationKit/tests/api/tdMoveVM1.py | 734 ++++ src/VBox/ValidationKit/tests/api/tdPython1.py | 210 ++ src/VBox/ValidationKit/tests/api/tdTreeDepth1.py | 151 + src/VBox/ValidationKit/tests/audio/Makefile.kup | 0 .../tests/audio/tdGuestHostTimings.py | 230 ++ .../ValidationKit/tests/autostart/Makefile.kmk | 41 + .../ValidationKit/tests/autostart/tdAutostart1.py | 699 ++++ .../ValidationKit/tests/benchmarks/Makefile.kmk | 41 + .../ValidationKit/tests/benchmarks/tdBenchmark1.py | 112 + src/VBox/ValidationKit/tests/cpu/Makefile.kmk | 41 + src/VBox/ValidationKit/tests/cpu/tdCpuPae1.py | 254 ++ .../ValidationKit/tests/installation/Makefile.kmk | 41 + .../tests/installation/tdGuestOsInstOs2.py | 248 ++ .../tests/installation/tdGuestOsInstTest1.py | 429 +++ src/VBox/ValidationKit/tests/network/Makefile.kmk | 41 + .../ValidationKit/tests/network/tdNetBenchmark1.py | 623 ++++ .../ValidationKit/tests/selftests/Makefile.kmk | 44 + .../ValidationKit/tests/selftests/tdSelfTest1.py | 47 + .../ValidationKit/tests/selftests/tdSelfTest2.py | 121 + .../ValidationKit/tests/selftests/tdSelfTest3.py | 115 + .../ValidationKit/tests/selftests/tdSelfTest4.py | 115 + src/VBox/ValidationKit/tests/serial/Makefile.kmk | 42 + src/VBox/ValidationKit/tests/serial/loopback.py | 247 ++ src/VBox/ValidationKit/tests/serial/tdSerial1.py | 337 ++ .../tests/shutdown/tdGuestOsShutdown1.py | 357 ++ .../ValidationKit/tests/smoketests/Makefile.kmk | 42 + .../tests/smoketests/tdExoticOrAncient1.py | 113 + .../ValidationKit/tests/smoketests/tdSmokeTest1.py | 163 + src/VBox/ValidationKit/tests/storage/Makefile.kmk | 45 + .../ValidationKit/tests/storage/remoteexecutor.py | 277 ++ src/VBox/ValidationKit/tests/storage/storagecfg.py | 608 ++++ .../tests/storage/tdStorageBenchmark1.py | 1350 +++++++ .../tests/storage/tdStorageSnapshotMerging1.py | 423 +++ .../tests/storage/tdStorageStress1.py | 517 +++ .../ValidationKit/tests/teleportation/Makefile.kmk | 41 + .../tests/teleportation/tdTeleportLocal1.py | 953 +++++ .../ValidationKit/tests/unittests/Makefile.kmk | 41 + .../ValidationKit/tests/unittests/tdUnitTest1.py | 774 ++++ src/VBox/ValidationKit/tests/usb/Makefile.kmk | 43 + src/VBox/ValidationKit/tests/usb/tdUsb1.py | 580 +++ src/VBox/ValidationKit/tests/usb/tst-utsgadget.py | 144 + src/VBox/ValidationKit/tests/usb/usbgadget.py | 1470 ++++++++ 66 files changed, 17910 insertions(+) create mode 100644 src/VBox/ValidationKit/tests/Makefile.kmk create mode 100644 src/VBox/ValidationKit/tests/__init__.py create mode 100644 src/VBox/ValidationKit/tests/additions/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/additions/tdAddBasic1.py create mode 100755 src/VBox/ValidationKit/tests/additions/tdAddGuestCtrl.py create mode 100644 src/VBox/ValidationKit/tests/api/Makefile.kmk create mode 100644 src/VBox/ValidationKit/tests/api/__init__.py create mode 100755 src/VBox/ValidationKit/tests/api/tdApi1.py create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t4.pem create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t6.pem create mode 100644 src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova create mode 100755 src/VBox/ValidationKit/tests/api/tdAppliance1.py create mode 100755 src/VBox/ValidationKit/tests/api/tdMoveMedium1.py create mode 100755 src/VBox/ValidationKit/tests/api/tdMoveVM1.py create mode 100755 src/VBox/ValidationKit/tests/api/tdPython1.py create mode 100755 src/VBox/ValidationKit/tests/api/tdTreeDepth1.py create mode 100644 src/VBox/ValidationKit/tests/audio/Makefile.kup create mode 100755 src/VBox/ValidationKit/tests/audio/tdGuestHostTimings.py create mode 100644 src/VBox/ValidationKit/tests/autostart/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/autostart/tdAutostart1.py create mode 100644 src/VBox/ValidationKit/tests/benchmarks/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/benchmarks/tdBenchmark1.py create mode 100644 src/VBox/ValidationKit/tests/cpu/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/cpu/tdCpuPae1.py create mode 100644 src/VBox/ValidationKit/tests/installation/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/installation/tdGuestOsInstOs2.py create mode 100755 src/VBox/ValidationKit/tests/installation/tdGuestOsInstTest1.py create mode 100644 src/VBox/ValidationKit/tests/network/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/network/tdNetBenchmark1.py create mode 100644 src/VBox/ValidationKit/tests/selftests/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/selftests/tdSelfTest1.py create mode 100755 src/VBox/ValidationKit/tests/selftests/tdSelfTest2.py create mode 100755 src/VBox/ValidationKit/tests/selftests/tdSelfTest3.py create mode 100755 src/VBox/ValidationKit/tests/selftests/tdSelfTest4.py create mode 100644 src/VBox/ValidationKit/tests/serial/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/serial/loopback.py create mode 100755 src/VBox/ValidationKit/tests/serial/tdSerial1.py create mode 100755 src/VBox/ValidationKit/tests/shutdown/tdGuestOsShutdown1.py create mode 100644 src/VBox/ValidationKit/tests/smoketests/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/smoketests/tdExoticOrAncient1.py create mode 100755 src/VBox/ValidationKit/tests/smoketests/tdSmokeTest1.py create mode 100644 src/VBox/ValidationKit/tests/storage/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/storage/remoteexecutor.py create mode 100755 src/VBox/ValidationKit/tests/storage/storagecfg.py create mode 100755 src/VBox/ValidationKit/tests/storage/tdStorageBenchmark1.py create mode 100755 src/VBox/ValidationKit/tests/storage/tdStorageSnapshotMerging1.py create mode 100755 src/VBox/ValidationKit/tests/storage/tdStorageStress1.py create mode 100644 src/VBox/ValidationKit/tests/teleportation/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/teleportation/tdTeleportLocal1.py create mode 100644 src/VBox/ValidationKit/tests/unittests/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/unittests/tdUnitTest1.py create mode 100644 src/VBox/ValidationKit/tests/usb/Makefile.kmk create mode 100755 src/VBox/ValidationKit/tests/usb/tdUsb1.py create mode 100755 src/VBox/ValidationKit/tests/usb/tst-utsgadget.py create mode 100755 src/VBox/ValidationKit/tests/usb/usbgadget.py (limited to 'src/VBox/ValidationKit/tests') 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 '); + 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='= 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 '); + 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 "\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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t1.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2-ovftool-4.1.0.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t2.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3-ovftool-4.1.0.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t3.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4-ovftool-4.1.0.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t4.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5-ovftool-4.1.0.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t5.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6-ovftool-4.1.0.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t6.ova 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 Binary files /dev/null and b/src/VBox/ValidationKit/tests/api/tdAppliance1-t7-bad-instance.ova 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 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 '); + 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 '); + reporter.log(' Default: %s' % (self.sTestBuildDir)); + reporter.log(' --test-vms '); + 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 '); + 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 = 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 ') + reporter.log(' --cpus <# CPUs>') + reporter.log(' --no-ioapic') + reporter.log(' --no-nested-paging') + reporter.log(' --pae') + reporter.log(' --set-extradata :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 ') + reporter.log(' Adjust the VM ram size by the given delta. Both negative and positive'); + reporter.log(' values are accepted.'); + reporter.log(' --set-extradata :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 '); + + 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 '); + reporter.log(' --local-host '); + reporter.log(' --guest-host '); + reporter.log(' --virt-modes '); + reporter.log(' Default: %s' % (':'.join(self.asNicTypes))); + reporter.log(' --nic-attachment '); + reporter.log(' Default: %s' % (self.sNicAttachmentDef)); + reporter.log(' --setups '); + reporter.log(' Default: %s (all)' % (':'.join(self.asSetupsDef))); + reporter.log(' --secs-per-run '); + reporter.log(' Default: %s' % (self.cSecsRunDef)); + reporter.log(' --tests '); + reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef))); + reporter.log(' --latency-sizes '); + reporter.log(' Default: %s' % (':'.join(str(cb) for cb in self.acbLatencyPktsDef))); # pychecker bug? + reporter.log(' --throughput-sizes '); + reporter.log(' Default: %s' % (':'.join(str(cb) for cb in self.acbThroughputPktsDef))); # pychecker bug? + reporter.log(' --test-vms '); + 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 '); + 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(''); + oSubXmlFile.write('' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write('' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write('' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write(''); + oSubXmlFile.write('' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write(''); + oSubXmlFile.close(); + oSubXmlFile = None; + reporter.testDone(); + + # Spaces + funny line endings. + reporter.testStart('subtest5'); + oSubXmlFile = reporter.FileWrapperTestPipe(); + oSubXmlFile.write('\r\n'); + oSubXmlFile.write('\n\n\t\n\r\n' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write('' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write(' \n' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write(' \n'); + oSubXmlFile.write(' \r' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write(''); + 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('\n\n\t\n\r\n' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write('' % (utils.getIsoTimestamp(),)); + oSubXmlFile.write('' % (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(""" + + + + + + + + + + + + + + + + + + + + + + + + + + +"""); + 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 = 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 ') + + reporter.log(' --cpus <# CPUs>') + reporter.log(' --no-ioapic') + reporter.log(' --no-nested-paging') + reporter.log(' --pae') + reporter.log(' --suspend-host') + reporter.log(' --suspend-time ') + 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 '); + 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 '); + reporter.log(' Default: %s' % (':'.join(self.asStorageCtrlsDef))); + reporter.log(' --host-io-cache '); + reporter.log(' Default: %s' % (':'.join(self.asHostIoCacheDef))); + reporter.log(' --disk-formats '); + reporter.log(' Default: %s' % (':'.join(self.asDiskFormatsDef))); + reporter.log(' --disk-variants '); + reporter.log(' Default: %s' % (':'.join(self.asDiskVariantsDef))); + reporter.log(' --iscsi-targets '); + reporter.log(' Default: %s' % (':'.join(self.asIscsiTargetsDef))); + reporter.log(' --tests '); + reporter.log(' Default: %s' % (':'.join(self.asTestsDef))); + reporter.log(' --test-sets '); + reporter.log(' Default: %s' % (':'.join(self.asTestSetsDef))); + reporter.log(' --diff-levels '); + reporter.log(' Default: %s' % (self.cDiffLvlsDef)); + reporter.log(' --test-vms '); + 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 '); + 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 '); + 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 ('', 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 '); + reporter.log(' Default: %s' % (':'.join(self.asStorageCtrls))); + reporter.log(' --disk-formats '); + 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 '); + reporter.log(' Default: %s' % (':'.join(self.asStorageCtrls))); + reporter.log(' --disk-formats '); + reporter.log(' Default: %s' % (':'.join(self.asDiskFormats))); + reporter.log(' --disk-dirs '); + reporter.log(' Default: %s' % (':'.join(self.asDirs))); + reporter.log(' --iscsi-targets '); + reporter.log(' Default: %s' % (':'.join(self.asIscsiTargets))); + reporter.log(' --tests '); + reporter.log(' Default: %s' % (':'.join(self.asTests))); + reporter.log(' --guest-fs '); + reporter.log(' Default: %s' % (':'.join(self.asGuestFs))); + reporter.log(' --test-vms '); + 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 '); + 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 '); + 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 '); + reporter.log(' Skip the specified VMs when testing.'); + reporter.log(' --tests '); + reporter.log(' Run the specified tests.'); + reporter.log(' Default: %s (all)' % (':'.join(self.asTestsDef))); + reporter.log(' --skip-tests '); + 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 '); + 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 '); + reporter.log(' Skip the specified VMs when testing.'); + reporter.log(' --usb-ctrls '); + reporter.log(' Default: %s' % (self.cUsbReattachCyclesDef)); + reporter.log(' --hostname: '); + reporter.log(' Default: %s' % (self.sHostname)); + reporter.log(' --default-gadget-host '); + reporter.log(' Default: %s' % (self.sGadgetHostnameDef)); + reporter.log(' --default-gadget-port '); + reporter.log(' Default: %s' % (6042)); + reporter.log(' --usb-capture-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 ] [--port ] [--timeout ]'); + 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; + -- cgit v1.2.3