TravisYmlView.php 9,57 Kio
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole;
use Piwik\View;
use Symfony\Component\Console\Output\OutputInterface;
use Exception;
/**
* View class for the travis.yml.twig template file. Generates the contents for a .travis.yml file.
*/
class TravisYmlView extends View
{
/**
* The .travis.yml section names that are overwritten by this command.
*
* @var string[]
*/
private static $travisYmlSectionNames = array(
'php',
'language',
'script',
'before_install',
'install',
'before_script',
'after_script',
'after_success'
);
/**
* The names of .travis.yml sections that can be extended w/ custom steps by plugins. Twig templates
* in the plugins/PluginName/tests/travis directory can be used to insert travis commands at the
* beginning or end of a section. For example, before_install.before.yml will add steps
* at the beginning of the before_install: section.
*
* @var string[]
*/
private static $travisYmlExtendableSectionNames = array(
'before_install',
'install',
'before_script',
'after_script',
'after_success'
);
/**
* Constructor.
*/
public function __construct()
{
parent::__construct("@CoreConsole/travis.yml");
}
/**
* Parse existing data in a .travis.yml file that should be preserved in the output .travis.yml.
* Includes comments.
*
* @var string $existingYmlPath The path to the existing .travis.yml file.
*/
public function processExistingTravisYml($existingYmlPath)
{
$existingYamlText = file_get_contents($existingYmlPath);
foreach ($this->getRootSectionsFromYaml($existingYamlText) as $sectionName => $offset) {
$section = $this->getRootSectionText($existingYamlText, $offset);
if ($sectionName == 'env') {
$this->existingEnv = $section;
} else if ($sectionName == 'matrix') {
$this->existingMatrix = $section;
} else if (!in_array($sectionName, self::$travisYmlSectionNames)) {
$this->extraSections .= "\n\n$sectionName:" . $section;
}
}
}
/**
* Sets the name of plugin the generated .travis.yml file is for.
*
* @param string $pluginName ie, ExamplePlugin, UserSettings, etc.
*/
public function setPlugin($pluginName)
{
$this->pluginName = $pluginName;
$customTravisBuildSteps = array();
foreach (self::$travisYmlExtendableSectionNames as $name) {
$customTravisBuildSteps[$name] = array();
$beforeStepsTemplate = $this->getPathToCustomTravisStepsFile($name, 'before');
if (file_exists($beforeStepsTemplate)) {
$customTravisBuildSteps[$name]['before'] = $this->changeIndent(file_get_contents($beforeStepsTemplate), ' ');
}
$afterStepsTemplate = $this->getPathToCustomTravisStepsFile($name, 'after');
if (file_exists($afterStepsTemplate)) {
$customTravisBuildSteps[$name]['after'] = $this->changeIndent(file_get_contents($afterStepsTemplate), ' ');
}
}
$this->customTravisBuildSteps = $customTravisBuildSteps;
}
/**
* Set extra global environment variables that should be set in the generated .travis.yml file. The entries
* should be whole statements like `"MY_VAR=myvalue"` or `"secure: mysecurevalue"`.
*
* @param string[] $extraVars
*/
public function setExtraGlobalEnvVars($extraVars)
{
$this->extraGlobalEnvVars = $extraVars;
}
/**
* Sets the self-referential command that will generate the .travis.yml file on travis.
*
* @param string $consoleCommand ie, `"./console generate:travis-yml ..."`
*/
public function setGenerateYmlCommand($consoleCommand)
{
$this->consoleCommand = addslashes($consoleCommand);
}
/**
* Sets the PHP versions to run tests against in travis.
*
* @param string[] $phpVersions ie, `array("5.3.3", "5.4", "5.5")`.
*/
public function setPhpVersions($phpVersions)
{
$this->phpVersions = $phpVersions;
}
/**
* Renders the view. See {@link Piwik\View::render()}.
*
* @return string
*/
public function render()
{
list($this->testsToRun, $this->testsToExclude) = $this->getTestsToRun();
return parent::render();
}
/**
* Extracts the name and offset of all root elements of a YAML document. This method does this by
* checking for text that starts at the beginning of a line and ends with a ':'.
*
* @param string $yamlText The YAML text to search through.
* @return array Array mapping string section names with the starting offset of the text in the YAML.
*/
private function getRootSectionsFromYaml($yamlText)
{
preg_match_all("/^[a-zA-Z_]+:/m", $yamlText, $allMatches, PREG_OFFSET_CAPTURE);
$result = array();
foreach ($allMatches[0] as $match) {
$matchLength = strlen($match[0]);
$sectionName = substr($match[0], 0, $matchLength - 1);
$result[$sectionName] = $match[1] + $matchLength;
}
return $result;
}
/**
* Gets the text of a root YAML element in a YAML doc using the name of the element and the starting
* offset of the element's text. This is accomplished by searching for the first line that doesn't
* start with whitespace after the given offset and using the text between the given offset and the
* line w/o starting whitespace.
*
* @param string $yamlText The YAML text to search through.
* @param int $offset The offset start of the YAML text (does not include the element name and colon, ie
* the offset is after `'element:'`).
* @return string
*/
private function getRootSectionText($yamlText, $offset)
{
preg_match("/^[^\s]/m", $yamlText, $endMatches, PREG_OFFSET_CAPTURE, $offset);
$endPos = isset($endMatches[0][1]) ? $endMatches[0][1] : strlen($yamlText);
return substr($yamlText, $offset, $endPos - $offset);
}
private function getTestsToRun()
{
$testsToRun = array();
$testsToExclude = array();
if ($this->isTargetPluginContainsPluginTests()) {
$testsToRun[] = array('name' => 'PluginTests',
'vars' => "MYSQL_ADAPTER=PDO_MYSQL");
$testsToRun[] = array('name' => 'PluginTests',
'vars' => "MYSQL_ADAPTER=PDO_MYSQL TEST_AGAINST_CORE=latest_stable");
$testsToExclude[] = array('description' => 'execute latest stable tests only w/ PHP 5.5',
'php' => '5.3.3',
'env' => 'TEST_SUITE=PluginTests MYSQL_ADAPTER=PDO_MYSQL TEST_AGAINST_CORE=latest_stable');
$testsToExclude[] = array('php' => '5.4',
'env' => 'TEST_SUITE=PluginTests MYSQL_ADAPTER=PDO_MYSQL TEST_AGAINST_CORE=latest_stable');
}
if ($this->isTargetPluginContainsUITests()) {
$testsToRun[] = array('name' => 'UITests',
'vars' => "MYSQL_ADAPTER=PDO_MYSQL");
$testsToExclude[] = array('description' => 'execute UI tests only w/ PHP 5.5',
'php' => '5.3.3',
'env' => 'TEST_SUITE=UITests MYSQL_ADAPTER=PDO_MYSQL');
$testsToExclude[] = array('php' => '5.4',
'env' => 'TEST_SUITE=UITests MYSQL_ADAPTER=PDO_MYSQL');
$testsToExclude[] = array('php' => '5.6',
'env' => 'TEST_SUITE=UITests MYSQL_ADAPTER=PDO_MYSQL');
}
if (!empty($this->pluginName)
&& empty($testsToRun)
) {
throw new Exception("No tests to run for this plugin, aborting .travis.yml generation.");
}
return array($testsToRun, $testsToExclude);
}
private function isTargetPluginContainsPluginTests()
{
$pluginPath = $this->getPluginRootFolder();
return $this->doesFolderContainPluginTests($pluginPath . "/tests")
|| $this->doesFolderContainPluginTests($pluginPath . "/Test");
}
private function doesFolderContainPluginTests($folderPath)
{
$testFiles = array_merge(glob($folderPath . "/**/*Test.php"), glob($folderPath . "/*Test.php"));
return !empty($testFiles);
}
private function isTargetPluginContainsUITests()
{
$pluginPath = $this->getPluginRootFolder();
return $this->doesFolderContainUITests($pluginPath . "/tests")
|| $this->doesFolderContainUITests($pluginPath . "/Test");
}
private function doesFolderContainUITests($folderPath)
{
$testFiles = array_merge(glob($folderPath . "/**/*_spec.js"), glob($folderPath . "/*_spec.js"));
return !empty($testFiles);
}
private function changeIndent($text, $newIndent)
{
$text = trim($text);
return preg_replace("/^\\s*/", $newIndent, $text);
}
public function getPluginRootFolder()
{
return PIWIK_INCLUDE_PATH . "/plugins/{$this->pluginName}";
}
private function getPathToCustomTravisStepsFile($sectionName, $type)
{
return $this->getPluginRootFolder() . "/tests/travis/$sectionName.$type.yml";
}
}