Skip to content

Commit 5adf85f

Browse files
[test optimization] Add faulty logic to EFD in playwright (#6737)
1 parent 2482bde commit 5adf85f

File tree

2 files changed

+93
-33
lines changed

2 files changed

+93
-33
lines changed

integration-tests/playwright/playwright.spec.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,51 @@ versions.forEach((version) => {
736736
receiverPromise,
737737
])
738738
})
739+
740+
it('does not run EFD if the percentage of new tests is too high', async () => {
741+
receiver.setSettings({
742+
early_flake_detection: {
743+
enabled: true,
744+
slow_test_retries: {
745+
'5s': NUM_RETRIES_EFD
746+
},
747+
faulty_session_threshold: 0
748+
},
749+
known_tests_enabled: true
750+
})
751+
752+
receiver.setKnownTests({ playwright: {} })
753+
754+
childProcess = exec(
755+
'./node_modules/.bin/playwright test -c playwright.config.js',
756+
{
757+
cwd,
758+
env: {
759+
...getCiVisAgentlessConfig(receiver.port),
760+
PW_BASE_URL: `http://localhost:${webAppPort}`
761+
},
762+
stdio: 'pipe'
763+
}
764+
)
765+
766+
await Promise.all([
767+
once(childProcess, 'exit'),
768+
receiver
769+
.gatherPayloadsMaxTimeout(({ url }) => url === '/api/v2/citestcycle', (payloads) => {
770+
const events = payloads.flatMap(({ payload }) => payload.events)
771+
const tests = events.filter(event => event.type === 'test').map(event => event.content)
772+
773+
const testSession = events.find(event => event.type === 'test_session_end').content
774+
assert.notProperty(testSession.meta, TEST_EARLY_FLAKE_ENABLED)
775+
assert.propertyVal(testSession.meta, TEST_EARLY_FLAKE_ABORT_REASON, 'faulty')
776+
777+
const newTests = tests.filter(test => test.meta[TEST_IS_NEW] === 'true')
778+
assert.equal(newTests.length, 0)
779+
const retriedTests = tests.filter(test => test.meta[TEST_IS_RETRY] === 'true')
780+
assert.equal(retriedTests.length, 0)
781+
})
782+
])
783+
})
739784
})
740785

