Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions backend/restapi_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
import argparse
import os
import signal
import sys
from flask import Flask, request, jsonify
from flasgger import Swagger
Expand Down Expand Up @@ -34,7 +35,7 @@
args = parser.parse_args()

# setup app logger
log_setup(filename=args.logfile, max_bytes=args.maxbytes*1024, backup_count=args.backupcount)
log_setup(filename=args.logfile, max_bytes=args.maxbytes * 1024, backup_count=args.backupcount)

# Check if the device_file exists
if os.path.exists(args.device_file) == False:
Expand All @@ -54,7 +55,7 @@
"swagger": "2.0",
"info": {
"title": "RPE Backend API",
"description": "The RPE Backend APIs which consumed by the RPE frontend for power and thermal estimation of the Rapid Silicon devices.",
"description": "The RPE Backend APIs which are consumed by the RPE frontend for power and thermal estimation of the Rapid Silicon devices.",
"version": "0.1.0"
}
}
Expand All @@ -81,6 +82,25 @@
log(f"{request.method} {request.url} {response.status_code} - DONE")
return response

# Graceful shutdown function
def shutdown_server():
log("Shutting down server...")
func = request.environ.get('werkzeug.server.shutdown')
if func is not None:
func()

Check warning on line 90 in backend/restapi_server.py

View check run for this annotation

Codecov / codecov/patch

backend/restapi_server.py#L87-L90

Added lines #L87 - L90 were not covered by tests
else:
log("Server shutdown function not found.", RsLogLevel.ERROR)

Check warning on line 92 in backend/restapi_server.py

View check run for this annotation

Codecov / codecov/patch

backend/restapi_server.py#L92

Added line #L92 was not covered by tests

# Signal handler for smooth shutdown
def signal_handler(signal_received, frame):
log(f"Signal {signal_received} received, initiating shutdown...")
shutdown_server()
sys.exit(0)

Check warning on line 98 in backend/restapi_server.py

View check run for this annotation

Codecov / codecov/patch

backend/restapi_server.py#L96-L98

Added lines #L96 - L98 were not covered by tests

# Register the signal handler for SIGINT (Ctrl+C) and SIGTERM
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)

# log app server started
log("App server is running...")

Expand Down
38 changes: 31 additions & 7 deletions cleanup.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
const isWindows = process.platform === 'win32';
const os = require('os');
const platform = os.platform();

const kill = (process) => {
if (isWindows) {
const kill = require('tree-kill');
kill(process.pid);
} else {
process.kill('SIGINT');
}
console.log(`Attempting to kill process with PID: ${process.pid}`);
const { spawn } = require('child_process');
if (platform === 'win32') {
// used taskkill for Windows to terminate the process tree
const taskKill = spawn('taskkill', ['/PID', process.pid, '/T', '/F']);
taskKill.on('close', (code) => {
if (code === 0) {
console.log('Process killed successfully on Windows.');
} else {
console.error(`taskkill failed with exit code: ${code}`);
}
});
} else if (platform === 'darwin' || platform === 'linux') {
const taskKill = spawn('kill', ['-9', process.pid]);
taskKill.on('close', (code) => {
if (code === 0) {
console.log('Process killed successfully on Unix.');
} else {
console.error(`taskkill failed with exit code: ${code}`);
}
});
} else {
try {
process.kill('SIGINT');
console.log('SIGINT sent to process');
} catch (error) {
console.error(`Failed to send SIGINT: ${error.message}`);
}
}
};

module.exports = { kill };
5 changes: 4 additions & 1 deletion main.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,10 @@ app.whenReady().then(() => {
});
});

app.on('window-all-closed', () => {
app.on("before-quit", function () {
kill(serverProcess);
})

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
102 changes: 102 additions & 0 deletions tests/e2e/restart.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const { _electron: electron } = require('playwright');
const { test, expect } = require('@playwright/test');
const { execSync } = require('child_process');
const os = require('os');

// Helper function to find a process ID by name
function getProcessIdByName(processName,pid) {
try {
const platform = os.platform();
let output;

if (platform === 'win32') {
// Fetch all processes
output = execSync(`tasklist /FO CSV`).toString();
const lines = output.split('\n');
for (const line of lines) {
if (line.includes(processName)) {
const parts = line.split(',');
const pid = parseInt(parts[1].replace(/"/g, '').trim(), 10);
console.log(`Found process: ${line}`);
return pid; // Return the PID of the process
}
}
} else if (platform === 'darwin' || platform === 'linux') {
// Fetch the PID for Unix-based systems
output = execSync(`pgrep "${processName}" -P ${pid}`).toString();
console.log(`Found backend PID: ${output.trim()}`);
return parseInt(output.trim(), 10); // Return the PID
}
} catch (error) {
console.error(`Error fetching process ID for ${processName}:`, error.message);
return null;
}
}

// Helper function to check if a process is running by PID
function isProcessRunning(pid) {
try {
const platform = os.platform();
let output;

if (platform === 'win32') {
output = execSync(`tasklist /FI "PID eq ${pid}"`).toString();
return output.includes(`${pid}`);
} else if (platform === 'darwin' || platform === 'linux') {
output = execSync(`ps -aux | grep ${pid}`).toString();
return output.includes(`main.js`);
}
} catch (error) {
console.error(`Error checking for process ${pid}:`, error.message);
return false;
}
}

test('Launch and close Electron app 10 times, ensuring backend termination', async () => {
for (let i = 0; i < 10; i++) {
console.log(`Iteration ${i + 1}: Launching and closing Electron app.`);

// Launch the Electron app
const app = await electron.launch({ args: ['main.js'] });
const pid = app.process().pid;
const window = await app.firstWindow();
console.log(`Frontend PID: ${pid}`)
// Selecting the device (MPW1 Gemini)
const deviceDropdown = await window.waitForSelector('#deviceId');
await deviceDropdown.selectOption('MPW1');

let backendProcessName = '';
if (os.platform() === 'win32') {
backendProcessName = 'python.exe';
} else if (os.platform() === 'darwin' || os.platform() === 'linux') {
backendProcessName = 'python';
}
console.log(`The backend process name is: ${backendProcessName}`);
const backendPid = getProcessIdByName(backendProcessName,pid);
if (!backendPid) {
console.error('Failed to fetch backend PID.');
break;
}
console.log(`Backend PID: ${backendPid}`);
// Close the Electron app
await app.close();
// Wait for a moment to allow processes to terminate
await new Promise((resolve) => setTimeout(resolve, 3000));
// Check if the Electron app is still running
let frontendRunning = isProcessRunning(pid);
if (frontendRunning) {
console.error(`Iteration ${i + 1}: Electron app could not be terminated.`);
break;
}
// Check if the backend process is still running
let backendRunning = isProcessRunning(backendPid);
if (backendRunning) {
console.error(
`Iteration ${i + 1}: Backend process ${backendPid} could not be terminated.`
);
break;
} else {
console.log(`Iteration ${i + 1}: Backend process terminated successfully.`);
}
}
});
Loading