Skip to content

Commit

Permalink
Merge pull request #31 from krassowski/custom-scenarios
Browse files Browse the repository at this point in the history
Allow to add scenarios programmatically
  • Loading branch information
krassowski authored Feb 11, 2023
2 parents fb106a7 + ff7e278 commit eac70e7
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 54 deletions.
51 changes: 51 additions & 0 deletions docs/source/user_guide.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,57 @@
"\n",
"If you use a replacement server (e.g. jupyverse) you will need to add the required header manually or switch to the default jupyter-server for the time of profiling."
]
},
{
"cell_type": "markdown",
"id": "73052b6b-24d6-4b91-812e-254a61f2ba90",
"metadata": {},
"source": [
"## Custom scenarios"
]
},
{
"cell_type": "markdown",
"id": "de361f64-5913-453f-8603-8624c92bf571",
"metadata": {},
"source": [
"### Programmatic scenarios\n",
"\n",
"You can define custom scenarios programmatically by creating a JupyterLab extension which consumes `IUIProfiler` token:\n",
"\n",
"```typescript\n",
"import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application';\n",
"import { IScenario, IUIProfiler } from '@jupyterlab/ui-profiler';\n",
"\n",
"\n",
"class MyScenario implements IScenario {\n",
" id = 'myScenario';\n",
" name = 'My scenario';\n",
"\n",
" async run(): Promise<void> {\n",
" console.log('Running!');\n",
" }\n",
"}\n",
"\n",
"export const plugin: JupyterFrontEndPlugin<void> = {\n",
" id: '@my-organization/my-ui-profiler-extension:my-scenario',\n",
" autoStart: true,\n",
" requires: [IUIProfiler],\n",
" activate: (app: JupyterFrontEnd, profiler: IUIProfiler) => {\n",
" const myScenario = new MyScenario();\n",
" profiler.addScenario(myScenario);\n",
" }\n",
"}\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "f41ca671-fa3b-4aec-9985-7d2b1116d93c",
"metadata": {},
"source": [
"Please see [`tokens.ts` file](https://github.com/jupyterlab/ui-profiler/blob/main/src/tokens.ts) for the full documentation of `IScenario` interface."
]
}
],
"metadata": {
Expand Down
16 changes: 1 addition & 15 deletions src/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,7 @@ import { layoutReady } from './dramaturg';
import benchmarkExecutionOptionsSchema from './schema/benchmark-execution.json';
import type { ExecutionTimeBenchmarkOptions } from './types/_benchmark-execution';
import { renderTimings } from './ui';

export interface IScenario {
id: string;
name: string;
run: () => Promise<void>;

setupSuite?: () => Promise<void>;
cleanupSuite?: () => Promise<void>;

setup?: () => Promise<void>;
cleanup?: () => Promise<void>;

configSchema: JSONSchema7;
setOptions: (options: any) => void;
}
import { IScenario } from './tokens';

export interface IProgress {
percentage: number;
Expand Down
40 changes: 18 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,15 @@ import {
styleRuleUsageBenchmark
} from './styleBenchmarks';
import { selfProfileBenchmark } from './jsBenchmarks';
import {
MenuOpenScenario,
MenuSwitchScenario,
SwitchTabScenario,
SwitchTabFocusScenario,
SidebarOpenScenario,
CompleterScenario,
ScrollScenario,
DebuggerScenario
} from './scenarios';
import {
executionTimeBenchmark,
IBenchmark,
ITimingOutcome,
IProfilingOutcome
} from './benchmark';
import { IJupyterState } from './utils';
import { IScenario, IUIProfiler } from './tokens';
import { plugin as scenariosPlugin } from './scenarios';

namespace CommandIDs {
// export const findUnusedStyles = 'ui-profiler:find-unused-styles';
Expand All @@ -45,11 +37,12 @@ namespace CommandIDs {
/**
* Initialization data for the @jupyterlab/ui-profiler extension.
*/
const plugin: JupyterFrontEndPlugin<void> = {
const plugin: JupyterFrontEndPlugin<IUIProfiler> = {
id: '@jupyterlab/ui-profiler:plugin',
autoStart: true,
requires: [IDocumentManager],
optional: [ILauncher, ILayoutRestorer],
provides: IUIProfiler,
activate: (
app: JupyterFrontEnd,
docManager: IDocumentManager,
Expand All @@ -59,6 +52,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
const fileBrowserModel = new FileBrowserModel({
manager: docManager
});
const scenarios: IScenario[] = [];
const options = {
benchmarks: [
executionTimeBenchmark,
Expand All @@ -68,16 +62,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
styleRuleUsageBenchmark,
selfProfileBenchmark
] as (IBenchmark<ITimingOutcome<any>> | IBenchmark<IProfilingOutcome>)[],
scenarios: [
new MenuOpenScenario(app),
new MenuSwitchScenario(app),
new SwitchTabScenario(app),
new SwitchTabFocusScenario(app),
new SidebarOpenScenario(app),
new CompleterScenario(app),
new ScrollScenario(app),
new DebuggerScenario(app)
],
scenarios: scenarios,
translator: nullTranslator,
upload: (file: File) => {
// https://github.com/jupyterlab/jupyterlab/issues/11416
Expand Down Expand Up @@ -156,7 +141,18 @@ const plugin: JupyterFrontEndPlugin<void> = {
rank: 1
});
}

return {
addScenario: (scenario: IScenario) => {
scenarios.push(scenario);
if (lastWidget) {
lastWidget.content.addScenario(scenario);
}
}
};
}
};

export default plugin;
export * from './tokens';

export default [plugin, scenariosPlugin];
2 changes: 1 addition & 1 deletion src/jsBenchmarks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { JSONSchema7 } from 'json-schema';

import {
IScenario,
IProfilingOutcome,
IBenchmark,
IProfileMeasurement,
Expand All @@ -10,6 +9,7 @@ import {
import { reportTagCounts } from './utils';
import { layoutReady } from './dramaturg';
import { renderProfile } from './ui';
import { IScenario } from './tokens';

import benchmarkProfileOptionsSchema from './schema/benchmark-profile.json';
import type { ProfileBenchmarkOptions } from './types/_benchmark-profile';
Expand Down
31 changes: 23 additions & 8 deletions src/scenarios.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import type { JupyterFrontEnd } from '@jupyterlab/application';
import type {
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import type { MainAreaWidget } from '@jupyterlab/apputils';

import { JSONSchema7 } from 'json-schema';

import {
page,
layoutReady,
ElementHandle,
waitForScrollEnd,
waitUntilDisappears
} from './dramaturg';
import { IScenario } from './benchmark';
import { IScenario, IUIProfiler } from './tokens';

import type { TabScenarioOptions, Tab } from './types/_scenario-tabs';
import type { ScenarioOptions } from './types/_scenario-base';
import type { MenuOpenScenarioOptions } from './types/_scenario-menu-open';
import type { CompleterScenarioOptions } from './types/_scenario-completer';
import type { SidebarsScenarioOptions } from './types/_scenario-sidebars';
Expand Down Expand Up @@ -53,10 +54,6 @@ export class MenuSwitchScenario implements IScenario {
// no-op
}

setOptions(options: ScenarioOptions): void {
// no-op
}

async setup(): Promise<void> {
return openMainMenu(this.jupyterApp);
}
Expand Down Expand Up @@ -597,3 +594,21 @@ export class SwitchTabFocusScenario extends SwitchTabScenario {
name = 'Switch Tab Focus';
split: 'first' | 'all' = 'all';
}

export const plugin: JupyterFrontEndPlugin<void> = {
id: '@jupyterlab/ui-profiler:default-scenarios',
autoStart: true,
requires: [IUIProfiler],
activate: (app: JupyterFrontEnd, profiler: IUIProfiler) => {
[
new MenuOpenScenario(app),
new MenuSwitchScenario(app),
new SwitchTabScenario(app),
new SwitchTabFocusScenario(app),
new SidebarOpenScenario(app),
new CompleterScenario(app),
new ScrollScenario(app),
new DebuggerScenario(app)
].map(scenario => profiler.addScenario(scenario));
}
};
2 changes: 1 addition & 1 deletion src/styleBenchmarks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { JSONSchema7 } from 'json-schema';
import React from 'react';

import {
IScenario,
ITimeMeasurement,
ITimingOutcome,
IBenchmark,
Expand All @@ -17,6 +16,7 @@ import {
collectRules
} from './css';
import { renderBlockResult } from './ui';
import { IScenario } from './tokens';

import benchmarkOptionsSchema from './schema/benchmark-base.json';
import benchmarkRuleOptionsSchema from './schema/benchmark-rule.json';
Expand Down
73 changes: 73 additions & 0 deletions src/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Token } from '@lumino/coreutils';
import { JSONSchema7 } from 'json-schema';

/**
* Scenario defining set of steps to carry out during benchmarking.
*/
export interface IScenario {
/**
* The internal identifier, has to be unique.
*/
id: string;

/**
* The name displayed to user.
*/
name: string;

/**
* The actual scenario execution.
*
* Note: any async calls made in the `run()` function should be awaited so
* that the execution time measurements are accurate and to prevent calling
* `cleanup()` too early.
*/
run: () => Promise<void>;

/**
* Prepare scenario before running, called once for any given benchmark run.
*/
setupSuite?: () => Promise<void>;

/**
* Clean up after scenario running, called once for any given benchmark run.
*/
cleanupSuite?: () => Promise<void>;

/**
* Prepare for scenario repeat, called as many times as benchmark repeats.
*/
setup?: () => Promise<void>;

/**
* Clean up after scenario, called as many times as benchmark repeats.
*/
cleanup?: () => Promise<void>;

/**
* Configuration schema used to build the configuration form with rjsf.
*/
configSchema?: JSONSchema7;

/**
* Callback receiving JSON object with user configuration choices.
*/
setOptions?: (options: any) => void;
}

/**
* The UIProfiler public API.
*/
export interface IUIProfiler {
/**
* Add scenario to profiler.
*/
addScenario(scenario: IScenario): void;
}

/**
* The UIProfiler token.
*/
export const IUIProfiler = new Token<IUIProfiler>(
'@jupyterlab/ui-profiler:manager'
);
Loading

0 comments on commit eac70e7

Please sign in to comment.