fix(trace): EPERM on windows (#27693)

When merging trace files, we sometimes left open read streams from the
zip, which prevents it from being removed.

Fixes #27286.
This commit is contained in:
Dmitry Gozman 2023-10-19 11:14:17 -07:00 committed by GitHub
parent b1325c9208
commit 6e62a11643
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 6 deletions

View file

@ -139,7 +139,7 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
zipFile.outputStream.pipe(fs.createWriteStream(params.zipFile)).on('close', () => { zipFile.outputStream.pipe(fs.createWriteStream(params.zipFile)).on('close', () => {
fs.promises.unlink(tempFile).then(() => { fs.promises.unlink(tempFile).then(() => {
promise.resolve(); promise.resolve();
}); }).catch(error => promise.reject(error));
}); });
}); });
} }

View file

@ -204,15 +204,18 @@ export async function mergeTraceFiles(fileName: string, temporaryTraceFiles: str
} else if (entry.fileName.match(/[\d-]*trace\./)) { } else if (entry.fileName.match(/[\d-]*trace\./)) {
entryName = i + '-' + entry.fileName; entryName = i + '-' + entry.fileName;
} }
if (entryNames.has(entryName)) {
if (--pendingEntries === 0)
promise.resolve();
return;
}
entryNames.add(entryName);
inZipFile.openReadStream(entry, (err, readStream) => { inZipFile.openReadStream(entry, (err, readStream) => {
if (err) { if (err) {
promise.reject(err); promise.reject(err);
return; return;
} }
if (!entryNames.has(entryName)) { zipFile.addReadStream(readStream!, entryName);
entryNames.add(entryName);
zipFile.addReadStream(readStream!, entryName);
}
if (--pendingEntries === 0) if (--pendingEntries === 0)
promise.resolve(); promise.resolve();
}); });
@ -225,7 +228,7 @@ export async function mergeTraceFiles(fileName: string, temporaryTraceFiles: str
zipFile.outputStream.pipe(fs.createWriteStream(fileName)).on('close', () => { zipFile.outputStream.pipe(fs.createWriteStream(fileName)).on('close', () => {
void Promise.all(temporaryTraceFiles.map(tempFile => fs.promises.unlink(tempFile))).then(() => { void Promise.all(temporaryTraceFiles.map(tempFile => fs.promises.unlink(tempFile))).then(() => {
mergePromise.resolve(); mergePromise.resolve();
}); }).catch(error => mergePromise.reject(error));
}).on('error', error => mergePromise.reject(error)); }).on('error', error => mergePromise.reject(error));
}); });
await mergePromise; await mergePromise;

View file

@ -753,3 +753,49 @@ test('should use custom expect message in trace', async ({ runInlineTest }, test
' fixture: context', ' fixture: context',
]); ]);
}); });
test('should not throw when merging traces multiple times', async ({ runInlineTest }, testInfo) => {
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/27286' });
const result = await runInlineTest({
'playwright.config.ts': `
module.exports = { use: { trace: 'on' } };
`,
'a.spec.ts': `
import { BrowserContext, expect, Page, test as baseTest } from '@playwright/test';
let pg: Page;
let ctx: BrowserContext;
const test = baseTest.extend({
page: async ({}, use) => {
await use(pg);
},
context: async ({}, use) => {
await use(ctx);
},
});
test.beforeAll(async ({ browser }) => {
ctx = await browser.newContext();
pg = await ctx.newPage();
});
test.beforeAll(async ({ page }) => {
await page.goto('https://playwright.dev');
});
test.afterAll(async ({ context }) => {
await context.close();
});
test('foo', async ({ page }) => {
await expect(page.locator('h1')).toContainText('Playwright');
});
`,
}, { workers: 1 });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
expect(fs.existsSync(testInfo.outputPath('test-results', 'a-foo', 'trace.zip'))).toBe(true);
});