Skip to content

Commit 90775b3

Browse files
watsonrochdev
authored andcommitted
[DI] Improve sampling tests (#4999)
To test that multiple probes doesn't interfere with each others sample rate, this commit also adds support for multiple breakpoints in a single file.
1 parent 7101f3b commit 90775b3

File tree

3 files changed

+100
-19
lines changed

3 files changed

+100
-19
lines changed

integration-tests/debugger/basic.spec.js

+59-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ describe('Dynamic Instrumentation', function () {
1414
it('base case: target app should work as expected if no test probe has been added', async function () {
1515
const response = await t.axios.get(t.breakpoint.url)
1616
assert.strictEqual(response.status, 200)
17-
assert.deepStrictEqual(response.data, { hello: 'foo' })
17+
assert.deepStrictEqual(response.data, { hello: 'bar' })
1818
})
1919

2020
describe('diagnostics messages', function () {
@@ -54,7 +54,7 @@ describe('Dynamic Instrumentation', function () {
5454
t.axios.get(t.breakpoint.url)
5555
.then((response) => {
5656
assert.strictEqual(response.status, 200)
57-
assert.deepStrictEqual(response.data, { hello: 'foo' })
57+
assert.deepStrictEqual(response.data, { hello: 'bar' })
5858
})
5959
.catch(done)
6060
} else {
@@ -245,7 +245,7 @@ describe('Dynamic Instrumentation', function () {
245245
message: 'Hello World!',
246246
logger: {
247247
name: t.breakpoint.file,
248-
method: 'handler',
248+
method: 'fooHandler',
249249
version,
250250
thread_name: 'MainThread'
251251
},
@@ -279,7 +279,7 @@ describe('Dynamic Instrumentation', function () {
279279
const topFrame = payload['debugger.snapshot'].stack[0]
280280
// path seems to be prefeixed with `/private` on Mac
281281
assert.match(topFrame.fileName, new RegExp(`${t.appFile}$`))
282-
assert.strictEqual(topFrame.function, 'handler')
282+
assert.strictEqual(topFrame.function, 'fooHandler')
283283
assert.strictEqual(topFrame.lineNumber, t.breakpoint.line)
284284
assert.strictEqual(topFrame.columnNumber, 3)
285285

@@ -375,6 +375,61 @@ describe('Dynamic Instrumentation', function () {
375375

376376
t.agent.addRemoteConfig(rcConfig)
377377
})
378+
379+
it('should adhere to individual probes sample rate', function (done) {
380+
const rcConfig1 = t.breakpoints[0].generateRemoteConfig({ sampling: { snapshotsPerSecond: 1 } })
381+
const rcConfig2 = t.breakpoints[1].generateRemoteConfig({ sampling: { snapshotsPerSecond: 1 } })
382+
const state = {
383+
[rcConfig1.config.id]: {
384+
payloadsReceived: 0,
385+
tiggerBreakpointContinuously () {
386+
t.axios.get(t.breakpoints[0].url).catch(done)
387+
this.timer = setTimeout(this.tiggerBreakpointContinuously.bind(this), 10)
388+
}
389+
},
390+
[rcConfig2.config.id]: {
391+
payloadsReceived: 0,
392+
tiggerBreakpointContinuously () {
393+
t.axios.get(t.breakpoints[1].url).catch(done)
394+
this.timer = setTimeout(this.tiggerBreakpointContinuously.bind(this), 10)
395+
}
396+
}
397+
}
398+
399+
t.agent.on('debugger-diagnostics', ({ payload }) => {
400+
const { probeId, status } = payload.debugger.diagnostics
401+
if (status === 'INSTALLED') state[probeId].tiggerBreakpointContinuously()
402+
})
403+
404+
t.agent.on('debugger-input', ({ payload }) => {
405+
const _state = state[payload['debugger.snapshot'].probe.id]
406+
_state.payloadsReceived++
407+
if (_state.payloadsReceived === 1) {
408+
_state.start = Date.now()
409+
} else if (_state.payloadsReceived === 2) {
410+
const duration = Date.now() - _state.start
411+
clearTimeout(_state.timer)
412+
413+
// Allow for a variance of -5/+50ms (time will tell if this is enough)
414+
assert.isAbove(duration, 995)
415+
assert.isBelow(duration, 1050)
416+
417+
// Wait at least a full sampling period, to see if we get any more payloads
418+
_state.timer = setTimeout(doneWhenCalledTwice, 1250)
419+
} else {
420+
clearTimeout(_state.timer)
421+
done(new Error('Too many payloads received!'))
422+
}
423+
})
424+
425+
t.agent.addRemoteConfig(rcConfig1)
426+
t.agent.addRemoteConfig(rcConfig2)
427+
428+
function doneWhenCalledTwice () {
429+
if (doneWhenCalledTwice.calledOnce) return done()
430+
doneWhenCalledTwice.calledOnce = true
431+
}
432+
})
378433
})
379434

380435
describe('race conditions', function () {

integration-tests/debugger/target-app/basic.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ const Fastify = require('fastify')
55

66
const fastify = Fastify()
77

8-
fastify.get('/:name', function handler (request) {
9-
return { hello: request.params.name } // BREAKPOINT: /foo
8+
fastify.get('/foo/:name', function fooHandler (request) {
9+
return { hello: request.params.name } // BREAKPOINT: /foo/bar
10+
})
11+
12+
fastify.get('/bar/:name', function barHandler (request) {
13+
return { hello: request.params.name } // BREAKPOINT: /bar/baz
1014
})
1115

1216
fastify.listen({ port: process.env.APP_PORT }, (err) => {

integration-tests/debugger/utils.js

+35-13
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,43 @@ module.exports = {
2020

2121
function setup () {
2222
let sandbox, cwd, appPort
23-
const breakpoint = getBreakpointInfo(1) // `1` to disregard the `setup` function
23+
const breakpoints = getBreakpointInfo(1) // `1` to disregard the `setup` function
2424
const t = {
25-
breakpoint,
25+
breakpoint: breakpoints[0],
26+
breakpoints,
27+
2628
axios: null,
2729
appFile: null,
2830
agent: null,
31+
32+
// Default to the first breakpoint in the file (normally there's only one)
2933
rcConfig: null,
30-
triggerBreakpoint,
31-
generateRemoteConfig,
32-
generateProbeConfig
34+
triggerBreakpoint: triggerBreakpoint.bind(null, breakpoints[0].url),
35+
generateRemoteConfig: generateRemoteConfig.bind(null, breakpoints[0]),
36+
generateProbeConfig: generateProbeConfig.bind(null, breakpoints[0])
3337
}
3438

35-
function triggerBreakpoint () {
39+
// Allow specific access to each breakpoint
40+
for (let i = 0; i < breakpoints.length; i++) {
41+
t.breakpoints[i] = {
42+
rcConfig: null,
43+
triggerBreakpoint: triggerBreakpoint.bind(null, breakpoints[i].url),
44+
generateRemoteConfig: generateRemoteConfig.bind(null, breakpoints[i]),
45+
generateProbeConfig: generateProbeConfig.bind(null, breakpoints[i]),
46+
...breakpoints[i]
47+
}
48+
}
49+
50+
function triggerBreakpoint (url) {
3651
// Trigger the breakpoint once probe is successfully installed
3752
t.agent.on('debugger-diagnostics', ({ payload }) => {
3853
if (payload.debugger.diagnostics.status === 'INSTALLED') {
39-
t.axios.get(breakpoint.url)
54+
t.axios.get(url)
4055
}
4156
})
4257
}
4358

44-
function generateRemoteConfig (overrides = {}) {
59+
function generateRemoteConfig (breakpoint, overrides = {}) {
4560
overrides.id = overrides.id || randomUUID()
4661
return {
4762
product: 'LIVE_DEBUGGING',
@@ -54,15 +69,19 @@ function setup () {
5469
sandbox = await createSandbox(['fastify']) // TODO: Make this dynamic
5570
cwd = sandbox.folder
5671
// The sandbox uses the `integration-tests` folder as its root
57-
t.appFile = join(cwd, 'debugger', breakpoint.file)
72+
t.appFile = join(cwd, 'debugger', breakpoints[0].file)
5873
})
5974

6075
after(async function () {
6176
await sandbox.remove()
6277
})
6378

6479
beforeEach(async function () {
65-
t.rcConfig = generateRemoteConfig(breakpoint)
80+
// Default to the first breakpoint in the file (normally there's only one)
81+
t.rcConfig = generateRemoteConfig(breakpoints[0])
82+
// Allow specific access to each breakpoint
83+
t.breakpoints.forEach((breakpoint) => { breakpoint.rcConfig = generateRemoteConfig(breakpoint) })
84+
6685
appPort = await getPort()
6786
t.agent = await new FakeAgent().start()
6887
t.proc = await spawnProc(t.appFile, {
@@ -96,16 +115,19 @@ function getBreakpointInfo (stackIndex = 0) {
96115
.slice(0, -1)
97116
.split(':')[0]
98117

99-
// Then, find the corresponding file in which the breakpoint exists
118+
// Then, find the corresponding file in which the breakpoint(s) exists
100119
const file = join('target-app', basename(testFile).replace('.spec', ''))
101120

102-
// Finally, find the line number of the breakpoint
121+
// Finally, find the line number(s) of the breakpoint(s)
103122
const lines = readFileSync(join(__dirname, file), 'utf8').split('\n')
123+
const result = []
104124
for (let i = 0; i < lines.length; i++) {
105125
const index = lines[i].indexOf(BREAKPOINT_TOKEN)
106126
if (index !== -1) {
107127
const url = lines[i].slice(index + BREAKPOINT_TOKEN.length + 1).trim()
108-
return { file, line: i + 1, url }
128+
result.push({ file, line: i + 1, url })
109129
}
110130
}
131+
132+
return result
111133
}

0 commit comments

Comments
 (0)