Files
pioarduino-vscode-debug/__tests__/backend/rtos-thread-mapping.test.ts
T
2026-05-01 22:03:05 +02:00

110 lines
3.9 KiB
TypeScript

import { GDBDebugSession } from '../../src/backend/adapter'
function makeThreadInfo(id: number, label: string) {
return [
[
'id',
String(id),
],
[
'target-id',
`Thread ${id}`,
],
[
'details',
label,
],
]
}
describe('RTOS thread mapping', () => {
test('threadsRequest enriches thread labels with RTOS metadata', async () => {
const session = new GDBDebugSession() as any
session.stopped = true
session.currentThreadId = 2
session.args = { rtos: { enabled: true, type: 'auto' } }
session.miDebugger = {
sendCommand: jest.fn().mockImplementation((command: string) => {
if (command === 'thread-list-ids') {
return Promise.resolve({
result: (path: string) => {
if (path === 'thread-ids') return [['id', '1'], ['id', '2']]
if (path === 'current-thread-id') return '2'
return undefined
},
})
}
if (command === 'thread-info 1') {
return Promise.resolve({ result: (path: string) => (path === 'threads' ? [makeThreadInfo(1, 'main')] : undefined) })
}
if (command === 'thread-info 2') {
return Promise.resolve({ result: (path: string) => (path === 'threads' ? [makeThreadInfo(2, 'worker')] : undefined) })
}
return Promise.reject(new Error(`unexpected ${command}`))
}),
}
session.rtosManager = {
load: jest.fn().mockResolvedValue({
type: 'freertos',
threads: [
{
id: 2,
gdbThreadId: 2,
name: 'IdleTask',
state: 'running',
priority: 1,
source: 'freertos',
},
],
}),
}
session.sendResponse = jest.fn()
session.sendErrorResponse = jest.fn()
const response: any = {}
await session.threadsRequest(response)
expect(response.body.threads).toHaveLength(2)
// Order-independent: locate threads by id
const mainThread = response.body.threads.find((t: any) => t.id === 1)
const idleThread = response.body.threads.find((t: any) => t.id === 2)
expect(mainThread).toMatchObject({ id: 1, name: 'main' })
expect(idleThread).toBeDefined()
expect(idleThread.name).toContain('IdleTask')
expect(idleThread.name).toContain('running')
expect(idleThread.name).toContain('prio 1')
})
test('stackTraceRequest resolves DAP thread ids through the RTOS map', async () => {
const session = new GDBDebugSession() as any
session.miDebugger = {
getStack: jest.fn().mockResolvedValue([
{
level: '0',
address: '0x1000',
function: 'main',
fileName: 'main.c',
file: '/tmp/main.c',
line: 42,
},
]),
}
session.checkFileExists = jest.fn().mockResolvedValue(true)
session.symbolTable = { getFunctionByName: jest.fn().mockReturnValue(undefined) }
session.dapThreadIdMap = new Map([[1001, 2]])
session.sendResponse = jest.fn()
session.sendErrorResponse = jest.fn()
const response: any = {}
await session.stackTraceRequest(response, { threadId: 1001, startFrame: 0, levels: 20 })
expect(session.miDebugger.getStack).toHaveBeenCalledWith(2, 0, 20)
expect(response.body.stackFrames).toHaveLength(1)
expect(response.body.stackFrames[0].name).toBe('main@0x1000')
})
})