N°2982 - speedup themes/scss compilation during setup

This commit is contained in:
odain
2020-05-01 01:04:53 +02:00
parent 6b2b56cf72
commit 2d8b888a18
11 changed files with 6367 additions and 21 deletions

View File

@@ -0,0 +1,333 @@
<?php
use Combodo\iTop\Test\UnitTest\ItopTestCase;
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
* @covers utils
*/
class ThemeHandlerTest extends ItopTestCase
{
private $compileCSSServiceMock;
private $cssPath;
private $jsonThemeParamFile;
private $tmpDir;
public function setUp()
{
parent::setUp();
require_once(APPROOT.'application/themehandler.class.inc.php');
require_once(APPROOT.'setup/modelfactory.class.inc.php');
$this->compileCSSServiceMock = $this->createMock('CompileCSSService');
ThemeHandler::mockCompileCSSService($this->compileCSSServiceMock);
$this->tmpDir=$this->tmpdir();
if (!is_dir($this->tmpDir ."/branding"))
{
@mkdir($this->tmpDir."/branding");
}
@mkdir($this->tmpDir."/branding/themes/");
@mkdir($this->tmpDir."/branding/themes/basque-red");
$this->cssPath = $this->tmpDir . '/branding/themes/basque-red/main.css';
$this->jsonThemeParamFile = $this->tmpDir . '/branding/themes/basque-red/theme-parameters.json';
$this->recurse_copy(APPROOT."/test/application/theme-handler/expected/css", $this->tmpDir."/branding/css");
}
function tmpdir() {
$tmpfile=tempnam(sys_get_temp_dir(),'');
if (file_exists($tmpfile))
{
unlink($tmpfile);
}
mkdir($tmpfile);
if (is_dir($tmpfile))
{
return $tmpfile;
}
return sys_get_temp_dir();
}
public function recurse_copy($src,$dst) {
$dir = opendir($src);
@mkdir($dst);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($src . '/' . $file) ) {
$this->recurse_copy($src . '/' . $file,$dst . '/' . $file);
}
else {
copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
closedir($dir);
}
public function testGetSignature()
{
$sig = ThemeHandler::GetSignature(APPROOT.'test/application/theme-handler/expected/themes/basque-red/main.css');
$expect_sig=<<<JSON
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
JSON;
$this->assertEquals($expect_sig,$sig);
}
public function testGetVarSignature()
{
$sig=<<<JSON
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
JSON;
$var_sig = ThemeHandler::GetVarSignature($sig);
$this->assertEquals("37c31105548fce44fecca5cb34e455c9",$var_sig);
}
/**
* @param bool $readFromParamAttributeFromJson
*
* @throws \CoreException
* @dataProvider CompileThemesProviderWithoutCss
*/
public function testCompileThemeWithoutCssFile_FocusOnParamAttribute($readFromParamAttributeFromJson=false)
{
$expectJsonFilePath = APPROOT.'test/application/theme-handler/expected/themes/basque-red/theme-parameters.json';
$expectedThemeParamJson = file_get_contents($expectJsonFilePath);
$aThemeParameters = json_decode($expectedThemeParamJson, true);
if (is_file($this->jsonThemeParamFile))
{
unlink($this->jsonThemeParamFile);
}
if (is_file($this->cssPath))
{
unlink($this->cssPath);
}
$this->compileCSSServiceMock->expects($this->exactly(1))
->method("CompileCSSFromSASS")
->willReturn("====CSSCOMPILEDCONTENT====");
if($readFromParamAttributeFromJson)
{
copy($expectJsonFilePath, $this->jsonThemeParamFile);
ThemeHandler::CompileTheme('basque-red', true, null, array($this->tmpDir.'/branding/themes/'), $this->tmpDir);
}
else
{
ThemeHandler::CompileTheme('basque-red', true, $aThemeParameters, array($this->tmpDir.'/branding/themes/'), $this->tmpDir);
}
$this->assertTrue(is_file($this->cssPath));
$this->assertEquals($expectedThemeParamJson, file_get_contents($this->jsonThemeParamFile));
$this->assertEquals(file_get_contents(APPROOT . 'test/application/theme-handler/expected/themes/basque-red/main.css'), file_get_contents($this->cssPath));
}
public function CompileThemesProviderWithoutCss()
{
return array(
"pass ParamAttributes and Save them in Json" => array(false),
"use them from saved json" => array(true)
);
}
/**
* @param $ThemeParametersJson
*
* @param int $CompileCount
*
* @throws \CoreException
* @dataProvider CompileThemesProviderEmptyArray
*/
public function testCompileThemesEmptyArray($ThemeParametersJson, $CompileCount=0)
{
$cssPath = $this->tmpDir . '/branding/themes/basque-red/main.css';
copy(APPROOT . 'test/application/theme-handler/expected/themes/basque-red/main.css', $cssPath);
$this->compileCSSServiceMock->expects($this->exactly($CompileCount))
->method("CompileCSSFromSASS")
->willReturn("====CSSCOMPILEDCONTENT====");
ThemeHandler::CompileTheme('basque-red', true, json_decode($ThemeParametersJson, true), array($this->tmpDir.'/branding/themes/'), $this->tmpDir);
}
public function CompileThemesProviderEmptyArray()
{
$emptyImports = '{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"imports":[],"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}';
$emptyStyleSheets='{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"imports":{"css-variables":"..\/css\/css-variables.scss"},"stylesheets":[]}';
$emptyVars='{"variables":[],"imports":{"css-variables":"..\/css\/css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}';
return array(
"empty imports" => array($emptyImports),
"empty styles" => array($emptyStyleSheets),
"empty vars" => array($emptyVars, 1),
);
}
/**
* @param $ThemeParametersJson
* @param $CompileCSSFromSASSCount
* @param int $missingFile
* @param int $filesTouchedRecently
* @param int $fileMd5sumModified
* @param null $fileToTest
*
* @param null $expected_maincss_path
*
* @throws \CoreException
* @dataProvider CompileThemesProvider
*/
public function testCompileThemes($ThemeParametersJson, $CompileCSSFromSASSCount, $missingFile=0, $filesTouchedRecently=0, $fileMd5sumModified=0, $fileToTest=null, $expected_maincss_path=null, $bSetup=true)
{
$fileToTest=$this->tmpDir.'/'.$fileToTest;
$cssPath = $this->tmpDir . '/branding/themes/basque-red/main.css';
copy(APPROOT . 'test/application/theme-handler/expected/themes/basque-red/main.css', $cssPath);
if ($missingFile==1)
{
unlink($fileToTest);
}
if ($filesTouchedRecently==1)
{
sleep(1);
touch($fileToTest);
}
if ($fileMd5sumModified==1)
{
sleep(1);
file_put_contents($fileToTest, "###\n".file_get_contents($fileToTest));
}
$this->compileCSSServiceMock->expects($this->exactly($CompileCSSFromSASSCount))
->method("CompileCSSFromSASS")
->willReturn("====CSSCOMPILEDCONTENT====");
ThemeHandler::CompileTheme('basque-red', $bSetup, json_decode($ThemeParametersJson, true), array($this->tmpDir.'/branding/themes/'), $this->tmpDir);
if ($CompileCSSFromSASSCount==1)
{
$this->assertEquals(file_get_contents(APPROOT . $expected_maincss_path), file_get_contents($cssPath));
}
}
/**
* @return array
*/
public function CompileThemesProvider()
{
$modifiedVariableThemeParameterJson='{"variables":{"brand-primary1":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"imports":{"css-variables":"..\/css\/css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}';
$initialThemeParamJson='{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"imports":{"css-variables":"..\/css\/css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}';
$import_file_path = '/branding/css/css-variables.scss';
$importmodified_maincss="test/application/theme-handler/expected/themes/basque-red/main_importmodified.css";
$varchanged_maincss="test/application/theme-handler/expected/themes/basque-red/main_varchanged.css";
$stylesheet_maincss="test/application/theme-handler/expected/themes/basque-red/main_stylesheet.css";
$stylesheet_file_path = '/branding/css/light-grey.scss';
return array(
"setup context: variables list modified without any file touched" => array($modifiedVariableThemeParameterJson, 1,0,0,0,$import_file_path, $varchanged_maincss),
"setup context: variables list modified with files touched" => array($modifiedVariableThemeParameterJson, 1,0,1,0,$import_file_path, $varchanged_maincss, false),
"itop page/theme loading; variables list modified sans touch de fichier" => array($modifiedVariableThemeParameterJson, 0,0,0,0,$import_file_path, $varchanged_maincss, false),
//imports
"import file missing" => array($initialThemeParamJson, 0, 1, 0, 0, $import_file_path),
"import file touched" => array($initialThemeParamJson, 0, 0, 1, 0, $import_file_path),
"import file modified" => array($initialThemeParamJson, 1, 0, 0, 1, $import_file_path, $importmodified_maincss),
//stylesheets
"stylesheets file missing" => array($initialThemeParamJson, 0, 1, 0, 0, $stylesheet_file_path),
"stylesheets file touched" => array($initialThemeParamJson, 0, 0, 1, 0, $stylesheet_file_path),
"stylesheets file modified" => array($initialThemeParamJson, 1, 0, 0, 1, $stylesheet_file_path, $stylesheet_maincss)
);
}
/**
* @param $xmlDataCusto
* @dataProvider providePrecompiledStyleSheets
* @throws \Exception
*/
public function testValidatePrecompiledStyles($xmlDataCusto)
{
echo "=== datamodel custo: $xmlDataCusto\n";
$oDom = new MFDocument();
$oDom->load($xmlDataCusto);
/**DOMNodeList **/$oThemeNodes=$oDom->GetNodes("/itop_design/branding/themes/theme");
$this->assertNotNull($oThemeNodes);
// Parsing themes from DM
foreach($oThemeNodes as $oTheme)
{
$sPrecompiledStylesheet = $oTheme->GetChildText('precompiled_stylesheet', '');
if (empty($sPrecompiledStylesheet))
{
continue;
}
$sThemeId = $oTheme->getAttribute('id');
echo "=== theme: $sThemeId ===\n";
$precompiledSig= ThemeHandler::GetSignature(dirname(__FILE__)."/../../datamodels/2.x/".$sPrecompiledStylesheet);
echo " precompiled signature: $precompiledSig\n";
$this->assertFalse(empty($precompiledSig), "Signature in precompiled theme '".$sThemeId."' is not retrievable (cf precompiledsheet $sPrecompiledStylesheet / datamodel $xmlDataCusto)");
$aThemeParameters = array(
'variables' => array(),
'imports' => array(),
'stylesheets' => array(),
'precompiled_stylesheet' => '',
);
$aThemeParameters['precompiled_stylesheet'] = $sPrecompiledStylesheet;
/** @var \DOMNodeList $oVariables */
$oVariables = $oTheme->GetNodes('variables/variable');
foreach($oVariables as $oVariable)
{
$sVariableId = $oVariable->getAttribute('id');
$aThemeParameters['variables'][$sVariableId] = $oVariable->GetText();
}
/** @var \DOMNodeList $oImports */
$oImports = $oTheme->GetNodes('imports/import');
foreach($oImports as $oImport)
{
$sImportId = $oImport->getAttribute('id');
$aThemeParameters['imports'][$sImportId] = $oImport->GetText();
}
/** @var \DOMNodeList $oStylesheets */
$oStylesheets = $oTheme->GetNodes('stylesheets/stylesheet');
foreach($oStylesheets as $oStylesheet)
{
$sStylesheetId = $oStylesheet->getAttribute('id');
$aThemeParameters['stylesheets'][$sStylesheetId] = $oStylesheet->GetText();
}
$compiled_json_sig = ThemeHandler::ComputeSignature($aThemeParameters, array(APPROOT.'datamodels'));
echo " current signature: $compiled_json_sig\n";
$this->assertEquals($precompiledSig, $compiled_json_sig, "Precompiled signature does not match currently compiled one on theme '".$sThemeId."' (cf precompiledsheet $sPrecompiledStylesheet / datamodel $xmlDataCusto)");
}
}
public function providePrecompiledStyleSheets()
{
$datamodelfiles=glob(dirname(__FILE__)."/../../datamodels/2.x/**/datamodel*.xml");
$test_set = array();
foreach ($datamodelfiles as $datamodelfile)
{
if (is_file($datamodelfile) &&
$datamodelfile=="/var/www/html/iTop/test/application/../../datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml")
{
$content=file_get_contents($datamodelfile);
if (strpos($content, "precompiled_stylesheet")!==false)
{
$test_set[$datamodelfile]=array($datamodelfile);
}
}
}
return $test_set;
}
}

