public function initAfterModulesAvailable(StoryTeller $st, CliEngine $engine, Injectables $injectables) { // are we persisting the device? if (isset($engine->options->persistProcesses) && $engine->options->persistProcesses) { $st->setPersistProcesses(); } }
public function initAfterModulesAvailable(StoryTeller $st, CliEngine $engine, Injectables $injectables) { // shorthand $output = $injectables->output; // are we keeping the test environment hanging around afterwards? if (isset($engine->options->persistTarget) && $engine->options->persistTarget) { $injectables->activeConfig->setData('storyplayer.phases.testEnvShutdown.TestEnvironmentDestruction', false); $injectables->activeConfig->unsetData('storyplayer.phases.userAbort.TestEnvironmentDestruction'); $st->setPersistTestEnvironment(); } // are we trying to use a test environment that has previously // been persisted? if (isset($engine->options->reuseTarget) && $engine->options->reuseTarget) { // does the target exist to be reused? $output->setSilentMode(); $hasTarget = $st->fromTargetsTable()->hasCurrentTestEnvironment(); $output->resetSilentMode(); if (!$hasTarget) { $output->logCliWarning("target environment '" . $st->getTestEnvironmentName() . "' does not exist; ignoring --reuse-target switch"); return; } // okay, so we have the test environment in the targets table, // but has the test environment been changed at all? $output->setSilentMode(); $origSig = $st->fromTargetsTable()->getCurrentTestEnvironmentSignature(); $currentSig = $st->getTestEnvironmentSignature(); $output->resetSilentMode(); if ($origSig != $currentSig) { // our test environment entry isn't valid, so remove it $output->setSilentMode(); $st->usingTargetsTable()->removeCurrentTestEnvironment(); $output->resetSilentMode(); $output->logCliWarning("target environment '" . $st->getTestEnvironmentName() . "' has changed; ignoring --reuse-target switch"); return; } // if we get here, then we do not need to create the test environment $injectables->activeConfig->setData('storyplayer.phases.testEnvStartup.TestEnvironmentConstruction', false); } else { // do we already have this target? // // this can happen when the test environment was previously // persisted, and this time we're being run without the // --reuse-target flag // does the target exist to be reused? $output->setSilentMode(); $hasTarget = $st->fromTargetsTable()->hasCurrentTestEnvironment(); $output->resetSilentMode(); if ($hasTarget) { // remove this target from the table $output->setSilentMode(); $st->usingTargetsTable()->removeCurrentTestEnvironment(); $output->resetSilentMode(); } } }
/** * * @param StoryTeller $st * @return void */ public function start(StoryTeller $st) { // Sauce Labs handles proxying for us (if required) // via the Sauce Connect app // build the Sauce Labs url $url = "http://" . urlencode($this->browserDetails->saucelabs->username) . ':' . urlencode($this->browserDetails->saucelabs->accesskey) . '@ondemand.saucelabs.com/wd/hub'; // build the Sauce Labs capabilities array $desiredCapabilities = $this->browserDetails->desiredCapabilities; // add the story's name, so that someone looking at the Sauce Labs // list of jobs can see what this browser was used for // // due to encoding errors at SauceLabs, we can't use '>' as a // delimiter in the story's name $story = $st->getStory(); $desiredCapabilities['name'] = $st->getTestEnvironmentName() . ' / ' . $st->getCurrentPhase() . ': ' . $st->getCurrentPhaseName() . ' / ' . $story->getName(); // create the browser session $webDriver = new WebDriverClient($url); $this->browserSession = $webDriver->newSession($this->browserDetails->browser, $desiredCapabilities); }
public function play(StoryTeller $st, Injectables $injectables) { // shorthand $output = $st->getOutput(); // we're building / destroying a test environment $activity = 'Creating test environment'; $testEnv = new TestEnvironment($injectables->activeTestEnvironmentName); // we're using this to build / destroy our test environment $phasesPlayer = new PhaseGroup_Player(); // announce what we're doing $output->startPhaseGroup($activity, $injectables->activeTestEnvironmentName); // run the startup phase $phasesPlayer->playPhases($activity, $st, $injectables, $this->startupPhases, $testEnv); $creationResult = $testEnv->getResult(); $output->endPhaseGroup($creationResult); // what happened? if (!$creationResult->getPhaseGroupSucceeded() && !$creationResult->getPhaseGroupSkipped()) { $output->logCliError("failed to create test environment - cannot continue"); exit(1); } // now we need to play whatever runs against this // test environment foreach ($this->wrappedPlayers as $wrappedPlayer) { // play the wrapped item // // this is normally a story $wrappedPlayer->play($st, $injectables); } // announce what we're doing $activity = 'Destroying test environment'; $output->startPhaseGroup($activity, $injectables->activeTestEnvironmentName); // run the shutdown phase $testEnv->resetResult(); $phasesPlayer->playPhases($activity, $st, $injectables, $this->shutdownPhases, $testEnv); $output->endPhaseGroup($testEnv->getResult()); // all done }
/** * @param string $type */ public function runHandlers(StoryTeller $st, $type) { // shorthand $output = $st->getOutput(); // Do we have any persistent tables to cleanup? $runtimeConfig = $st->getRuntimeConfig(); if (!isset($runtimeConfig->storyplayer, $runtimeConfig->storyplayer->tables)) { // there are no tables at all return; } // if we get here, then we know that there are persistent // tables that we need to cleanup // this will keep track of any tables that have no associated // cleanup handler $missingCleanupHandlers = []; // Take a look at all of our process list tables foreach ($runtimeConfig->storyplayer->tables as $key => $value) { $className = "cleanup" . ucfirst($key); try { $st->{$className}($key)->{$type}(); } catch (E5xx_NoMatchingActions $e) { // We don't know about a cleanup module for this, SHOUT LOUDLY $missingCleanupHandlers[] = "Missing cleanup module for '{$key}'" . PHP_EOL; } } // Now we've cleaned everything up, save the runtime config $st->saveRuntimeConfig(); // If we have any missing cleanup handlers, output it to the screen // and exit with an error code if (count($missingCleanupHandlers)) { foreach ($missingCleanupHandlers as $msg) { $output->logCliError($msg); } exit(1); } }
protected function initDevice() { // start the test device $this->device = $this->st->getRunningDevice(); // set our top XPATH node // // for the moment, we are assuming that the test device is // a web browser, because historically this has always been // the case // // when this assumption is no longer valid, we will need to // revisit this code $this->setTopXpath("//html"); // set our top element // // we cannot assume that the browser has any DOM loaded at all $this->setTopElement($this->device); }
public function uploadFile($sourceFilename, $destFilename) { // vet our input Contract::RequiresValue($sourceFilename, is_string($sourceFilename)); Contract::RequiresValue($sourceFilename, !empty($sourceFilename)); Contract::RequiresValue($sourceFilename, is_file($sourceFilename)); Contract::RequiresValue($destFilename, is_string($destFilename)); Contract::RequiresValue($destFilename, !empty($destFilename)); // make the params printable / executable // $printableParams = $this->convertParamsForUse($params); // what are we doing? $log = usingLog()->startAction("upload file '{$sourceFilename}' to host as '{$destFilename}'"); // do we actually have everything we need to run the command? if (!$this->hasSshUsername()) { throw new E4xx_NeedSshUsername(); } if (!$this->hasIpAddress()) { throw new E4xx_NeedIpAddress(); } // build the full command // // the options that we pass (by default) to SCP: // // -o StrictHostKeyChecking=no // do not verify the SSH host key (avoids an interactive prompt) // -i <private_key> // use specified private key to access the remote/guest OS // <username>@<hostname> // who we are logging in as (should never be root) // <additional SSH options> // any other flags, such as -n to force non-interactive session $fullCommand = 'scp ' . ' ' . $this->getSshKeyForUse() . ' ' . $this->getScpOptionsForUse() . ' "' . $sourceFilename . '" ' . ' "' . $this->getSshUsername() . '@' . $this->getIpAddress() . ':' . $destFilename . '"'; // run the command $commandRunner = $this->st->getNewCommandRunner(); $result = $commandRunner->runSilently($fullCommand); // all done $log->endAction("return code was '{$result->returnCode}'"); return $result; }
public function uploadFile($sourceFilename, $destFilename) { // vet our input Contract::RequiresValue($sourceFilename, is_string($sourceFilename)); Contract::RequiresValue($sourceFilename, !empty($sourceFilename)); Contract::RequiresValue($sourceFilename, is_file($sourceFilename)); Contract::RequiresValue($destFilename, is_string($destFilename)); Contract::RequiresValue($destFilename, !empty($destFilename)); // make the params printable / executable // $printableParams = $this->convertParamsForUse($params); // what are we doing? $log = usingLog()->startAction("copy file '{$sourceFilename}' to localhost as '{$destFilename}'"); // build the full command // $fullCommand = 'cp ' . "'" . $sourceFilename . "' " . "'" . $destFilename . "'"; // run the command $commandRunner = $this->st->getNewCommandRunner(); $result = $commandRunner->runSilently($fullCommand); // all done $log->endAction("return code was '{$result->returnCode}'"); return $result; }
/** * * @param StoryTeller $st * @param Injectables $injectables * @param Phase $phase * @param boolean $isActive * @return Phase_Result */ public function playPhase(StoryTeller $st, Injectables $injectables, Phase $phase, $isActive, $thingBeingPlayed = null) { // shorthand $output = $st->getOutput(); $phaseName = $phase->getPhaseName(); // run the phase if we're allowed to if ($isActive) { $st->setCurrentPhase($phase); $phaseResult = $phase->doPhase($thingBeingPlayed); } else { $phaseResult = new Phase_Result($phaseName); $phaseResult->setContinuePlaying($phaseResult::SKIPPED); $output->logPhaseSkipped($phaseName, self::MSG_PHASE_NOT_ACTIVE); } // close off any open log actions $st->closeAllOpenActions(); // stop any running test devices if (!$st->getPersistDevice()) { $st->stopDevice(); } // close off any log actions left open by closing down // the test device $st->closeAllOpenActions(); // all done return $phaseResult; }
public function play(StoryTeller $st, Injectables $injectables) { // shorthand $output = $st->getOutput(); // we're going to use this to play our setup and teardown phases $phasesPlayer = new PhaseGroup_Player(); // load our story $story = Story_Loader::loadStory($this->storyFilename); $st->setStory($story); // does our story want to keep the test device open between // phases? if ($story->getPersistDevice()) { $st->setPersistDevice(); } // set default callbacks up $story->setDefaultCallbacks(); // make sure we start with a brand new checkpoint $st->setCheckpoint(new Story_Checkpoint($st)); // tell the outside world what we're doing $activity = "Running story"; $name = $story->getCategory() . ' > ' . $story->getGroupAsString() . ' > ' . $story->getName(); $output->startPhaseGroup($activity, $name); // run the phases before the story truly starts $phasesPlayer->playPhases($activity, $st, $injectables, $this->startupPhases, $story); // what happened? $result = $story->getResult(); if (!$result->getPhaseGroupSucceeded()) { // make sure the result has the story's filename in $result->filename = $this->storyFilename; // announce the results $output->endPhaseGroup($result); // all done return; } // run the phases in the 'story' section $phasesPlayer->playPhases($activity, $st, $injectables, $this->storyPhases, $story); // grab the result at this point $result = clone $story->getResult(); // run the shutdown phase $phasesPlayer->playPhases($activity, $st, $injectables, $this->shutdownPhases, $story); // do we also need to look at any failures that happened during // the shutdown phase? if ($result->getPhaseGroupSucceeded()) { $result = $story->getResult(); } // make sure the result has the story's filename in $result->filename = $this->storyFilename; // announce the results $output->endPhaseGroup($result); // all done }
/** * * @param VagrantVmDetails $vmDetails * @param array $provisioningVars * @return void */ public function createHost($vmDetails, $provisioningVars = array()) { // what are we doing? $log = usingLog()->startAction('provision new VM'); // make sure we like the provided details foreach (array('name', 'osName', 'homeFolder') as $param) { if (!isset($vmDetails->{$param})) { throw new E5xx_ActionFailed(__METHOD__, "missing vmDetails['{$param}']"); } } // make sure the folder exists $config = $this->st->getConfig(); if (!isset($config->storyplayer->modules->vagrant)) { throw new E5xx_ActionFailed(__METHOD__, "'vagrant' section missing in your storyplayer.json config file"); } if (!isset($config->storyplayer->modules->vagrant->dir)) { throw new E5xx_ActionFailed(__METHOD__, "'dir' setting missing from 'vagrant' section of your storyplayer.json config file"); } $pathToHomeFolder = $config->storyplayer->modules->vagrant->dir . '/' . $vmDetails->homeFolder; if (!is_dir($pathToHomeFolder)) { throw new E5xx_ActionFailed(__METHOD__, "VM dir '{$pathToHomeFolder}' does not exist"); } // remember where the Vagrantfile is $vmDetails->dir = $pathToHomeFolder; // make sure the VM is stopped, if it is running $log->addStep("stop vagrant VM in '{$pathToHomeFolder}' if already running", function () use($vmDetails) { $command = "vagrant destroy --force"; $this->runCommandAgainstHostManager($vmDetails, $command); }); // remove any existing hosts table entry usingHostsTable()->removeHost($vmDetails->hostId); // remove any roles usingRolesTable()->removeHostFromAllRoles($vmDetails->hostId); // let's start the VM $command = "vagrant up"; $result = $log->addStep("create vagrant VM in '{$pathToHomeFolder}'", function () use($command, $vmDetails) { return $this->runCommandAgainstHostManager($vmDetails, $command); }); // did it work? if ($result->returnCode !== 0) { $log->endAction("VM failed to start or provision :("); throw new E5xx_ActionFailed(__METHOD__); } // yes it did!! // now, we need to know how to contact this VM $vmDetails->ipAddress = $this->determineIpAddress($vmDetails); $vmDetails->hostname = $this->determineHostname($vmDetails); // mark the box as provisioned // we will use this in stopBox() to avoid destroying VMs that failed // to provision $vmDetails->provisioned = true; // remember this vm, now that it is running usingHostsTable()->addHost($vmDetails->hostId, $vmDetails); // now, let's get this VM into our SSH known_hosts file, to avoid // prompting people when we try and provision this VM $log->addStep("get the VM into the SSH known_hosts file", function () use($vmDetails) { usingHost($vmDetails->hostId)->runCommand("ls"); }); // all done $log->endAction("VM successfully started; IP address is {$vmDetails->ipAddress}"); }