Skip to content

Commit 95c5fe1

Browse files
authored
Added Signal Handlers to the backend for shutting down the process and adding restart.test.js (#268)
1 parent f7aa4b6 commit 95c5fe1

File tree

4 files changed

+159
-10
lines changed

4 files changed

+159
-10
lines changed

backend/restapi_server.py

+22-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#
55
import argparse
66
import os
7+
import signal
78
import sys
89
from flask import Flask, request, jsonify
910
from flasgger import Swagger
@@ -34,7 +35,7 @@ def main():
3435
args = parser.parse_args()
3536

3637
# setup app logger
37-
log_setup(filename=args.logfile, max_bytes=args.maxbytes*1024, backup_count=args.backupcount)
38+
log_setup(filename=args.logfile, max_bytes=args.maxbytes * 1024, backup_count=args.backupcount)
3839

3940
# Check if the device_file exists
4041
if os.path.exists(args.device_file) == False:
@@ -54,7 +55,7 @@ def main():
5455
"swagger": "2.0",
5556
"info": {
5657
"title": "RPE Backend API",
57-
"description": "The RPE Backend APIs which consumed by the RPE frontend for power and thermal estimation of the Rapid Silicon devices.",
58+
"description": "The RPE Backend APIs which are consumed by the RPE frontend for power and thermal estimation of the Rapid Silicon devices.",
5859
"version": "0.1.0"
5960
}
6061
}
@@ -81,6 +82,25 @@ def after_request(response):
8182
log(f"{request.method} {request.url} {response.status_code} - DONE")
8283
return response
8384

85+
# Graceful shutdown function
86+
def shutdown_server():
87+
log("Shutting down server...")
88+
func = request.environ.get('werkzeug.server.shutdown')
89+
if func is not None:
90+
func()
91+
else:
92+
log("Server shutdown function not found.", RsLogLevel.ERROR)
93+
94+
# Signal handler for smooth shutdown
95+
def signal_handler(signal_received, frame):
96+
log(f"Signal {signal_received} received, initiating shutdown...")
97+
shutdown_server()
98+
sys.exit(0)
99+
100+
# Register the signal handler for SIGINT (Ctrl+C) and SIGTERM
101+
signal.signal(signal.SIGINT, signal_handler)
102+
signal.signal(signal.SIGTERM, signal_handler)
103+
84104
# log app server started
85105
log("App server is running...")
86106

cleanup.js

+31-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,36 @@
1-
const isWindows = process.platform === 'win32';
1+
const os = require('os');
2+
const platform = os.platform();
23

34
const kill = (process) => {
4-
if (isWindows) {
5-
const kill = require('tree-kill');
6-
kill(process.pid);
7-
} else {
8-
process.kill('SIGINT');
9-
}
5+
console.log(`Attempting to kill process with PID: ${process.pid}`);
6+
const { spawn } = require('child_process');
7+
if (platform === 'win32') {
8+
// used taskkill for Windows to terminate the process tree
9+
const taskKill = spawn('taskkill', ['/PID', process.pid, '/T', '/F']);
10+
taskKill.on('close', (code) => {
11+
if (code === 0) {
12+
console.log('Process killed successfully on Windows.');
13+
} else {
14+
console.error(`taskkill failed with exit code: ${code}`);
15+
}
16+
});
17+
} else if (platform === 'darwin' || platform === 'linux') {
18+
const taskKill = spawn('kill', ['-9', process.pid]);
19+
taskKill.on('close', (code) => {
20+
if (code === 0) {
21+
console.log('Process killed successfully on Unix.');
22+
} else {
23+
console.error(`taskkill failed with exit code: ${code}`);
24+
}
25+
});
26+
} else {
27+
try {
28+
process.kill('SIGINT');
29+
console.log('SIGINT sent to process');
30+
} catch (error) {
31+
console.error(`Failed to send SIGINT: ${error.message}`);
32+
}
33+
}
1034
};
1135

1236
module.exports = { kill };

main.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,10 @@ app.whenReady().then(() => {
317317
});
318318
});
319319

