Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ apps/test-bundles @microsoft/fluentui-react
apps/public-docsite-v9 @microsoft/cxe-red @microsoft/cxe-coastal @microsoft/fluentui-react-build
apps/theming-designer @microsoft/fluentui-react
apps/ssr-tests-v9 @microsoft/fluentui-react-build
apps/stress-test @microsoft/cxe-red @spmonahan @micahgodbolt

#### Packages
packages/azure-themes @hyoshis @Jacqueline-ms
Expand Down
1 change: 1 addition & 0 deletions apps/stress-test/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules/
dev-build/
dist/

benchmarks/**/*.json
Expand Down
56 changes: 48 additions & 8 deletions apps/stress-test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,26 @@ $ yarn workspace @fluentui/stress-test stress-test build --build-deps
$ yarn workspace @fluentui/stress-test stress-test build
```

> NOTE: `build:local` is much slower, but is required the first time you're building the application or if you've pulled in lots of changes. Use `build:app` if you don't need to build dependencies like `@fluentui/react` as it's much faster.
> NOTE: `build --build-deps` is much slower, but is required the first time you're building the application or if you've pulled in lots of changes. Use `build` if you don't need to build dependencies like `@fluentui/react` as it's much faster.

### Examples

```shell
# Run the "simple-stress" scenario with the "mount" and "prop-update" test cases against Firefox with small sizes and low sample size
$ yarn workspace @fluentui/stress-test stress-test run --scenario simple-stress --sample-size 2 --test-cases mount prop-update --browsers firefox --sizes xs s
# Run your "my-scenario" with the "mount" and "inject-styles" test cases against Firefox with small sizes and low sample size. Target the "stress-tree" page with "button" renderers.
$ yarn workspace @fluentui/stress-test stress-test run --scenario my-scenario --test-cases mount inject-styles --browsers chrome firefox --sizes xs s --targets v8/stress-test?r=button v9/stress-test?r=button wc/stress-test?r=button

# Run the "simple-stress" scenario with the "mount" and "prop-update" test cases against the default browsers at the default sizes and sample size
$ yarn workspace @fluentui/stress-test stress-test run --scenario simple-stress --test-cases mount prop-update
# Run your "my-scenario" scenario with the "mount" and "inject-styles" test cases against the default browsers at the default sizes and sample size. Target the "stress-tree" page with "button" renderers.
$ yarn workspace @fluentui/stress-test stress-test run --scenario my-scenario --test-cases mount inject-styles --targets v8/stress-test?r=button v9/stress-test?r=button wc/stress-test?r=button
```

> NOTE: Tests should be run against production builds. While tests can be run against development builds, and this is useful for gathering quick results and debugging, the performance characteristics of development and production builds can differ quite a bit.

## Glossary

- **scenario**: A testing scenario for a specific line of investigation. For example, if you wanted to compare the performance of different `Button` implementations you might create a "button-test" scenario for various targets.
- **targets**: Different implementation targets. For example: "v8" for Fluent UI v8, "v9" for Fluent UI v9, "wc" for Fluent UI Web Components.
- **targets**: Different implementation targets. These are URLs that can accept query parameters. For example "v8/stress-tree?r=button", "wc/stress-tree?r=button".
- **test cases**: Different test cases to run against a given scenario. For example, you might want to test mounting performance for a scenario.
- **renderers**: Simple components that can be plugged into targets allowing easy customization of the component under test.

## Development

Expand All @@ -65,6 +66,45 @@ The `benchmarks` folder houses Tachometer configurations and test results; and h

The `scripts` folder house the Stress Test CLI app that is used to run tests.

### Adding test cases
### Creating your own tests

Add tests cases to the appropriate `src/pages` sub-folder. For example to create a new test, "my test" for Fluent v9 add it to `src/pages/v9/my-test`. Use an existing page as a guide for the files you need to add. Pages are automatically picked up by Webpack when the dev server is started.
There are a few way you can create your own tests depending on what you want to do.

#### Adding a new test with a renderer

The simplest thing to do is use an existing test page like "stress-tree" but with your own renderer. A renderer is just a function that renders a component to be rendered. Renderers are specific to the UI library being tested so a Web Component renderer is different from a React one but conceptually they are the same.

An example React renderer:

```tsx
// myButton.tsx
const componentRenderer: ReactSelectorTreeComponentRenderer = (node, depth, index) => {
return <Button>Button at index {index}</Button>;
};
```

> Note: renderers need to be saved in the appropriate `src/renderers` subfolder. `v8` for Fluent V8, `v9` for Fluent V9, `wc` for Fluent Web Components

In your test specify the renderer with the `r` query parameter:

```shell
yarn workspace @fluentui/stress-test stress-test run my-button-scenarion --test-cases mount --targets v9/stress-tree?r=myButton
```

#### Adding new test fixtures

Test fixtures are used to drive tests. In the "stress-tree` page fixtures are used to control different sizes of DOM tree so tests can be run against "simple" to "complex" DOM trees.

Fixtures can be generated with the `build-fixture` command:

```shell
yarn workspace @fluentui/stress-test stress-test build-fixture --name my-fixture --option option1=value1 option2=value2
```

Fixtures are saved in the `src/fixtures` folder.

> Note: fixtures can become quite large so they are excluded from git. If you need others to be able to generate your fixture be sure to keep track of the options you use to create the fixture.

#### Adding a new test page

A page is a way to control the entire structure of your test. Currently, we ship a "stress-tree" page which loads a broad and deep DOM tree to simulate a complex real-world web application. If you need something different create a page to represent the situation you need to test. See `src/pages` for examples of how to create a page.
1 change: 0 additions & 1 deletion apps/stress-test/scripts/commands/buildTestConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ const makeConfigJson: MakeConfigJson = (_scenario, browser, testCase, sampleSize
],

expand: targets.map(target => {
// const params = querystring.stringify({ test: testCase, ...testOptions });
const targetParams = target.includes('?') ? querystring.parse(target.substring(target.indexOf('?') + 1)) : {};
const params = querystring.stringify({
...targetParams,
Expand Down
7 changes: 0 additions & 7 deletions apps/stress-test/scripts/utils/configureYargs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ const cliOptions = {
describe: 'Optimization mode for Griffel.',
default: 'buildtime',
},
renderers: {
describe: 'Renderers to use for testing. This determines what is actually tested.',
},
'test-options': {
describe: 'Options to apply to each test. E.g., option1=value1 option2=value2',
coerce: (arg: string[]) => {
Expand Down Expand Up @@ -164,7 +161,6 @@ const configureYargs: CongfigureYargs = (command, y) => {
targets,
'test-options': testOptions,
port,
renderers,
} = cliOptions;
configure(y, {
scenario,
Expand All @@ -175,7 +171,6 @@ const configureYargs: CongfigureYargs = (command, y) => {
targets,
'test-options': testOptions,
port,
renderers,
});
break;
}
Expand All @@ -192,7 +187,6 @@ const configureYargs: CongfigureYargs = (command, y) => {
'process-results': processResults,
port,
root,
renderers,
} = cliOptions;
configure(y, {
scenario,
Expand All @@ -205,7 +199,6 @@ const configureYargs: CongfigureYargs = (command, y) => {
'process-results': processResults,
port,
root,
renderers,
});
break;
}
Expand Down
91 changes: 51 additions & 40 deletions apps/stress-test/scripts/utils/fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import fs from 'fs-extra';
import { join } from 'path';
import { getFixturesDir, remove } from './paths.js';
import { RandomTree } from '../../src/shared/tree/RandomTree.js';
import { RandomSelectorTreeNode, selectorTreeCreator } from '../../src/shared/tree/RandomSelectorTreeNode.js';
import { RandomTree } from './tree/RandomTree.js';
import { RandomSelectorTreeNode } from '../../src/shared/tree/types.js';
import { selectorTreeCreator } from './tree/RandomSelectorTreeNode.js';
import glob from 'glob';

type BuildTreeFixture = (name: string, options: { [key: string]: string }) => void;
Expand All @@ -20,77 +21,87 @@ const defaultFixtureOptions: DefaultFixtureOptions = {
// eslint-disable-next-line @typescript-eslint/naming-convention
xs_1: {
minBreadth: '1',
maxBreadth: '3',
maxBreadth: '5',
minDepth: '1',
maxDepth: '3',
maxDepth: '5',
targetSize: '250',
},
// eslint-disable-next-line @typescript-eslint/naming-convention
xs_2: {
minBreadth: '1',
maxBreadth: '3',
maxBreadth: '5',
minDepth: '1',
maxDepth: '3',
seed: '4212021',
maxDepth: '5',
targetSize: '250',
seed: '7032017',
},
// eslint-disable-next-line @typescript-eslint/naming-convention
s_1: {
minBreadth: '2',
maxBreadth: '5',
maxBreadth: '10',
minDepth: '2',
maxDepth: '5',
maxDepth: '10',
targetSize: '500',
},
// eslint-disable-next-line @typescript-eslint/naming-convention
s_2: {
minBreadth: '2',
maxBreadth: '5',
maxBreadth: '10',
minDepth: '2',
maxDepth: '5',
seed: '4212021',
maxDepth: '10',
targetSize: '500',
seed: '7032017',
},
// eslint-disable-next-line @typescript-eslint/naming-convention
m_1: {
minBreadth: '3',
maxBreadth: '8',
minDepth: '3',
maxDepth: '8',
minBreadth: '4',
maxBreadth: '20',
minDepth: '4',
maxDepth: '20',
targetSize: '1000',
},
// eslint-disable-next-line @typescript-eslint/naming-convention
m_2: {
minBreadth: '3',
maxBreadth: '8',
minDepth: '3',
maxDepth: '8',
seed: '4212021',
minBreadth: '4',
maxBreadth: '20',
minDepth: '4',
maxDepth: '20',
targetSize: '1000',
seed: '7032017',
},
// eslint-disable-next-line @typescript-eslint/naming-convention
l_1: {
minBreadth: '5',
maxBreadth: '15',
minDepth: '4',
maxDepth: '15',
minBreadth: '8',
maxBreadth: '40',
minDepth: '8',
maxDepth: '40',
targetSize: '2000',
},
// eslint-disable-next-line @typescript-eslint/naming-convention
l_2: {
minBreadth: '5',
maxBreadth: '15',
minDepth: '4',
maxDepth: '15',
seed: '4212021',
minBreadth: '8',
maxBreadth: '40',
minDepth: '8',
maxDepth: '40',
targetSize: '2000',
seed: '7032017',
},
// eslint-disable-next-line @typescript-eslint/naming-convention
xl_1: {
minBreadth: '10',
maxBreadth: '20',
minDepth: '10',
maxDepth: '20',
minBreadth: '16',
maxBreadth: '80',
minDepth: '16',
maxDepth: '80',
targetSize: '4000',
},
// eslint-disable-next-line @typescript-eslint/naming-convention
xl_2: {
minBreadth: '10',
maxBreadth: '20',
minDepth: '10',
maxDepth: '20',
seed: '4212021',
minBreadth: '16',
maxBreadth: '80',
minDepth: '16',
maxDepth: '80',
targetSize: '4000',
seed: '7032017',
},
};

Expand Down Expand Up @@ -123,7 +134,7 @@ export const buildTreeFixture: BuildTreeFixture = (name, options) => {

const data = {
tree: JSON.parse(jsonTree),
selectors,
selectors: Array.from(new Set(selectors)),
};

const js = `export default ${JSON.stringify(data, null, 2)};`;
Expand Down
Loading