mirror of
https://github.com/DeNNiiInc/Web-Page-Performance-Test.git
synced 2026-04-26 07:26:25 +00:00
Fix: Implement Test Queue and Unique Profiles for Concurrency
This commit is contained in:
@@ -2,13 +2,57 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
|
// Simple Queue Implementation to prevent concurrency crashes
|
||||||
|
class TestQueue {
|
||||||
|
constructor() {
|
||||||
|
this.queue = [];
|
||||||
|
this.running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(task) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.queue.push({ task, resolve, reject });
|
||||||
|
this.process();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async process() {
|
||||||
|
if (this.running || this.queue.length === 0) return;
|
||||||
|
|
||||||
|
this.running = true;
|
||||||
|
const { task, resolve, reject } = this.queue.shift();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await task();
|
||||||
|
resolve(result);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
} finally {
|
||||||
|
this.running = false;
|
||||||
|
this.process(); // Process next item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const runnerQueue = new TestQueue();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a performance test using Lighthouse
|
* Run a performance test using Lighthouse (Serialized)
|
||||||
* @param {string} url - The URL to test
|
* @param {string} url - The URL to test
|
||||||
* @param {object} options - Configuration options (mobile vs desktop, etc.)
|
* @param {object} options - Configuration options (mobile vs desktop, etc.)
|
||||||
* @returns {Promise<object>} - Test results
|
* @returns {Promise<object>} - Test results
|
||||||
*/
|
*/
|
||||||
async function runTest(url, options = {}) {
|
async function runTest(url, options = {}) {
|
||||||
|
// Wrap the actual test logic in a queue task
|
||||||
|
return runnerQueue.add(async () => {
|
||||||
|
return _executeTest(url, options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function to execute the test (NOT exported directly)
|
||||||
|
*/
|
||||||
|
async function _executeTest(url, options) {
|
||||||
// Dynamically import dependencies (ESM)
|
// Dynamically import dependencies (ESM)
|
||||||
const { default: lighthouse } = await import('lighthouse');
|
const { default: lighthouse } = await import('lighthouse');
|
||||||
const chromeLauncher = await import('chrome-launcher');
|
const chromeLauncher = await import('chrome-launcher');
|
||||||
@@ -25,10 +69,25 @@ async function runTest(url, options = {}) {
|
|||||||
// Launch Chrome
|
// Launch Chrome
|
||||||
console.log('Launching Chrome...');
|
console.log('Launching Chrome...');
|
||||||
const chromePath = process.platform === 'linux' ? '/usr/bin/chromium' : undefined;
|
const chromePath = process.platform === 'linux' ? '/usr/bin/chromium' : undefined;
|
||||||
|
|
||||||
|
// Use a unique user data dir to prevent profile locking issues
|
||||||
|
const userDataDir = path.join(os.tmpdir(), `lh-profile-${uuidv4()}`);
|
||||||
|
|
||||||
|
// Ensure tmp dir exists if likely to crash
|
||||||
|
if (!fs.existsSync(userDataDir)) {
|
||||||
|
fs.mkdirSync(userDataDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
const chrome = await chromeLauncher.launch({
|
const chrome = await chromeLauncher.launch({
|
||||||
chromeFlags: ['--headless', '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
|
chromeFlags: [
|
||||||
|
'--headless',
|
||||||
|
'--no-sandbox',
|
||||||
|
'--disable-setuid-sandbox',
|
||||||
|
'--disable-dev-shm-usage',
|
||||||
|
`--user-data-dir=${userDataDir}` // Unique profile for this run
|
||||||
|
],
|
||||||
chromePath: chromePath,
|
chromePath: chromePath,
|
||||||
port: 0 // Force random port to avoid concurrency issues with default 9222
|
port: 0 // Force random port
|
||||||
});
|
});
|
||||||
|
|
||||||
// Lighthouse Config
|
// Lighthouse Config
|
||||||
@@ -88,6 +147,13 @@ async function runTest(url, options = {}) {
|
|||||||
|
|
||||||
await chrome.kill();
|
await chrome.kill();
|
||||||
|
|
||||||
|
// Cleanup User Data Dir
|
||||||
|
try {
|
||||||
|
fs.rmSync(userDataDir, { recursive: true, force: true });
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to cleanup temp profile:', e);
|
||||||
|
}
|
||||||
|
|
||||||
// Insert into Database
|
// Insert into Database
|
||||||
// We expect user_uuid and user_ip to be passed in options, or handle gracefully if not
|
// We expect user_uuid and user_ip to be passed in options, or handle gracefully if not
|
||||||
const userUuid = options.userUuid || 'anonymous';
|
const userUuid = options.userUuid || 'anonymous';
|
||||||
@@ -120,6 +186,8 @@ async function runTest(url, options = {}) {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (chrome) await chrome.kill();
|
if (chrome) await chrome.kill();
|
||||||
|
// Try cleanup again in error case
|
||||||
|
try { fs.rmSync(userDataDir, { recursive: true, force: true }); } catch (e) {}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,6 +228,9 @@ async function getHistory(userUuid, userIp) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Need os for tmpdir
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
runTest,
|
runTest,
|
||||||
getHistory
|
getHistory
|
||||||
|
|||||||
Reference in New Issue
Block a user