appium-xcuitest-driver
Version:
Appium driver for iOS using XCUITest for backend
130 lines (119 loc) • 4.5 kB
text/typescript
import {fs} from 'appium/support';
import _ from 'lodash';
import os from 'node:os';
import path from 'node:path';
import {exec} from 'teen_process';
import {log} from '../logger';
const XCTEST_LOG_FILES_PATTERNS = [
/^Session-WebDriverAgentRunner.*\.log$/i,
/^StandardOutputAndStandardError\.txt$/i,
];
const XCTEST_LOGS_CACHE_FOLDER_PREFIX = 'com.apple.dt.XCTest';
// This map contains derived data logs folders as keys
// and values are the count of times the particular
// folder has been scheduled for removal
const derivedDataCleanupMarkers = new Map<string, number>();
/** Deletes the provided filesystem locations, logging reclaimed size when available. */
export async function clearLogs(locations: string[]): Promise<void> {
log.debug('Clearing log files');
const cleanupPromises: Promise<void>[] = [];
for (const location of locations) {
if (!(await fs.exists(location))) {
continue;
}
cleanupPromises.push(
(async () => {
let size: string | undefined;
try {
const {stdout} = await exec('du', ['-sh', location]);
size = stdout.trim().split(/\s+/)[0];
} catch {}
try {
log.debug(`Deleting '${location}'. ${size ? `Freeing ${size}.` : ''}`);
await fs.rimraf(location);
} catch (err: any) {
log.warn(`Unable to delete '${location}': ${err.message}`);
}
})(),
);
}
if (!_.isEmpty(cleanupPromises)) {
await Promise.all(cleanupPromises);
}
log.debug('Finished clearing log files');
}
/** Marks WDA logs folder for deferred cleanup across parallel sessions. */
export async function markSystemFilesForCleanup(wda: any): Promise<void> {
if (!wda || !(await wda.retrieveDerivedDataPath())) {
log.warn(
'No WebDriverAgent derived data available, so unable to mark system files for cleanup',
);
return;
}
const logsRoot = path.resolve(await wda.retrieveDerivedDataPath(), 'Logs');
const markersCount = derivedDataCleanupMarkers.get(logsRoot) ?? 0;
derivedDataCleanupMarkers.set(logsRoot, markersCount + 1);
}
/** Cleans per-session WDA logs and stale XCTest temporary logs. */
export async function clearSystemFiles(wda: any): Promise<void> {
// only want to clear the system files for the particular WDA xcode run
if (!wda || !(await wda.retrieveDerivedDataPath())) {
log.warn('No WebDriverAgent derived data available, so unable to clear system files');
return;
}
const logsRoot = path.resolve(await wda.retrieveDerivedDataPath(), 'Logs');
const existingCount = derivedDataCleanupMarkers.get(logsRoot);
if (existingCount !== undefined) {
let markersCount = existingCount;
derivedDataCleanupMarkers.set(logsRoot, --markersCount);
if (markersCount > 0) {
log.info(
`Not cleaning '${logsRoot}' folder, because the other session does not expect it to be cleaned`,
);
return;
}
}
derivedDataCleanupMarkers.set(logsRoot, 0);
// Cleaning up big temporary files created by XCTest: https://github.com/appium/appium/issues/9410
const globPattern = `${os.tmpdir()}/${XCTEST_LOGS_CACHE_FOLDER_PREFIX}*/`;
const dstFolders = await fs.glob(globPattern);
if (_.isEmpty(dstFolders)) {
log.debug(`Did not find the temporary XCTest logs root at '${globPattern}'`);
} else {
// perform the cleanup asynchronously
const promises: Promise<void>[] = [];
for (const dstFolder of dstFolders) {
const promise = (async () => {
const deletionPromises: Promise<void>[] = [];
try {
await fs.walkDir(dstFolder, true, (itemPath, isDir) => {
if (isDir) {
return;
}
const fileName = path.basename(itemPath);
if (XCTEST_LOG_FILES_PATTERNS.some((p) => p.test(fileName))) {
deletionPromises.push(fs.rimraf(itemPath));
}
});
if (deletionPromises.length) {
await Promise.all(deletionPromises);
}
} catch (e: any) {
log.debug(e.stack);
log.info(e.message);
}
})();
promises.push(promise);
}
log.debug(`Started XCTest logs cleanup in '${dstFolders}'`);
if (promises.length) {
await Promise.all(promises);
}
}
if (await fs.exists(logsRoot)) {
log.info(`Cleaning test logs in '${logsRoot}' folder`);
await clearLogs([logsRoot]);
return;
}
log.info(`There is no ${logsRoot} folder, so not cleaning files`);
}