741786
it('does not crash when maxFailures=1 and there is an error', (done) => {

packages/datadog-instrumentations/src/playwright.js

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ const shimmer = require('../../datadog-shimmer')
77
const {
88
parseAnnotations,
99
getTestSuitePath,
10-
PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE
10+
PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE,
11+
getIsFaultyEarlyFlakeDetection
1112
} = require('../../dd-trace/src/plugins/util/test')
1213
const log = require('../../dd-trace/src/log')
1314
const { DD_MAJOR } = require('../../../version')
@@ -52,6 +53,7 @@ let isKnownTestsEnabled = false
5253
let isEarlyFlakeDetectionEnabled = false
5354
let earlyFlakeDetectionNumRetries = 0
5455
let isEarlyFlakeDetectionFaulty = false
56+
let earlyFlakeDetectionFaultyThreshold = 0
5557
let isFlakyTestRetriesEnabled = false
5658
let flakyTestRetriesCount = 0
5759
let knownTests = {}
@@ -542,6 +544,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
542544
isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
543545
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
544546
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
547+
earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
545548
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
546549
flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
547550
isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
@@ -846,40 +849,52 @@ addHook({
846849
if (isKnownTestsEnabled) {
847850
const newTests = allTests.filter(isNewTest)
848851

849-
/**
850-
* We could repeat the logic of `applyRepeatEachIndex` here, but it'd be more risky
851-
* as playwright could change it at any time.
852-
*
853-
* `applyRepeatEachIndex` goes through all the tests in a suite and applies the "repeat" logic
854-
* for a single repeat index.
855-
*
856-
* This means that the clone logic is cumbersome:
857-
* - we grab the unique file suites that have new tests
858-
* - we store its project suite
859-
* - we clone each of these file suites for each repeat index
860-
* - we execute `applyRepeatEachIndex` for each of these cloned file suites
861-
* - we add the cloned file suites to the project suite
862-
*/
863-
864-
const fileSuitesWithNewTestsToProjects = new Map()
865-
newTests.forEach(newTest => {
866-
newTest._ddIsNew = true
867-
if (isEarlyFlakeDetectionEnabled && newTest.expectedStatus !== 'skipped' && !newTest._ddIsModified) {
868-
const fileSuite = getSuiteType(newTest, 'file')
869-
if (!fileSuitesWithNewTestsToProjects.has(fileSuite)) {
870-
fileSuitesWithNewTestsToProjects.set(fileSuite, getSuiteType(newTest, 'project'))
852+
const isFaulty = getIsFaultyEarlyFlakeDetection(
853+
allTests.map(test => getTestSuitePath(test._requireFile, rootDir)),
854+
knownTests.playwright,
855+
earlyFlakeDetectionFaultyThreshold
856+
)
857+
858+
if (isFaulty) {
859+
isEarlyFlakeDetectionEnabled = false
860+
isKnownTestsEnabled = false
861+
isEarlyFlakeDetectionFaulty = true
862+
} else {
863+
/**
864+
* We could repeat the logic of `applyRepeatEachIndex` here, but it'd be more risky
865+
* as playwright could change it at any time.
866+
*
867+
* `applyRepeatEachIndex` goes through all the tests in a suite and applies the "repeat" logic
868+
* for a single repeat index.
869+
*
870+
* This means that the clone logic is cumbersome:
871+
* - we grab the unique file suites that have new tests
872+
* - we store its project suite
873+
* - we clone each of these file suites for each repeat index
874+
* - we execute `applyRepeatEachIndex` for each of these cloned file suites
875+
* - we add the cloned file suites to the project suite
876+
*/
877+
878+
const fileSuitesWithNewTestsToProjects = new Map()
879+
newTests.forEach(newTest => {
880+
newTest._ddIsNew = true
881+
if (isEarlyFlakeDetectionEnabled && newTest.expectedStatus !== 'skipped' && !newTest._ddIsModified) {
882+
const fileSuite = getSuiteType(newTest, 'file')
883+
if (!fileSuitesWithNewTestsToProjects.has(fileSuite)) {
884+
fileSuitesWithNewTestsToProjects.set(fileSuite, getSuiteType(newTest, 'project'))
885+
}
871886
}
872-
}
873-
})
887+
})
874888

875-
for (const [fileSuite, projectSuite] of fileSuitesWithNewTestsToProjects.entries()) {
876-
for (let repeatEachIndex = 1; repeatEachIndex <= earlyFlakeDetectionNumRetries; repeatEachIndex++) {
877-
const copyFileSuite = deepCloneSuite(fileSuite, isNewTest, [
878-
'_ddIsNew',
879-
'_ddIsEfdRetry'
880-
])
881-
applyRepeatEachIndex(projectSuite._fullProject, copyFileSuite, repeatEachIndex + 1)
882-
projectSuite._addSuite(copyFileSuite)
889+
for (const [fileSuite, projectSuite] of fileSuitesWithNewTestsToProjects.entries()) {
890+
for (let repeatEachIndex = 1; repeatEachIndex <= earlyFlakeDetectionNumRetries; repeatEachIndex++) {
891+
const copyFileSuite = deepCloneSuite(fileSuite, isNewTest, [
892+
'_ddIsNew',
893+
'_ddIsEfdRetry'
894+
])
895+
applyRepeatEachIndex(projectSuite._fullProject, copyFileSuite, repeatEachIndex + 1)
896+
projectSuite._addSuite(copyFileSuite)
897+
}
883898
}
884899
}
885900
}

0 commit comments

Comments
 (0)