blob: 5eb3e1dd1079b3382a639160638c4bc1e022b583 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const path = require('path');
const fse = require('fs-extra');
const fs = require('fs');
const globby = require('globby');
const {testNameFromFile} = require('./util');
const {blacklist, SVGBlacklist} = require('./blacklist');
let _tests = [];
let _testsMap = {};
let _runHash = '';
const RESULT_FILE_NAME = '__result__.json';
const RESULTS_ROOT_DIR = path.join(__dirname, 'tmp', 'result');
module.exports.RESULT_FILE_NAME = RESULT_FILE_NAME;
module.exports.RESULTS_ROOT_DIR = RESULTS_ROOT_DIR;
const TEST_HASH_SPLITTER = '__';
function convertBytes(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
if (bytes == 0) {
return 'N/A';
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return (bytes / Math.pow(1024, i)).toFixed(Math.min(1, i)) + ' ' + sizes[i]
}
class Test {
constructor(fileUrl) {
this.fileUrl = fileUrl;
this.name = testNameFromFile(fileUrl);
this.status = 'unsettled';
// Run results
this.results = []; // Screenshots
this.actualLogs = [];
this.expectedLogs = [];
this.actualErrors = [];
this.expectedErrors = [];
// Use echarts versions.
this.actualVersion = null;
this.expectedVersion = null;
// Last timestamp
this.lastRun = 0;
// Use SVG
this.useSVG = false;
}
}
/**
* hash of each run is mainly for storing the results.
* It depends on two versions and rendering mode.
*/
function getRunHash(params) {
return [
params.expectedVersion,
params.actualVersion,
params.renderer
].join(TEST_HASH_SPLITTER);
}
/**
* Parse versions and rendering mode from run hash.
*/
function parseRunHash(str) {
const parts = str.split(TEST_HASH_SPLITTER);
return {
expectedVersion: parts[0],
actualVersion: parts[1],
renderer: parts[2]
};
}
function getResultBaseDir() {
return path.join(RESULTS_ROOT_DIR, _runHash);
}
module.exports.getResultBaseDir = getResultBaseDir;
module.exports.getRunHash = getRunHash;
/**
* Check run version is same with store version.
*/
module.exports.checkStoreVersion = function (runParams) {
const storeParams = parseRunHash(_runHash);
console.log('Store ', _runHash);
return storeParams.expectedVersion === runParams.expectedVersion
&& storeParams.actualVersion === runParams.actualVersion
&& storeParams.renderer === runParams.renderer;
}
function getResultFilePath() {
return path.join(getResultBaseDir(), RESULT_FILE_NAME);
}
module.exports.getResultFilePath = getResultFilePath;
module.exports.getTestsList = function () {
return _tests;
};
module.exports.getTestByFileUrl = function (url) {
return _testsMap[url];
};
module.exports.updateTestsList = async function (
runHash,
setPendingTestToUnsettled
) {
_runHash = runHash;
_tests = [];
_testsMap = {};
_testsExists = {};
const isSVGRenderer = parseRunHash(runHash).renderer === 'svg';
fse.ensureDirSync(getResultBaseDir());
try {
let cachedStr = fs.readFileSync(getResultFilePath(), 'utf-8');
const tests = JSON.parse(cachedStr);
tests.forEach(test => {
// In somehow tests are stopped and leave the status pending.
// Set the status to unsettled again.
if (setPendingTestToUnsettled) {
if (test.status === 'pending') {
test.status = 'unsettled';
}
}
_testsMap[test.fileUrl] = test;
});
}
catch(e) {
}
// Find if there is new html file
const files = await globby('*.html', { cwd: path.resolve(__dirname, '../') });
files.forEach(fileUrl => {
if (blacklist.includes(fileUrl)) {
return;
}
if (isSVGRenderer && SVGBlacklist.includes(fileUrl)) {
return;
}
_testsExists[fileUrl] = true;
if (_testsMap[fileUrl]) {
return;
}
const test = new Test(fileUrl);
_testsMap[fileUrl] = test;
});
// Exclude tests that there is no HTML files.
Object.keys(_testsExists).forEach(key => {
_tests.push(_testsMap[key]);
});
let actionsMetaData = {};
const metaPath = path.join(__dirname, 'actions/__meta__.json');
try {
actionsMetaData = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
}
catch(e) {
console.log(e);
}
_tests.forEach(testOpt => {
testOpt.actions = actionsMetaData[testOpt.name] || 0;
});
// Save once.
module.exports.saveTestsList();
return _tests;
};
module.exports.saveTestsList = function () {
fse.ensureDirSync(getResultBaseDir());
fs.writeFileSync(getResultFilePath(), JSON.stringify(_tests, null, 2), 'utf-8');
};
module.exports.mergeTestsResults = function (testsResults) {
testsResults.forEach(testResult => {
if (_testsMap[testResult.fileUrl]) {
Object.assign(_testsMap[testResult.fileUrl], testResult);
}
});
};
module.exports.updateActionsMeta = function (testName, actions) {
let metaPath = path.join(__dirname, 'actions/__meta__.json');
try {
metaData = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
}
catch(e) {
metaData = {};
}
metaData[testName] = actions.length;
fs.writeFileSync(metaPath, JSON.stringify(
metaData, Object.keys(metaData).sort((a, b) => a.localeCompare(b)), 2
), 'utf-8');
};
async function getFolderSize(dir) {
const files = await globby(dir);
let size = 0;
for (let file of files) {
size += fs.statSync(file).size;
}
return size;
}
/**
* Get results of all runs
* @return [ { id, expectedVersion, actualVersion, renderer, lastRunTime, total, finished, passed, diskSize } ]
*/
module.exports.getAllTestsRuns = async function () {
const dirs = await globby('*', { cwd: RESULTS_ROOT_DIR, onlyDirectories: true });
const results = [];
function f(number) {
return number < 10 ? '0' + number : number;
}
function formatDate(lastRunTime) {
const date = new Date(lastRunTime);
return `${date.getFullYear()}-${f(date.getMonth() + 1)}-${f(date.getDate())} ${f(date.getHours())}:${f(date.getMinutes())}:${f(date.getSeconds())}`;
}
for (let dir of dirs) {
const params = parseRunHash(dir);
let resultJson = [];
try {
resultJson = JSON.parse(fs.readFileSync(path.join(
RESULTS_ROOT_DIR,
dir,
RESULT_FILE_NAME
), 'utf-8'));
}
catch (e) {
console.error('Invalid result ' + dir)
continue;
}
const total = resultJson.length;
let lastRunTime = 0;
let finishedCount = 0;
let passedCount = 0;
resultJson.forEach(test => {
lastRunTime = Math.max(test.lastRun, lastRunTime);
if (test.status === 'finished') {
finishedCount++;
let passed = true;
test.results.forEach(result => {
// Threshold?
if (result.diffRatio > 0.0001) {
passed = false;
}
});
if (passed) {
passedCount++;
}
}
});
if (finishedCount === 0) {
// Cleanup empty runs
await module.exports.delTestsRun(dir);
continue;
}
params.lastRunTime = lastRunTime > 0 ? formatDate(lastRunTime) : 'N/A';
params.total = total;
params.passed = passedCount;
params.finished = finishedCount;
params.id = dir;
params.diskSize = convertBytes(await getFolderSize(path.join(RESULTS_ROOT_DIR, dir)));
results.push(params);
};
return results;
}
module.exports.delTestsRun = async function (hash) {
fse.removeSync(path.join(RESULTS_ROOT_DIR, hash));
}