View File

@@ -0,0 +1,126 @@
/*!
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
$version: "v2.7.0-1";
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
// Base colors
$gray-base: #000 !default;
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
$gray-dark: #444 !default;
$gray: #777 !default;
$gray-light: #808080 !default;
$gray-lighter: #ddd !default;
$gray-extra-light: #F1F1F1 !default;
$white: #FFFFFF !default;
$combodo-orange: #EA7D1E !default;
$combodo-dark-gray: #585653 !default;
$combodo-orange-dark: darken($combodo-orange, 13.8%) !default;
$combodo-orange-darker: darken($combodo-orange, 18%) !default;
$combodo-dark-gray-dark: darken($combodo-dark-gray, 13.5%) !default;
$combodo-dark-gray-darker: darken($combodo-dark-gray, 18%) !default;
// Brand colors
// - Bases
$brand-primary: $combodo-orange !default;
$brand-secondary: $combodo-dark-gray !default;
// - Shades
$brand-primary-lightest: lighten($brand-primary, 15%) !default;
$brand-primary-lighter: lighten($brand-primary, 10%) !default;
$brand-primary-light: lighten($brand-primary, 6%) !default;
$brand-primary-dark: darken($brand-primary, 6%) !default;
$brand-primary-darker: darken($brand-primary, 10%) !default;
$brand-primary-darkest: darken($brand-primary, 15%) !default;
$brand-secondary-lightest: lighten($brand-secondary, 15%) !default;
$brand-secondary-lighter: lighten($brand-secondary, 10%) !default;
$brand-secondary-light: lighten($brand-secondary, 6%) !default;
$brand-secondary-dark: darken($brand-secondary, 6%) !default;
$brand-secondary-darker: darken($brand-secondary, 10%) !default;
$brand-secondary-darkest: darken($brand-secondary, 15%) !default;
// Vars
$highlight-color: $brand-primary !default;
$grey-color: #555555 !default;
$complement-color: #1c94c4 !default;
$complement-light: #d6e8ef !default;
$frame-background-color: $gray-extra-light !default;
$text-color: #000 !default;
$box-radius: 0px !default;
$box-shadow-regular: 0 1px 1px rgba(0, 0, 0, 0.15) !default;
$body-background-color : $white !default;
$hyperlink-color: $complement-color !default;
$hyperlink-text-decoration: none !default;
////////////
// Search //
$search-form-container-color: $white !default;
$search-form-container-bg-color: $complement-color !default;
//
$search-criteria-box-color: #2D2D2D !default;
$search-criteria-box-picto-color: $brand-primary !default;
$search-criteria-box-bg-color: #EEEEEE !default;
$search-criteria-box-hover-color: $white !default;
$search-criteria-box-border-color: #CCCCCC !default;
$search-criteria-box-border: 1px solid $search-criteria-box-border-color !default;
$search-criteria-box-radius: 1px !default;
$search-criteria-more-less-details-color: #3F7294 !default;
//
$search-add-criteria-box-color: $search-criteria-box-color !default;
$search-add-criteria-box-bg-color: $white !default;
$search-add-criteria-box-hover-color: $gray-extra-light !default;
//
$search-button-box-color: $brand-primary !default;
$search-button-box-bg-color: $white !default;
$search-button-box-bg-hover-color: $gray-extra-light !default;
/////////////
// Buttons //
/////////////
// Toggle button
$toggle-button-bg-color: #CCCCCC !default;
$toggle-button-bg-checked-color: $brand-primary !default;
$toggle-button-slider-bg-color: #FFFFFF !default;
// Console elements
$summary-details-background: $grey-color !default;
$main-header-background: $frame-background-color !default;
$table-even-background: $frame-background-color !default;
$table-hover-background: #fdf5d0 !default;
$popup-menu-highlight-color: $highlight-color !default;
$popup-menu-text-color: #000 !default;
$popup-menu-background-color: #fff !default;
$popup-menu-text-higlight-color: #fff !default;
$breadcrumb-color: $grey-color !default;
$breadcrumb-highlight-color: $highlight-color !default;
// jQuery UI widgets vars
$primary-text-color: #333333 !default;
$secondary-text-color: $grey-color !default;
$error-text-color: $white !default;
$highlight-text-color: #363636 !default;
$hover-background-color: #fde17c !default;
$border-highlight-color: $brand-primary-dark !default;
$highlight-item-color: $white !default;
$content-color: #eeeeee !default;
$default-font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif !default;
$icons-filter: hue-rotate(0deg) !default;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"6b9dcbf6f6ae66d3f94ab6c19729b5ee","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"63ba7dfe2a2eba40c2596ebb2a405f0b"},"imports":[]}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1,6 @@
/*
=== SIGNATURE BEGIN ===
{"variables":"8100523d2e76a70266f3e7110e2fe5fb","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"imports":[]}
=== SIGNATURE END ===
*/
====CSSCOMPILEDCONTENT====

View File

@@ -0,0 +1 @@
{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"imports":{"css-variables":"..\/css\/css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/jqueryui.scss","main":"..\/css\/light-grey.scss"}}