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

View file

@ -753,3 +753,49 @@ test('should use custom expect message in trace', async ({ runInlineTest }, test
' 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);
});