Skip to content

Commit bdad0cf

Browse files
support v4
1 parent 870560e commit bdad0cf

File tree

6 files changed

+182
-114
lines changed

6 files changed

+182
-114
lines changed

ci/init.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ function detectTestWorkerType () {
2929
if (getEnvironmentVariable('MOCHA_WORKER_ID')) return 'mocha'
3030
if (getEnvironmentVariable('DD_PLAYWRIGHT_WORKER')) return 'playwright'
3131
if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) return 'vitest'
32+
if (getEnvironmentVariable('DD_VITEST_FORKS_POOL_WORKER')) return 'vitest'
3233
return null
3334
}
3435

integration-tests/vitest/vitest.spec.js

Lines changed: 117 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -91,134 +91,139 @@ versions.forEach((version) => {
9191
const poolConfig = ['forks', 'threads']
9292

9393
poolConfig.forEach((poolConfig) => {
94-
it(`can run and report tests with pool=${poolConfig}`, (done) => {
95-
receiver.gatherPayloadsMaxTimeout(({ url }) => url === '/api/v2/citestcycle', payloads => {
96-
const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata)
94+
it(`can run and report tests with pool=${poolConfig}`, async () => {
95+
childProcess = exec(
96+
'./node_modules/.bin/vitest run',
97+
{
98+
cwd,
99+
env: {
100+
...getCiVisAgentlessConfig(receiver.port),
101+
NODE_OPTIONS: '--import dd-trace/register.js -r dd-trace/ci/init', // ESM requires more flags
102+
DD_TEST_SESSION_NAME: 'my-test-session',
103+
POOL_CONFIG: poolConfig,
104+
DD_SERVICE: undefined
105+
},
106+
stdio: 'pipe'
107+
}
108+
)
97109

98-
metadataDicts.forEach(metadata => {
99-
for (const testLevel of TEST_LEVEL_EVENT_TYPES) {
100-
assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session')
101-
}
102-
})
110+
await Promise.all([
111+
once(childProcess, 'exit'),
112+
receiver.gatherPayloadsMaxTimeout(({ url }) => url === '/api/v2/citestcycle', payloads => {
113+
const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata)
103114

104-
const events = payloads.flatMap(({ payload }) => payload.events)
115+
metadataDicts.forEach(metadata => {
116+
for (const testLevel of TEST_LEVEL_EVENT_TYPES) {
117+
assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session')
118+
}
119+
})
105120

106-
const testSessionEvent = events.find(event => event.type === 'test_session_end')
121+
const events = payloads.flatMap(({ payload }) => payload.events)
107122

108-
if (poolConfig === 'threads') {
109-
assert.equal(testSessionEvent.content.meta[VITEST_POOL], 'worker_threads')
110-
} else {
111-
assert.equal(testSessionEvent.content.meta[VITEST_POOL], 'child_process')
112-
}
123+
const testSessionEvent = events.find(event => event.type === 'test_session_end')
113124

114-
const testModuleEvent = events.find(event => event.type === 'test_module_end')
115-
const testSuiteEvents = events.filter(event => event.type === 'test_suite_end')
116-
const testEvents = events.filter(event => event.type === 'test')
125+
if (poolConfig === 'threads') {
126+
assert.equal(testSessionEvent.content.meta[VITEST_POOL], 'worker_threads')
127+
} else {
128+
assert.equal(testSessionEvent.content.meta[VITEST_POOL], 'child_process')
129+
}
117130

118-
assert.include(testSessionEvent.content.resource, 'test_session.vitest run')
119-
assert.equal(testSessionEvent.content.meta[TEST_STATUS], 'fail')
120-
assert.include(testModuleEvent.content.resource, 'test_module.vitest run')
121-
assert.equal(testModuleEvent.content.meta[TEST_STATUS], 'fail')
122-
assert.equal(testSessionEvent.content.meta[TEST_TYPE], 'test')
123-
assert.equal(testModuleEvent.content.meta[TEST_TYPE], 'test')
131+
const testModuleEvent = events.find(event => event.type === 'test_module_end')
132+
const testSuiteEvents = events.filter(event => event.type === 'test_suite_end')
133+
const testEvents = events.filter(event => event.type === 'test')
124134

125-
const passedSuite = testSuiteEvents.find(
126-
suite => suite.content.resource === 'test_suite.ci-visibility/vitest-tests/test-visibility-passed-suite.mjs'
127-
)
128-
assert.equal(passedSuite.content.meta[TEST_STATUS], 'pass')
135+
assert.include(testSessionEvent.content.resource, 'test_session.vitest run')
136+
assert.equal(testSessionEvent.content.meta[TEST_STATUS], 'fail')
137+
assert.include(testModuleEvent.content.resource, 'test_module.vitest run')
138+
assert.equal(testModuleEvent.content.meta[TEST_STATUS], 'fail')
139+
assert.equal(testSessionEvent.content.meta[TEST_TYPE], 'test')
140+
assert.equal(testModuleEvent.content.meta[TEST_TYPE], 'test')
129141

130-
const failedSuite = testSuiteEvents.find(
131-
suite => suite.content.resource === 'test_suite.ci-visibility/vitest-tests/test-visibility-failed-suite.mjs'
132-
)
133-
assert.equal(failedSuite.content.meta[TEST_STATUS], 'fail')
142+
const passedSuite = testSuiteEvents.find(
143+
suite =>
144+
suite.content.resource === 'test_suite.ci-visibility/vitest-tests/test-visibility-passed-suite.mjs'
145+
)
146+
assert.equal(passedSuite.content.meta[TEST_STATUS], 'pass')
134147

135-
const failedSuiteHooks = testSuiteEvents.find(
136-
suite => suite.content.resource === 'test_suite.ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs'
137-
)
138-
assert.equal(failedSuiteHooks.content.meta[TEST_STATUS], 'fail')
139-
140-
assert.includeMembers(testEvents.map(test => test.content.resource),
141-
[
142-
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
143-
'.test-visibility-failed-suite-first-describe can report failed test',
144-
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
145-
'.test-visibility-failed-suite-first-describe can report more',
146-
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
147-
'.test-visibility-failed-suite-second-describe can report passed test',
148-
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
149-
'.test-visibility-failed-suite-second-describe can report more',
150-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.context can report passed test',
151-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.context can report more',
152-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can report passed test',
153-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can report more',
154-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can skip',
155-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can todo',
156-
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report failed test',
157-
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report more',
158-
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report passed test',
159-
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report more',
160-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.no suite',
161-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.skip no suite',
162-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.programmatic skip no suite'
163-
]
164-
)
148+
const failedSuite = testSuiteEvents.find(
149+
suite =>
150+
suite.content.resource === 'test_suite.ci-visibility/vitest-tests/test-visibility-failed-suite.mjs'
151+
)
152+
assert.equal(failedSuite.content.meta[TEST_STATUS], 'fail')
165153

166-
const failedTests = testEvents.filter(test => test.content.meta[TEST_STATUS] === 'fail')
167-
168-
assert.includeMembers(
169-
failedTests.map(test => test.content.resource),
170-
[
171-
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
172-
'.test-visibility-failed-suite-first-describe can report failed test',
173-
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report failed test',
174-
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report more',
175-
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report passed test',
176-
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report more'
177-
]
178-
)
154+
const failedSuiteHooks = testSuiteEvents.find(
155+
suite =>
156+
suite.content.resource === 'test_suite.ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs'
157+
)
158+
assert.equal(failedSuiteHooks.content.meta[TEST_STATUS], 'fail')
179159

180-
const skippedTests = testEvents.filter(test => test.content.meta[TEST_STATUS] === 'skip')
160+
assert.includeMembers(testEvents.map(test => test.content.resource),
161+
[
162+
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
163+
'.test-visibility-failed-suite-first-describe can report failed test',
164+
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
165+
'.test-visibility-failed-suite-first-describe can report more',
166+
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
167+
'.test-visibility-failed-suite-second-describe can report passed test',
168+
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
169+
'.test-visibility-failed-suite-second-describe can report more',
170+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.context can report passed test',
171+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.context can report more',
172+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can report passed test',
173+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can report more',
174+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can skip',
175+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can todo',
176+
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report failed test',
177+
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report more',
178+
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report passed test',
179+
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report more',
180+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.no suite',
181+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.skip no suite',
182+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.programmatic skip no suite'
183+
]
184+
)
181185

182-
assert.includeMembers(
183-
skippedTests.map(test => test.content.resource),
184-
[
185-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can skip',
186-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can todo',
187-
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can programmatic skip'
188-
]
189-
)
186+
const failedTests = testEvents.filter(test => test.content.meta[TEST_STATUS] === 'fail')
190187

191-
testEvents.forEach(test => {
192-
assert.equal(test.content.meta[TEST_COMMAND], 'vitest run')
193-
assert.exists(test.content.metrics[DD_HOST_CPU_COUNT])
194-
assert.equal(test.content.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'false')
195-
})
188+
assert.includeMembers(
189+
failedTests.map(test => test.content.resource),
190+
[
191+
'ci-visibility/vitest-tests/test-visibility-failed-suite.mjs' +
192+
'.test-visibility-failed-suite-first-describe can report failed test',
193+
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report failed test',
194+
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.context can report more',
195+
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report passed test',
196+
'ci-visibility/vitest-tests/test-visibility-failed-hooks.mjs.other context can report more'
197+
]
198+
)
199+
200+
const skippedTests = testEvents.filter(test => test.content.meta[TEST_STATUS] === 'skip')
196201

197-
testSuiteEvents.forEach(testSuite => {
198-
assert.equal(testSuite.content.meta[TEST_COMMAND], 'vitest run')
199-
assert.isTrue(
200-
testSuite.content.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/vitest-tests/test-visibility')
202+
assert.includeMembers(
203+
skippedTests.map(test => test.content.resource),
204+
[
205+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can skip',
206+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can todo',
207+
'ci-visibility/vitest-tests/test-visibility-passed-suite.mjs.other context can programmatic skip'
208+
]
201209
)
202-
assert.equal(testSuite.content.metrics[TEST_SOURCE_START], 1)
203-
assert.exists(testSuite.content.metrics[DD_HOST_CPU_COUNT])
204-
})
205-
// TODO: check error messages
206-
}).then(() => done()).catch(done)
207210

208-
childProcess = exec(
209-
'./node_modules/.bin/vitest run',
210-
{
211-
cwd,
212-
env: {
213-
...getCiVisAgentlessConfig(receiver.port),
214-
NODE_OPTIONS: '--import dd-trace/register.js -r dd-trace/ci/init', // ESM requires more flags
215-
DD_TEST_SESSION_NAME: 'my-test-session',
216-
POOL_CONFIG: poolConfig,
217-
DD_SERVICE: undefined
218-
},
219-
stdio: 'pipe'
220-
}
221-
)
211+
testEvents.forEach(test => {
212+
assert.equal(test.content.meta[TEST_COMMAND], 'vitest run')
213+
assert.exists(test.content.metrics[DD_HOST_CPU_COUNT])
214+
assert.equal(test.content.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'false')
215+
})
216+
217+
testSuiteEvents.forEach(testSuite => {
218+
assert.equal(testSuite.content.meta[TEST_COMMAND], 'vitest run')
219+
assert.isTrue(
220+
testSuite.content.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/vitest-tests/test-visibility')
221+
)
222+
assert.equal(testSuite.content.metrics[TEST_SOURCE_START], 1)
223+
assert.exists(testSuite.content.metrics[DD_HOST_CPU_COUNT])
224+
})
225+
})
226+
])
222227
})
223228
})
224229

packages/datadog-instrumentations/src/vitest.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,14 @@ function isTestPackage (testPackage) {
158158
return testPackage.V?.name === 'VitestTestRunner'
159159
}
160160

161+
function hasForksPoolWorker (vitestPackage) {
162+
return vitestPackage.f?.name === 'ForksPoolWorker'
163+
}
164+
165+
function hasThreadsPoolWorker (vitestPackage) {
166+
return vitestPackage.T?.name === 'ThreadsPoolWorker'
167+
}
168+
161169
function getSessionStatus (state) {
162170
if (state.getCountOfFailedTests() > 0) {
163171
return 'fail'
@@ -481,11 +489,57 @@ addHook({
481489
return TinyPool
482490
})
483491

492+
function getWrappedOn (on) {
493+
return function (event, callback) {
494+
if (event !== 'message') {
495+
return on.apply(this, arguments)
496+
}
497+
// `arguments[1]` is the callback function, which
498+
// we modify to intercept our messages to not interfere
499+
// with vitest's own messages
500+
arguments[1] = shimmer.wrapFunction(callback, callback => function (message) {
501+
if (message.type !== 'Buffer' && Array.isArray(message)) {
502+
const [interprocessCode, data] = message
503+
if (interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE) {
504+
workerReportTraceCh.publish(data)
505+
} else if (interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE) {
506+
workerReportLogsCh.publish(data)
507+
}
508+
// If we execute the callback vitest crashes, as the message is not supported
509+
return
510+
}
511+
return callback.apply(this, arguments)
512+
})
513+
return on.apply(this, arguments)
514+
}
515+
}
516+
484517
function getStartVitestWrapper (cliApiPackage, frameworkVersion) {
485518
if (!isCliApiPackage(cliApiPackage)) {
486519
return cliApiPackage
487520
}
488521
shimmer.wrap(cliApiPackage, 's', getCliOrStartVitestWrapper(frameworkVersion))
522+
523+
if (hasForksPoolWorker(cliApiPackage)) {
524+
// function is async
525+
shimmer.wrap(cliApiPackage.f.prototype, 'start', start => function () {
526+
vitestPool = 'child_process'
527+
this.env.DD_VITEST_FORKS_POOL_WORKER = '1'
528+
529+
return start.apply(this, arguments)
530+
})
531+
shimmer.wrap(cliApiPackage.f.prototype, 'on', getWrappedOn)
532+
}
533+
534+
if (hasThreadsPoolWorker(cliApiPackage)) {
535+
// function is async
536+
shimmer.wrap(cliApiPackage.T.prototype, 'start', start => function () {
537+
vitestPool = 'worker_threads'
538+
this.env.DD_VITEST_THREADS_POOL_WORKER = '1'
539+
return start.apply(this, arguments)
540+
})
541+
shimmer.wrap(cliApiPackage.T.prototype, 'on', getWrappedOn)
542+
}
489543
return cliApiPackage
490544
}
491545

packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ function getInterprocessTraceCode () {
2929
if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) {
3030
return VITEST_WORKER_TRACE_PAYLOAD_CODE
3131
}
32+
if (getEnvironmentVariable('DD_VITEST_FORKS_POOL_WORKER')) {
33+
return VITEST_WORKER_TRACE_PAYLOAD_CODE
34+
}
3235
return null
3336
}
3437

@@ -47,6 +50,9 @@ function getInterprocessLogsCode () {
4750
if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) {
4851
return VITEST_WORKER_LOGS_PAYLOAD_CODE
4952
}
53+
if (getEnvironmentVariable('DD_VITEST_FORKS_POOL_WORKER')) {
54+
return VITEST_WORKER_LOGS_PAYLOAD_CODE
55+
}
5056
return null
5157
}
5258

packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ class Writer {
3535
// See cucumber code:
3636
// https://github.com/cucumber/cucumber-js/blob/5ce371870b677fe3d1a14915dc535688946f734c/src/runtime/parallel/run_worker.ts#L13
3737
if (process.send) { // it only works if process.send is available
38-
const isVitestWorker = !!getEnvironmentVariable('TINYPOOL_WORKER_ID')
38+
// Old because vitest@>=4 use DD_VITEST_FORKS_POOL_WORKER and report arrays just like other frameworks
39+
const isVitestWorkerOld = !!getEnvironmentVariable('TINYPOOL_WORKER_ID')
3940

40-
const payload = isVitestWorker
41+
const payload = isVitestWorkerOld
4142
? { __tinypool_worker_message__: true, interprocessCode: this._interprocessCode, data }
4243
: [this._interprocessCode, data]
4344

packages/dd-trace/src/supported-configurations.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@
444444
"DD_VERSION": ["A"],
445445
"DD_VERTEXAI_SPAN_CHAR_LIMIT": ["A"],
446446
"DD_VERTEXAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE": ["A"],
447+
"DD_VITEST_FORKS_POOL_WORKER": ["A"],
447448
"OTEL_LOG_LEVEL": ["A"],
448449
"OTEL_LOGS_EXPORTER": ["A"],
449450
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": ["A"],

0 commit comments

Comments
 (0)