tia
Version:
Time is All (logs driven test engine with ExtJs support)
315 lines (268 loc) • 8.33 kB
text/typescript
import * as fs from 'fs';
import * as path from 'path';
import { TestInfo } from '../engine/test-info';
import * as textUtils from './text-utils';
import * as childProcess from 'child_process';
/* globals gT, gIn */
// TODO: сделать так, чтобы тесты работали под правами специально заведеного юзера.
// У этого юзера будет доступ к тестовой директории только на чтение.
// Права на запись у него будут только в его home.
// Перед тестом (после подключения всех require), юзер процесса будет подменяться (process.setuid(id)).
// Весь test suite будет копироваться в директорию юзера и работать оттуда.
// gT.fileUtilsCheckPath = function(path){
//
// };
/**
* Checks that file or directory absent by statSync, without checking for catch reason (ENOENT or no).
*
* @param fileOrDirPath
* @returns {boolean}
*/
export function isAbsent(fileOrDirPath: string) {
try {
fs.statSync(fileOrDirPath);
} catch (e) {
return true;
}
return false;
}
export function isEtalonAbsent(jsPath: string) {
const etPath = textUtils.jsToEt(jsPath);
try {
fs.statSync(etPath);
} catch (e) {
return true;
}
return false;
}
export function safeUnlink(fileOrDirPath: string) {
try {
fs.unlinkSync(fileOrDirPath);
} catch (e) {
// No handling intentionaly.
}
}
export function safeReadFile(fileOrDirPath: string) {
let res = '';
try {
res = fs.readFileSync(fileOrDirPath, gT.engineConsts.logEncoding);
} catch (e) {
gIn.tracer.msg3(`safeReadFile: No such file: ${fileOrDirPath}`);
// No handling intentionaly.
}
return res;
}
export function backupDif(fileOrDirPath: string) {
try {
fs.renameSync(fileOrDirPath, `${fileOrDirPath}.old`);
} catch (e) {
// No handling intentionaly.
}
}
export function isDirectory(fileOrDirPath: string) {
let stat;
try {
stat = fs.statSync(fileOrDirPath);
} catch (e) {
return false;
}
return stat.isDirectory();
}
export function rmPngs(jsPath: string) {
const dir = path.dirname(jsPath);
const start = path.basename(textUtils.changeExt(jsPath, ''));
fs.readdirSync(dir).forEach(fileName => {
const filePath = path.join(dir, fileName);
if (!isDirectory(filePath) && filePath.endsWith('.png') && fileName.startsWith(start)) {
safeUnlink(filePath);
}
});
}
export function rmDir(dir: string, removeSelf?: boolean) {
let files;
try {
files = fs.readdirSync(dir);
} catch (e) {
return;
}
if (files.length > 0) {
for (let i = 0; i < files.length; i++) {
const filePath = path.join(dir, files[i]);
const fdata = fs.lstatSync(filePath);
try {
if (fdata.isSymbolicLink()) {
fs.unlinkSync(filePath);
}
if (fdata.isFile()) {
fs.unlinkSync(filePath);
}
} catch (e) {
gIn.tracer.err(`rmDir: ${gIn.textUtils.excToStr(e)}`);
}
if (fdata.isDirectory()) {
rmDir(filePath, true);
}
}
}
if (removeSelf) {
fs.rmdirSync(dir);
}
}
export function emptyDir(dir: string) {
rmDir(dir);
}
export function safeRename(oldPath: string, newPath: string) {
safeUnlink(newPath);
try {
fs.renameSync(oldPath, newPath);
} catch (e) {
// No handling intentionaly.
}
}
// Removes file, if exists.
export function createEmptyFileSync(fileOrDirPath: string) {
fs.closeSync(fs.openSync(fileOrDirPath, 'w'));
}
export function createEmptyLog(fileOrDirPath: string) {
gIn.logger.setLogFile(gIn.textUtils.jsToLog(fileOrDirPath));
createEmptyFileSync(gIn.logger.getLogFile());
}
export function fileToStdout(file: string) {
console.log(fs.readFileSync(file, { encoding: gT.engineConsts.logEncoding }));
}
export function fileToStderr(file: string) {
// console.error(fs.readFileSync(file, {encoding: gT.engineConsts.logEncoding}));
gIn.cLogger.errln(fs.readFileSync(file, { encoding: gT.engineConsts.logEncoding }));
}
export function saveJson(obj: any, file: string) {
fs.writeFileSync(file, JSON.stringify(obj, null, 2), { encoding: gT.engineConsts.logEncoding });
}
function collectArcPaths(dirInfo: TestInfo, arcPaths: string[]) {
if (!dirInfo.diffed) {
return;
}
// Absense of 'children' property says that it is test and not directory,
// we should not allow to use this function for not directory.
for (const curInfo of dirInfo.children!) {
// eslint-disable-line no-restricted-syntax
if (Object.prototype.hasOwnProperty.call(curInfo, 'children')) {
collectArcPaths(curInfo, arcPaths);
} else if (curInfo.diffed) {
arcPaths.push(gIn.textUtils.changeExt(curInfo.path, ''));
}
}
}
export function getDirectoryAlias(dirPath: string) {
const pathArr = dirPath.split(path.sep);
const last = pathArr.pop();
if (last !== gT.engineConsts.suiteDirName) {
throw new Error(`getDirectoryAlias: Incorrect path format: ${dirPath}`);
}
const alias = pathArr.pop();
if (!alias) {
throw new Error(`getDirectoryAlias: Incorrect path format: ${dirPath}`);
}
return alias;
}
export function archiveSuiteDir(dirInfo: TestInfo) {
if (
!gT.cLParams.enableEmail ||
!gT.suiteConfig.attachArchiveToMail ||
!gT.suiteConfig.mailRecipientList
) {
return null;
}
const alias = getDirectoryAlias(dirInfo.path);
const arcName = `${alias}_${new Date()
.toISOString()
.slice(0, 19)
.replace(/:/g, '_')}.zip`;
const suitePathWOSuiteDirName = path.resolve(gT.cLParams.rootDir, dirInfo.path, '..');
const wholeSuiteArcRelPath = path.relative(suitePathWOSuiteDirName, dirInfo.path);
const resultArchivePath = path.resolve(suitePathWOSuiteDirName, arcName);
if (!gT.suiteConfig.attachOnlyDiffs) {
try {
childProcess.execSync(
`cd ${suitePathWOSuiteDirName} && zip -r ${arcName} "${wholeSuiteArcRelPath}"/*`,
{
stdio: [null, null, null],
windowsHide: true,
}
);
} catch (e) {
gIn.tracer.err(`zip stderr: ${e.stderr.toString()}`);
gIn.tracer.err(`zip stdout: ${e.stdout.toString()}`);
throw new Error('Error with zip (whole)');
}
return resultArchivePath;
}
// Only diffs handling.
const diffedPathsToArc: string[] = [];
collectArcPaths(dirInfo, diffedPathsToArc);
if (diffedPathsToArc.length === 0) {
gIn.tracer.msg3('Archieve: No diffs, no archieve');
return null;
}
const diffedRelativePathsToArc = diffedPathsToArc.map(diffedArcPath => {
const relativePath = `"${path.relative(suitePathWOSuiteDirName, diffedArcPath)}"*`;
return relativePath;
});
try {
childProcess.execSync(
`cd ${suitePathWOSuiteDirName} && zip -r ${arcName} ${diffedRelativePathsToArc.join(' ')}`,
{ stdio: [null, null, null] }
);
} catch (e) {
gIn.tracer.err(`zip stderr: ${e.stderr.toString()}`);
gIn.tracer.err(`zip stdout: ${e.stdout.toString()}`);
throw new Error('Error with zip (diffs only)');
}
return resultArchivePath;
}
export function mkdir(dirPath: string) {
try {
fs.mkdirSync(dirPath, { recursive: true });
} catch (e) {
gIn.tracer.msg3(e.toString());
}
}
export function mkDirRecursive(targetDir: string, subDirsArr: string[]) {
let curPath = targetDir;
subDirsArr.forEach(dir => {
curPath = path.join(curPath, dir);
mkdir(curPath);
});
}
export function rmLastDirSep(dir: string) {
if (dir.endsWith(path.sep)) {
return dir.slice(0, -1);
}
return dir;
}
// One of filenames.
export function whichDirContain(
base: string,
fileNames: string[],
excludeThisBase?: string
): string | null {
const dirList = fs.readdirSync(base);
for (const name of dirList) {
if (name === 'node_modules') {
continue;
}
const newBase = path.join(base, name);
if (newBase === excludeThisBase) {
continue;
}
if (fileNames.includes(name)) {
return base;
}
if (fs.statSync(newBase).isDirectory()) {
const dir = whichDirContain(newBase, fileNames, excludeThisBase);
if (dir) {
return dir;
}
}
}
return null;
}