Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: upload snapshot as artifacts when test failed, record hean snapshot in mem test #4594

Merged
merged 8 commits into from
Feb 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
run: pnpm build:e2e

- name: Run Playwright Tests
run: pnpm exec playwright test --output=playwright-assets
run: pnpm exec playwright test

- name: 🚀 Deploy to Vercel
uses: amondnet/vercel-action@v25
Expand All @@ -49,7 +49,13 @@ jobs:
if: ${{ !cancelled() }}
with:
name: playwright-report
path: |
playwright-report/
test-results/
path: playwright-report/
retention-days: 30

- name: Upload HeapSnapshots
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: snapshots
path: test-results/*.heapsnapshot
retention-days: 3
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ typings/
# examples

screenshot
*.heapsnapshot

univer-custom-build
universheet-custom-build
Expand Down
100 changes: 99 additions & 1 deletion e2e/memory/memory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* limitations under the License.
*/

import type { CDPSession } from '@playwright/test';
import { createWriteStream } from 'node:fs';
import { expect, test } from '@playwright/test';
import { getMetrics } from './util';

Expand All @@ -27,11 +29,101 @@ const MAX_UNIVER_MEMORY_OVERFLOW = 6_000_000; // TODO@wzhudev: temporarily added

const MAX_SECOND_INSTANCE_OVERFLOW = 100_000; // Only 100 KB

interface HeapSnapshotChunk {
chunk: string;
}

interface HeapSnapshotProgress {
done: number;
total: number;
finished?: boolean;
}
async function takeHeapSnapshot(client: CDPSession, filename: string) {
return new Promise((resolve, reject) => {
const file = createWriteStream(`./test-results/${filename}`);
let isFinished = false;
let error = null;
let noChunkTimeout = null;
let chunkHandler = (_: HeapSnapshotChunk) => {};
let progressHandler = (_: HeapSnapshotProgress) => {};

// Cleanup function to remove listeners
const cleanup = () => {
client.off('HeapProfiler.addHeapSnapshotChunk', chunkHandler);
client.off('HeapProfiler.reportHeapSnapshotProgress', progressHandler);
};

// Handle file stream errors
file.on('error', (err) => {
error = err;
reject(err);
});

// Handle successful completion
file.on('finish', () => {
if (!error && isFinished) {
resolve(0);
}
});

const scheduleEnd = () => {
if (noChunkTimeout) {
clearTimeout(noChunkTimeout);
}

// Set new timeout
noChunkTimeout = setTimeout(() => {
cleanup();
file.end();
}, 1000); // Wait 1 second after last chunk
};

// Set up the chunk handler
chunkHandler = (payload: HeapSnapshotChunk) => {
try {
if (payload.chunk) {
file.write(payload.chunk);
scheduleEnd();
}
} catch (err) {
error = err;
console.error('chunkHandler error', err);
cleanup();
reject(err);
}
};

// Set up the progress handler
progressHandler = (params: HeapSnapshotProgress) => {
if (params.finished) {
isFinished = true;
}
};

// Add event listeners
client.on('HeapProfiler.addHeapSnapshotChunk', chunkHandler);
client.on('HeapProfiler.reportHeapSnapshotProgress', progressHandler);

// Start the heap snapshot process
client.send('HeapProfiler.enable')
.then(() => client.send('HeapProfiler.takeHeapSnapshot', { reportProgress: true }))
.catch((err) => {
console.error('HeapProfiler.enable error', err);
error = err;
file.end();
cleanup();
reject(err);
});
});
}

// const isLocal = !process.env.CI;
test('memory', async ({ page }) => {
test.setTimeout(60_000);
const client = await page.context().newCDPSession(page);

await page.goto('http://localhost:3000/sheets/');
await page.waitForTimeout(5000);
await page.waitForTimeout(2000);

const memoryAfterFirstInstance = (await getMetrics(page)).JSHeapUsedSize;

Expand All @@ -47,12 +139,18 @@ test('memory', async ({ page }) => {

await page.evaluate(() => window.univer.dispose());
await page.waitForTimeout(2000);

await takeHeapSnapshot(client, 'memory-first.heapsnapshot');

const memoryAfterDisposingFirstInstance = (await getMetrics(page)).JSHeapUsedSize;

await page.evaluate(() => window.createNewInstance());
await page.waitForTimeout(2000);
await page.evaluate(() => window.univer.dispose());
await page.waitForTimeout(2000);

await takeHeapSnapshot(client, 'memory-second.heapsnapshot');

const memoryAfterDisposingSecondUniver = (await getMetrics(page)).JSHeapUsedSize;
expect(memoryAfterDisposingSecondUniver - memoryAfterDisposingFirstInstance)
.toBeLessThanOrEqual(MAX_SECOND_INSTANCE_OVERFLOW);
Expand Down
10 changes: 1 addition & 9 deletions e2e/perf/scroll.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License.
*/

import type { Page } from '@playwright/test';
/* eslint-disable no-console */
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { sheetData as emptySheetData } from '../__testing__/emptysheet';
import { sheetData as freezeData } from '../__testing__/freezesheet';
Expand Down Expand Up @@ -135,14 +135,6 @@ async function measureFPS(page: Page, testDuration = 5, deltaX: number, deltaY:
const createTest = (title: string, sheetData: IJsonObject, minFpsValue: number, deltaX = 0, deltaY = 0) => {
// Default Size Of browser: 1280x720 pixels. And default DPR is 1.
test(title, async ({ page }) => {
// dev:e2e open localhost:3000, not 3002
// let port = 3000;
// if (!isCI) {
// const browser = await chromium.launch({ headless: false }); // launch browser
// page = await browser.newPage();
// port = 3002;
// }

await page.goto('http://localhost:3000/sheets/');
await page.waitForTimeout(2000);

Expand Down
1 change: 1 addition & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const HEADLESS = !!process.env.HEADLESS;
*/
export default defineConfig({
testDir: './e2e',
outputDir: 'test-results/', // Make sure this is set
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
Expand Down