320-
app.on('window-all-closed', () => {
320+
app.on("before-quit", function () {
321321
kill(serverProcess);
322+
})
323+
324+
app.on('window-all-closed', () => {
322325
if (process.platform !== 'darwin') app.quit();
323326
});

tests/e2e/restart.test.js

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
const { _electron: electron } = require('playwright');
2+
const { test, expect } = require('@playwright/test');
3+
const { execSync } = require('child_process');
4+
const os = require('os');
5+
6+
// Helper function to find a process ID by name
7+
function getProcessIdByName(processName,pid) {
8+
try {
9+
const platform = os.platform();
10+
let output;
11+
12+
if (platform === 'win32') {
13+
// Fetch all processes
14+
output = execSync(`tasklist /FO CSV`).toString();
15+
const lines = output.split('\n');
16+
for (const line of lines) {
17+
if (line.includes(processName)) {
18+
const parts = line.split(',');
19+
const pid = parseInt(parts[1].replace(/"/g, '').trim(), 10);
20+
console.log(`Found process: ${line}`);
21+
return pid; // Return the PID of the process
22+
}
23+
}
24+
} else if (platform === 'darwin' || platform === 'linux') {
25+
// Fetch the PID for Unix-based systems
26+
output = execSync(`pgrep "${processName}" -P ${pid}`).toString();
27+
console.log(`Found backend PID: ${output.trim()}`);
28+
return parseInt(output.trim(), 10); // Return the PID
29+
}
30+
} catch (error) {
31+
console.error(`Error fetching process ID for ${processName}:`, error.message);
32+
return null;
33+
}
34+
}
35+
36+
// Helper function to check if a process is running by PID
37+
function isProcessRunning(pid) {
38+
try {
39+
const platform = os.platform();
40+
let output;
41+
42+
if (platform === 'win32') {
43+
output = execSync(`tasklist /FI "PID eq ${pid}"`).toString();
44+
return output.includes(`${pid}`);
45+
} else if (platform === 'darwin' || platform === 'linux') {
46+
output = execSync(`ps -aux | grep ${pid}`).toString();
47+
return output.includes(`main.js`);
48+
}
49+
} catch (error) {
50+
console.error(`Error checking for process ${pid}:`, error.message);
51+
return false;
52+
}
53+
}
54+
55+
test('Launch and close Electron app 10 times, ensuring backend termination', async () => {
56+
for (let i = 0; i < 10; i++) {
57+
console.log(`Iteration ${i + 1}: Launching and closing Electron app.`);
58+
59+
// Launch the Electron app
60+
const app = await electron.launch({ args: ['main.js'] });
61+
const pid = app.process().pid;
62+
const window = await app.firstWindow();
63+
console.log(`Frontend PID: ${pid}`)
64+
// Selecting the device (MPW1 Gemini)
65+
const deviceDropdown = await window.waitForSelector('#deviceId');
66+
await deviceDropdown.selectOption('MPW1');
67+
68+
let backendProcessName = '';
69+
if (os.platform() === 'win32') {
70+
backendProcessName = 'python.exe';
71+
} else if (os.platform() === 'darwin' || os.platform() === 'linux') {
72+
backendProcessName = 'python';
73+
}
74+
console.log(`The backend process name is: ${backendProcessName}`);
75+
const backendPid = getProcessIdByName(backendProcessName,pid);
76+
if (!backendPid) {
77+
console.error('Failed to fetch backend PID.');
78+
break;
79+
}
80+
console.log(`Backend PID: ${backendPid}`);
81+
// Close the Electron app
82+
await app.close();
83+
// Wait for a moment to allow processes to terminate
84+
await new Promise((resolve) => setTimeout(resolve, 3000));
85+
// Check if the Electron app is still running
86+
let frontendRunning = isProcessRunning(pid);
87+
if (frontendRunning) {
88+
console.error(`Iteration ${i + 1}: Electron app could not be terminated.`);
89+
break;
90+
}
91+
// Check if the backend process is still running
92+
let backendRunning = isProcessRunning(backendPid);
93+
if (backendRunning) {
94+
console.error(
95+
`Iteration ${i + 1}: Backend process ${backendPid} could not be terminated.`
96+
);
97+
break;
98+
} else {
99+
console.log(`Iteration ${i + 1}: Backend process terminated successfully.`);
100+
}
101+
}
102+
});

0 commit comments

Comments
 (0)