VirtualBox

Changeset 79056 in vbox for trunk/src/VBox/ValidationKit


Ignore:
Timestamp:
Jun 9, 2019 8:55:56 PM (6 years ago)
Author:
vboxsync
Message:

ValKit/tdGuestOsUnattendedInst1: Added option for installing guest additions and implemented waiting for them to come online. bugref:9151

Location:
trunk/src/VBox/ValidationKit
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ValidationKit/testdriver/vbox.py

    r79046 r79056  
    537537
    538538    @staticmethod
    539     def registerDerivedEventHandler(oVBoxMgr, fpApiVer, oSubClass, dArgsCopy,
     539    def registerDerivedEventHandler(oVBoxMgr, fpApiVer, oSubClass, dArgsCopy, # pylint: disable=too-many-arguments
    540540                                    oSrcParent, sSrcParentNm, sICallbackNm,
    541                                     fMustSucceed = True, sLogSuffix = ''):
     541                                    fMustSucceed = True, sLogSuffix = '', aenmEvents = None):
    542542        """
    543543        Registers the callback / event listener.
     
    559559                        reporter.errorXcpt('%s::registerCallback(%s)%s' % (sSrcParentNm, oRet, sLogSuffix));
    560560        else:
     561            #
     562            # Scalable event handling introduced in VBox 4.0.
     563            #
    561564            fPassive = sys.platform == 'win32'; # or webservices.
     565
     566            if not aenmEvents:
     567                aenmEvents = (vboxcon.VBoxEventType_Any,);
     568
    562569            try:
    563570                oEventSrc = oSrcParent.eventSource;
     
    573580            else:
    574581                try:
    575                     oEventSrc.registerListener(oListener, [vboxcon.VBoxEventType_Any], not fPassive);
     582                    oEventSrc.registerListener(oListener, aenmEvents, not fPassive);
    576583                except Exception as oXcpt:
    577584                    if fMustSucceed or ComError.notEqual(oXcpt, ComError.E_UNEXPECTED):
    578                         reporter.errorXcpt('%s::eventSource.registerListener(%s) failed%s' \
     585                        reporter.errorXcpt('%s::eventSource.registerListener(%s) failed%s'
    579586                                           % (sSrcParentNm, oListener, sLogSuffix));
    580587                else:
     
    39223929            sRet = None;
    39233930        return sRet;
     3931
     3932    #
     3933    # Other stuff
     3934    #
     3935
     3936    def waitForGAs(self,
     3937                   oSession, # type: vboxwrappers.SessionWrapper
     3938                   cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None, aenmWaitForInactive = None):
     3939        """
     3940        Waits for the guest additions to enter a certain state.
     3941
     3942        aenmWaitForRunLevels - List of run level values to wait for (success if one matches).
     3943        aenmWaitForActive    - List facilities (type values) that must be active.
     3944        aenmWaitForInactive  - List facilities (type values) that must be inactive.
     3945
     3946        Defaults to wait for AdditionsRunLevelType_Userland if nothing else is given.
     3947
     3948        Returns True on success, False w/ error logging on timeout or failure.
     3949        """
     3950        reporter.log2('waitForGAs: oSession=%s, cMsTimeout=%s' % (oSession, cMsTimeout,));
     3951
     3952        #
     3953        # Get IGuest:
     3954        #
     3955        try:
     3956            oIGuest = oSession.o.console.guest;
     3957        except:
     3958            return reporter.errorXcpt();
     3959
     3960        #
     3961        # Create a wait task:
     3962        #
     3963        from testdriver.vboxwrappers import AdditionsStatusTask;
     3964        try:
     3965            oGaStatusTask = AdditionsStatusTask(oSession             = oSession,
     3966                                                oIGuest              = oIGuest,
     3967                                                cMsTimeout           = cMsTimeout,
     3968                                                aenmWaitForRunLevels = aenmWaitForRunLevels,
     3969                                                aenmWaitForActive    = aenmWaitForActive,
     3970                                                aenmWaitForInactive  = aenmWaitForInactive);
     3971        except:
     3972            return reporter.errorXcpt();
     3973
     3974        #
     3975        # Add the task and make sure the VM session is also present.
     3976        #
     3977        self.addTask(oGaStatusTask);
     3978        fRemoveSession = self.addTask(oSession);
     3979        oTask          = self.waitForTasks(cMsTimeout + 1);
     3980        reporter.log2('waitForGAs: returned %s (oGaStatusTask=%s, oSession=%s)' % (oTask, oGaStatusTask, oSession,));
     3981        self.removeTask(oGaStatusTask);
     3982        if fRemoveSession:
     3983            self.removeTask(oSession);
     3984
     3985        #
     3986        # Digest the result.
     3987        #
     3988        if oTask is oGaStatusTask:
     3989            fSucceeded = oGaStatusTask.getResult();
     3990            if fSucceeded is True:
     3991                reporter.log('waitForGAs: Succeeded.');
     3992            else:
     3993                reporter.error('waitForGAs: Failed.');
     3994        else:
     3995            oGaStatusTask.cancelTask();
     3996            if oTask is None:
     3997                reporter.error('waitForGAs: Timed out.');
     3998            elif oTask is oSession:
     3999                oSession.reportPrematureTermination('waitForGAs: ');
     4000            else:
     4001                reporter.error('waitForGAs: unknown/wrong task %s' % (oTask,));
     4002            fSucceeded = False;
     4003        return fSucceeded;
     4004
  • trunk/src/VBox/ValidationKit/testdriver/vboxwrappers.py

    r79045 r79056  
    32283228        return True;
    32293229
     3230
     3231
     3232class AdditionsStatusTask(TdTaskBase):
     3233    """
     3234    Class that takes care of waiting till the guest additions are in a given state.
     3235    """
     3236
     3237    class AdditionsStatusTaskCallback(vbox.EventHandlerBase):
     3238        """ Class for looking for IPv4 address changes on interface 0."""
     3239        def __init__(self, dArgs):
     3240            self.oParentTask = dArgs['oParentTask'];
     3241            vbox.EventHandlerBase.__init__(self, dArgs, self.oParentTask.oSession.fpApiVer,
     3242                                           'AdditionsStatusTaskCallback/%s' % (self.oParentTask.oSession.sName,));
     3243
     3244        def handleEvent(self, oEvt):
     3245            try:
     3246                enmType = oEvt.type;
     3247            except:
     3248                reporter.errorXcpt();
     3249            else:
     3250                reporter.log2('AdditionsStatusTaskCallback:handleEvent: enmType=%s' % (enmType,));
     3251                if enmType == vboxcon.VBoxEventType_OnGuestAdditionsStatusChanged:
     3252                    oParentTask = self.oParentTask;
     3253                    if oParentTask:
     3254                        oParentTask.pollTask();
     3255
     3256            # end
     3257
     3258
     3259    def __init__(self, oSession, oIGuest, cMsTimeout = 120000, aenmWaitForRunLevels = None, aenmWaitForActive = None,
     3260                 aenmWaitForInactive = None):
     3261        """
     3262        aenmWaitForRunLevels - List of run level values to wait for (success if one matches).
     3263        aenmWaitForActive    - List facilities (type values) that must be active.
     3264        aenmWaitForInactive  - List facilities (type values) that must be inactive.
     3265
     3266        The default is to wait for AdditionsRunLevelType_Userland if all three lists
     3267        are unspecified or empty.
     3268        """
     3269        TdTaskBase.__init__(self, utils.getCallerName());
     3270        self.oSession               = oSession      # type: vboxwrappers.SessionWrapper
     3271        self.oIGuest                = oIGuest;
     3272        self.cMsTimeout             = cMsTimeout;
     3273        self.fSucceeded             = False;
     3274        self.oVBoxEventHandler      = None;
     3275        self.aenmWaitForRunLevels   = aenmWaitForRunLevels if aenmWaitForRunLevels else [];
     3276        self.aenmWaitForActive      = aenmWaitForActive    if aenmWaitForActive    else [];
     3277        self.aenmWaitForInactive    = aenmWaitForInactive  if aenmWaitForInactive  else [];
     3278
     3279        # Provide a sensible default if nothing is given.
     3280        if not self.aenmWaitForRunLevels and not self.aenmWaitForActive and not self.aenmWaitForInactive:
     3281            self.aenmWaitForRunLevels = [vboxcon.AdditionsRunLevelType_Userland,];
     3282
     3283        # Register the event handler on hosts which has it:
     3284        if oSession.fpApiVer >= 6.1 or hasattr(vboxcon, 'VBoxEventType_OnGuestAdditionsStatusChanged'):
     3285            aenmEvents = (vboxcon.VBoxEventType_OnGuestAdditionsStatusChanged,);
     3286            dArgs = {
     3287                'oParentTask': self,
     3288            };
     3289            self.oVBoxEventHandler = vbox.EventHandlerBase.registerDerivedEventHandler(oSession.oVBoxMgr,
     3290                                                                                       oSession.fpApiVer,
     3291                                                                                       self.AdditionsStatusTaskCallback,
     3292                                                                                       dArgs,
     3293                                                                                       oIGuest,
     3294                                                                                       'IGuest',
     3295                                                                                       'AdditionsStatusTaskCallback',
     3296                                                                                       aenmEvents = aenmEvents);
     3297        reporter.log2('AdditionsStatusTask: %s' % (self.toString(), ));
     3298
     3299    def __del__(self):
     3300        """ Make sure we deregister the callback. """
     3301        self._deregisterEventHandler();
     3302        self.oIGuest = None;
     3303        return TdTaskBase.__del__(self);
     3304
     3305    def toString(self):
     3306        return '<%s cMsTimeout=%s, fSucceeded=%s, aenmWaitForRunLevels=%s, aenmWaitForActive=%s, aenmWaitForInactive=%s, ' \
     3307               'oVBoxEventHandler=%s>' \
     3308             % (TdTaskBase.toString(self), self.cMsTimeout, self.fSucceeded, self.aenmWaitForRunLevels, self.aenmWaitForActive,
     3309                self.aenmWaitForInactive, self.oVBoxEventHandler,);
     3310
     3311    def _deregisterEventHandler(self):
     3312        """Deregisters the event handler."""
     3313        fRc = True;
     3314        oVBoxEventHandler = self.oVBoxEventHandler;
     3315        if oVBoxEventHandler is not None:
     3316            self.oVBoxEventHandler = None;
     3317            fRc = oVBoxEventHandler.unregister();
     3318            oVBoxEventHandler.oParentTask = None; # Try avoid cylic deps.
     3319        return fRc;
     3320
     3321    def _poll(self):
     3322        """
     3323        Internal worker for pollTask() that returns the new signalled state.
     3324        """
     3325
     3326        #
     3327        # Check if any of the runlevels we wait for have been reached:
     3328        #
     3329        if self.aenmWaitForRunLevels:
     3330            try:
     3331                enmRunLevel = self.oIGuest.additionsRunLevel;
     3332            except:
     3333                reporter.errorXcpt();
     3334                return True;
     3335            if enmRunLevel not in self.aenmWaitForRunLevels:
     3336                reporter.log2('AdditionsStatusTask/poll: enmRunLevel=%s not in %s' % (enmRunLevel, self.aenmWaitForRunLevels,));
     3337                return False;
     3338            reporter.log2('AdditionsStatusTask/poll: enmRunLevel=%s matched %s!' % (enmRunLevel, self.aenmWaitForRunLevels,));
     3339
     3340
     3341        #
     3342        # Check for the facilities that must all be active.
     3343        #
     3344        for enmFacility in self.aenmWaitForActive:
     3345            try:
     3346                (enmStatus, _) = self.oIGuest.getFacilityStatus(enmFacility);
     3347            except:
     3348                reporter.errorXcpt('enmFacility=%s' % (enmFacility,));
     3349                return True;
     3350            if enmStatus != vboxcon.AdditionsFacilityStatus_Active:
     3351                reporter.log2('AdditionsStatusTask/poll: enmFacility=%s not active: %s' % (enmFacility, enmStatus,));
     3352                return False;
     3353
     3354        #
     3355        # Check for the facilities that must all be inactive or terminated.
     3356        #
     3357        for enmFacility in self.aenmWaitForInactive:
     3358            try:
     3359                (enmStatus, _) = self.oIGuest.getFacilityStatus(enmFacility);
     3360            except:
     3361                reporter.errorXcpt('enmFacility=%s' % (enmFacility,));
     3362                return True;
     3363            if enmStatus not in (vboxcon.AdditionsFacilityStatus_Inactive,
     3364                                 vboxcon.AdditionsFacilityStatus_Terminated):
     3365                reporter.log2('AdditionsStatusTask/poll: enmFacility=%s not inactive: %s' % (enmFacility, enmStatus,));
     3366                return False;
     3367
     3368
     3369        reporter.log('AdditionsStatusTask: Poll succeeded, signalling...');
     3370        self.fSucceeded = True;
     3371        return True;
     3372
     3373
     3374    #
     3375    # Task methods
     3376    #
     3377
     3378    def pollTask(self, fLocked = False):
     3379        """
     3380        Overridden pollTask method.
     3381        """
     3382        if not fLocked:
     3383            self.lockTask();
     3384
     3385        fDeregister = False;
     3386        fRc = self.fSignalled;
     3387        if not fRc:
     3388            fRc = self._poll();
     3389            if fRc or self.getAgeAsMs() >= self.cMsTimeout:
     3390                self.signalTaskLocked();
     3391                fDeregister = True;
     3392
     3393        if not fLocked:
     3394            self.unlockTask();
     3395
     3396        # If we're done, deregister the event callback (w/o owning lock).
     3397        if fDeregister:
     3398            self._deregisterEventHandler();
     3399        return fRc;
     3400
     3401    def getResult(self):
     3402        """
     3403        Returns true if the we succeeded.
     3404        Returns false if not.  If the task is signalled already, then we
     3405        encountered a problem while polling.
     3406        """
     3407        return self.fSucceeded;
     3408
     3409    def cancelTask(self):
     3410        """
     3411        Cancels the task.
     3412        Just to actively disengage the event handler.
     3413        """
     3414        self._deregisterEventHandler();
     3415        return True;
     3416
  • trunk/src/VBox/ValidationKit/tests/installation/tdGuestOsUnattendedInst1.py

    r79046 r79056  
    7777        self.fOptIoApic     = None;
    7878        self.fOptPae        = None;
     79        self.fOptInstallGAs = False;
    7980        self.asOptExtraData = [];
    8081        if fFlags & self.kfIdeIrqDelay:
    8182            self.asOptExtraData = self.kasIdeIrqDelay;
     83
     84    def _unattendedConfigure(self, oIUnattended, oTestDrv): # type: (Any, vbox.TestDriver) -> bool
     85        """
     86        Configures the unattended install object.
     87
     88        The ISO attribute has been set and detectIsoOS has been done, the rest of the
     89        setup is done here.
     90
     91        Returns True on success, False w/ errors logged on failure.
     92        """
     93
     94        #
     95        # Make it install the TXS.
     96        #
     97        try:    oIUnattended.installTestExecService = True;
     98        except: return reporter.errorXcpt();
     99        try:    oIUnattended.validationKitIsoPath   = oTestDrv.sVBoxValidationKitIso;
     100        except: return reporter.errorXcpt();
     101        oTestDrv.processPendingEvents();
     102
     103        #
     104        # Install GAs?
     105        #
     106        if self.fOptInstallGAs:
     107            try:    oIUnattended.installGuestAdditions = True;
     108            except: return reporter.errorXcpt();
     109            try:    oIUnattended.additionsIsoPath      = oTestDrv.getGuestAdditionsIso();
     110            except: return reporter.errorXcpt();
     111            oTestDrv.processPendingEvents();
     112
     113        return True;
     114
     115    def _unattendedDoIt(self, oIUnattended, oVM, oTestDrv): # type: (Any, Any, vbox.TestDriver) -> bool
     116        """
     117        Does the unattended installation preparing, media construction and VM reconfiguration.
     118
     119        Returns True on success, False w/ errors logged on failure.
     120        """
     121
     122        # Associate oVM with the installer:
     123        try:
     124            oIUnattended.machine = oVM;
     125        except:
     126            return reporter.errorXcpt();
     127        oTestDrv.processPendingEvents();
     128
     129        # Prepare and log it:
     130        try:
     131            oIUnattended.prepare();
     132        except:
     133            return reporter.errorXcpt("IUnattended.prepare failed");
     134        oTestDrv.processPendingEvents();
     135
     136        reporter.log('IUnattended attributes after prepare():');
     137        self._unattendedLogIt(oIUnattended, oTestDrv);
     138
     139        # Create media:
     140        try:
     141            oIUnattended.constructMedia();
     142        except:
     143            return reporter.errorXcpt("IUnattended.constructMedia failed");
     144        oTestDrv.processPendingEvents();
     145
     146        # Reconfigure the VM:
     147        try:
     148            oIUnattended.reconfigureVM();
     149        except:
     150            return reporter.errorXcpt("IUnattended.reconfigureVM failed");
     151        oTestDrv.processPendingEvents();
     152
     153        return True;
     154
     155    def _unattendedLogIt(self, oIUnattended, oTestDrv):
     156        """
     157        Logs the attributes of the unattended installation object.
     158        """
     159        fRc = True;
     160        asAttribs = ( 'isoPath', 'user', 'password', 'fullUserName', 'productKey', 'additionsIsoPath', 'installGuestAdditions',
     161                      'validationKitIsoPath', 'installTestExecService', 'timeZone', 'locale', 'language', 'country', 'proxy',
     162                      'packageSelectionAdjustments', 'hostname', 'auxiliaryBasePath', 'imageIndex', 'machine',
     163                      'scriptTemplatePath', 'postInstallScriptTemplatePath', 'postInstallCommand',
     164                      'extraInstallKernelParameters', 'detectedOSTypeId', 'detectedOSVersion', 'detectedOSLanguages',
     165                      'detectedOSFlavor', 'detectedOSHints', );
     166        for sAttrib in asAttribs:
     167            try:
     168                oValue = getattr(oIUnattended, sAttrib);
     169            except:
     170                fRc = reporter.errorXcpt('sAttrib=%s' % sAttrib);
     171            else:
     172                reporter.log('%s: %s' % (sAttrib.rjust(32), oValue,));
     173                oTestDrv.processPendingEvents();
     174        return fRc;
    82175
    83176
     
    172265        return (fRc, oVM)
    173266
     267    def isLoggedOntoDesktop(self):
     268        #
     269        # Normally all unattended installations should end up on the desktop.
     270        # An exception is a minimal install, but we currently don't support that.
     271        #
     272        return True;
     273
    174274    #
    175275    # Our methods.
     
    226326
    227327        return True;
    228 
    229     def _unattendedConfigure(self, oIUnattended, oTestDrv): # type: (Any, vbox.TestDriver) -> bool
    230         """
    231         Configures the unattended install object.
    232 
    233         The ISO attribute has been set and detectIsoOS has been done, the rest of the
    234         setup is done here.
    235 
    236         Returns True on success, False w/ errors logged on failure.
    237         """
    238 
    239         #
    240         # Make it install the TXS.
    241         #
    242         try:    oIUnattended.installTestExecService = True;
    243         except: return reporter.errorXcpt();
    244         try:    oIUnattended.validationKitIsoPath = oTestDrv.sVBoxValidationKitIso;
    245         except: return reporter.errorXcpt();
    246 
    247         return True;
    248 
    249     def _unattendedDoIt(self, oIUnattended, oVM, oTestDrv): # type: (Any, Any, vbox.TestDriver) -> bool
    250         """
    251         Does the unattended installation preparing, media construction and VM reconfiguration.
    252 
    253         Returns True on success, False w/ errors logged on failure.
    254         """
    255 
    256         # Associate oVM with the installer:
    257         try:
    258             oIUnattended.machine = oVM;
    259         except:
    260             return reporter.errorXcpt();
    261         oTestDrv.processPendingEvents();
    262 
    263         # Prepare:
    264         try:
    265             oIUnattended.prepare();
    266         except:
    267             return reporter.errorXcpt("IUnattended.prepare failed");
    268         oTestDrv.processPendingEvents();
    269 
    270         # Create media:
    271         try:
    272             oIUnattended.constructMedia();
    273         except:
    274             return reporter.errorXcpt("IUnattended.constructMedia failed");
    275         oTestDrv.processPendingEvents();
    276 
    277         # Reconfigure the VM:
    278         try:
    279             oIUnattended.reconfigureVM();
    280         except:
    281             return reporter.errorXcpt("IUnattended.reconfigureVM failed");
    282         oTestDrv.processPendingEvents();
    283 
    284         return True;
    285 
    286     def _unattendedLogIt(self, oIUnattended):
    287         """
    288         Logs the attributes of the unattended installation object.
    289         """
    290         fRc = True;
    291         asAttribs = ( 'isoPath', 'user', 'password ', 'fullUserName', 'productKey', 'additionsIsoPath', 'installGuestAdditions',
    292                       'validationKitIsoPath', 'installTestExecService', 'timeZone', 'locale', 'language', 'country', 'proxy',
    293                       'packageSelectionAdjustments', 'hostname', 'auxiliaryBasePath', 'imageIndex', 'machine',
    294                       'scriptTemplatePath', 'postInstallScriptTemplatePath', 'postInstallCommand',
    295                       'extraInstallKernelParameters', 'detectedOSTypeId', 'detectedOSVersion', 'detectedOSLanguages',
    296                       'detectedOSFlavor', 'detectedOSHints', );
    297         for sAttrib in asAttribs:
    298             try:
    299                 oValue = getattr(oIUnattended, sAttrib);
    300             except:
    301                 fRc = reporter.errorXcpt('sAttrib=%s' % sAttrib);
    302             else:
    303                 reporter.log('%s: %s' % (sAttrib.rjust(32), oValue,));
    304         return fRc;
    305328
    306329
     
    441464            for oTestVm in self.aoSelectedVms:
    442465                oTestVm.fOptPae = False;
     466        elif asArgs[iArg] == '--install-additions':
     467            for oTestVm in self.aoSelectedVms:
     468                oTestVm.fOptInstallGAs = True;
     469        elif asArgs[iArg] == '--no-install-additions':
     470            for oTestVm in self.aoSelectedVms:
     471                oTestVm.fOptInstallGAs = False;
    443472        else:
    444473            return vbox.TestDriver.parseOption(self, asArgs, iArg);
     
    456485        return self.oTestVmSet.actionExecute(self, self.testOneVmConfig)
    457486
    458     def testOneVmConfig(self, oVM, oTestVm):
     487    def testOneVmConfig(self, oVM, oTestVm): # type: (Any, UnattendedVm) -> bool
    459488        """
    460489        Install guest OS and wait for result
     
    462491
    463492        self.logVmInfo(oVM)
    464         reporter.testStart('Installing %s' % (oTestVm.sVmName,))
     493        reporter.testStart('Installing %s%s' % (oTestVm.sVmName, ' with GAs' if oTestVm.fOptInstallGAs else ''))
    465494
    466495        cMsTimeout = 40*60000;
     
    469498
    470499        oSession, _ = self.startVmAndConnectToTxsViaTcp(oTestVm.sVmName, fCdWait = False, cMsTimeout = cMsTimeout);
     500        #oSession = self.startVmByName(oTestVm.sVmName); # (for quickly testing waitForGAs)
    471501        if oSession is not None:
    472             # The guest has connected to TXS, so we're done (for now anyways).
    473             reporter.log('Guest reported success')
    474             ## @todo Do save + restore.
     502            # The guest has connected to TXS.
     503            reporter.log('Guest reported success via TXS.');
     504            #reporter.log('VM started...');              # (for quickly testing waitForGAs)
     505
     506            # If we're installing GAs, wait for them to come online:
     507            fRc = True;
     508            if oTestVm.fOptInstallGAs:
     509                aenmRunLevels = [vboxcon.AdditionsRunLevelType_Userland,];
     510                if oTestVm.isLoggedOntoDesktop():
     511                    aenmRunLevels.append(vboxcon.AdditionsRunLevelType_Desktop);
     512                fRc = self.waitForGAs(oSession, cMsTimeout = cMsTimeout / 2, aenmWaitForRunLevels = aenmRunLevels);
     513
     514            # Now do a save & restore test:
     515            if fRc is True:
     516                pass; ## @todo Do save + restore.
     517
     518            # Test GAs if requested:
     519            if oTestVm.fOptInstallGAs and fRc is True:
     520                pass;
    475521
    476522            reporter.testDone()
    477             fRc = self.terminateVmBySession(oSession)
     523            fRc = self.terminateVmBySession(oSession) and fRc;
    478524            return fRc is True
    479525
    480526        reporter.error('Installation of %s has failed' % (oTestVm.sVmName,))
    481         oTestVm.detatchAndDeleteHd(self); # Save space.
     527        #oTestVm.detatchAndDeleteHd(self); # Save space.
    482528        reporter.testDone()
    483529        return False
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette