Help us shape the future of Go! Take the 2026 Go Developer Survey

memlab

package
v0.57.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 2, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

Memlab WASM E2E Tests

This package adds heap-snapshot analysis on top of Alpha's e2e/wasm browser test harness.

It boots the real WASM app, captures Chrome V8 heap snapshots through the DevTools Protocol, and analyzes retained JavaScript objects with @memlab/heap-analysis.

Scope

The memlab tests are intended for browser-side retention checks around the WASM app runtime and its JS transport layers.

They currently track:

  • ClientRPC
  • ChannelStream
  • Promise
  • Generator
  • onNext closures

For retained ClientRPC objects, the analyzer also groups counts by service/method pair.

What It Measures

These tests measure the page's V8 heap.

They do not directly measure:

  • dedicated worker heaps
  • ServiceWorker heaps
  • Go WASM linear memory
  • CPU usage

Use this package when the question is whether browser-side JS objects are being retained across route changes, idle periods, or watch lifecycles.

Package Layout

  • memlab_test.go
    • scenario definitions
  • snapshot.go
    • heap snapshot capture via CDP HeapProfiler
  • capture.go
    • labeled snapshot set management
  • analyze.js
    • heap analysis and object counting
  • analyze-runner.go
    • ordered invocation of the JS analyzer from Go
  • assert.go
    • threshold-based test assertions
  • testdata/
    • captured .heapsnapshot files for each scenario

Installation

The Go tests install the package-local Node dependencies automatically on first run if node_modules/ is missing.

To install them manually:

cd e2e/wasm/memlab
bun install

Running The Tests

From the repository root:

Run the full memlab suite:

go test -v -timeout=45m ./e2e/wasm/memlab/...

Run a single scenario:

go test -v -timeout=20m ./e2e/wasm/memlab/... -run 'TestDriveScenario$'
go test -v -timeout=20m ./e2e/wasm/memlab/... -run 'TestWatchCleanupScenario$'
go test -v -timeout=20m ./e2e/wasm/memlab/... -run 'TestIdleBaselineScenario$'
go test -v -timeout=20m ./e2e/wasm/memlab/... -run 'TestDriveIdleGrowthScenario$'

Scenarios

TestDriveScenario

Captures snapshots across the quickstart drive flow:

  • baseline on landing
  • snapshot after drive loads
  • cleanup snapshot after navigating away

Use this for mount/unmount retention across the drive route.

TestWatchCleanupScenario

Captures snapshots around the session dashboard route:

  • baseline on landing
  • cleanup snapshot after entering and leaving the session dashboard

Use this for retention caused by dashboard- and session-scoped watchers.

TestIdleBaselineScenario

Captures snapshots on the landing page with no route change:

  • immediate baseline
  • idle snapshot after 30 seconds

Use this for steady-state landing-page accumulation checks.

TestDriveIdleGrowthScenario

Captures a small time series after drive is fully loaded:

  • load #/quickstart/drive
  • wait for WaitForDriveReady
  • capture t1
  • wait 15 seconds
  • capture t2
  • wait 15 seconds
  • capture t3

Use this to distinguish ongoing idle growth from steady-state retained objects after the drive screen has settled.

Output

Snapshots are written under:

e2e/wasm/memlab/testdata/<TestName>/

Typical files include:

  • baseline.heapsnapshot
  • action.heapsnapshot
  • cleanup.heapsnapshot
  • t1.heapsnapshot
  • t2.heapsnapshot
  • t3.heapsnapshot

Test logs include:

  • aggregate retained object counts
  • top retained ClientRPC service/method pairs
  • pair deltas between the first and last snapshot

TestDriveIdleGrowthScenario also logs per-snapshot counts so time-series behavior is visible directly in the test output.

Analyzer Semantics

The analyzer compares the last snapshot in a scenario against the first snapshot in that scenario.

Examples:

  • baseline -> cleanup
  • baseline -> action
  • t1 -> t3

Snapshot order is preserved by the Go runner before invoking analyze.js.

Interpreting Results

A failure means one or more retained object deltas exceeded the thresholds in assert.go.

Typical output includes lines such as:

  • LEAK: ClientRpc delta 19 exceeds threshold 2
  • ok: ChannelStream delta 0 (threshold 2)

The most useful follow-up signals are usually:

  • top retained ClientRPC service/method pairs
  • top pair deltas between first and last snapshot
  • per-snapshot counts in time-series scenarios

Flat counts across multiple snapshots suggest steady-state retention. Rising counts across snapshots suggest ongoing accumulation.

  • e2e/wasm/
    • shared browser/WASM harness used by these tests
  • app/, web/, and SDK/resource layers
    • common sources of retained transport or watch state surfaced by the pair breakdowns

Documentation

Overview

Package memlab provides heap snapshot capture via Chrome DevTools Protocol and analysis via @memlab/heap-analysis for memory leak detection in e2e tests.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AssertDeltas

func AssertDeltas(t testing.TB, result *AnalysisResult, thresholds Thresholds)

AssertDeltas fails the test if any delta exceeds its threshold.

func BuildSnapshotArg

func BuildSnapshotArg(snapshots *SnapshotSet) (string, error)

BuildSnapshotArg builds the ordered label=path argument for analyze.js.

func CaptureHeapSnapshot

func CaptureHeapSnapshot(ctx playwright.BrowserContext, page playwright.Page, outPath string) (string, error)

CaptureHeapSnapshot captures a Chrome V8 heap snapshot via CDP HeapProfiler on the given Playwright page. It forces GC first, then triggers a heap snapshot, collects all chunks, and writes them to outPath. Returns the written file path.

func EnsureDeps

func EnsureDeps() error

EnsureDeps runs bun install in the memlab dir if node_modules is missing. Returns an error if installation fails.

func SortPairDeltas

func SortPairDeltas(pairDeltas []RetainedRpcDelta)

SortPairDeltas sorts pair deltas by descending delta, then final count, then name.

Types

type AnalysisResult

type AnalysisResult struct {
	Snapshots   []SnapshotAnalysis `json:"snapshots"`
	Deltas      ObjectCounts       `json:"deltas"`
	TopRetained []RetainedRpcEntry `json:"topRetained"`
	PairDeltas  []RetainedRpcDelta `json:"pairDeltas"`
}

AnalysisResult is the JSON output from analyze.js.

func RunAnalysis

func RunAnalysis(snapshots *SnapshotSet) (*AnalysisResult, error)

RunAnalysis invokes analyze.js with the given snapshot set and returns the parsed result.

type ObjectCounts

type ObjectCounts struct {
	ClientRpc     int `json:"clientRpc"`
	ChannelStream int `json:"channelStream"`
	Promise       int `json:"promise"`
	Generator     int `json:"generator"`
	OnNext        int `json:"onNext"`
}

ObjectCounts holds counts of leak-target objects.

type RetainedRpcDelta

type RetainedRpcDelta struct {
	Service       string `json:"service"`
	Method        string `json:"method"`
	BaselineCount int    `json:"baselineCount"`
	FinalCount    int    `json:"finalCount"`
	Delta         int    `json:"delta"`
}

RetainedRpcDelta is a per-pair retained count delta between first and last snapshots.

type RetainedRpcEntry

type RetainedRpcEntry struct {
	Service string `json:"service"`
	Method  string `json:"method"`
	Count   int    `json:"count"`
}

RetainedRpcEntry is a service/method pair with count.

type SnapshotAnalysis

type SnapshotAnalysis struct {
	Label       string             `json:"label"`
	Path        string             `json:"path"`
	Counts      ObjectCounts       `json:"counts"`
	TopRetained []RetainedRpcEntry `json:"topRetained"`
}

SnapshotAnalysis is per-snapshot analysis output.

type SnapshotSet

type SnapshotSet struct {
	// Dir is the directory containing the snapshots.
	Dir string
	// Labels tracks snapshot labels in insertion order.
	Labels []string
	// Snapshots maps label to file path.
	Snapshots map[string]string
}

SnapshotSet holds paths to a set of labeled heap snapshots.

func NewSnapshotSet

func NewSnapshotSet(dir string) *SnapshotSet

NewSnapshotSet creates a new SnapshotSet writing to dir.

func (*SnapshotSet) CaptureSnapshot

func (s *SnapshotSet) CaptureSnapshot(ctx playwright.BrowserContext, page playwright.Page, label string) error

CaptureSnapshot captures a single labeled snapshot into the set.

func (*SnapshotSet) Path

func (s *SnapshotSet) Path(label string) string

Path returns the snapshot file path for a label.

func (*SnapshotSet) SnapshotLabels

func (s *SnapshotSet) SnapshotLabels() []string

SnapshotLabels returns the snapshot labels in insertion order. Use the Snapshots map for path lookup.

type Thresholds

type Thresholds struct {
	ClientRpc     int
	ChannelStream int
	Promise       int
	Generator     int
	OnNext        int
}

Thresholds defines per-type delta thresholds for leak assertions.

func DefaultThresholds

func DefaultThresholds() Thresholds

DefaultThresholds returns conservative initial thresholds. Calibrate after first successful run.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL