Skip to content

Commit bb423d8

Browse files
committed
build: add infrastructure for performing size golden tests
1 parent c05a07e commit bb423d8

16 files changed

+277
-28
lines changed

.circleci/config.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -514,8 +514,8 @@ jobs:
514514
- *setup_bazel_remote_execution
515515
- *yarn_install
516516
- *setup_bazel_binary
517-
# Integration tests run with --config=view-engine because we release with View Engine.
518-
- run: bazel test integration/... --build_tests_only --config=view-engine
517+
- run: yarn integration-tests
518+
- run: yarn integration-tests:view-engine
519519

520520
# ----------------------------------------------------------------------------
521521
# Job that runs all Bazel tests against material-components-web@canary

.github/CODEOWNERS

+2
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,5 @@
319319
/.vscode/** @angular/dev-infra-components @mmalerba
320320
/src/* @jelbourn
321321
/goldens/ts-circular-deps.json @angular/dev-infra-components
322+
/goldens/size-test.json @jelbourn @mmalerba @crisbeto
323+
/goldens/* @angular/dev-infra-components

WORKSPACE

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
88
# Add NodeJS rules
99
http_archive(
1010
name = "build_bazel_rules_nodejs",
11-
sha256 = "d14076339deb08e5460c221fae5c5e9605d2ef4848eee1f0c81c9ffdc1ab31c1",
12-
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.6.1/rules_nodejs-1.6.1.tar.gz"],
11+
sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77",
12+
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"],
1313
)
1414

1515
# Add sass rules

goldens/BUILD.bazel

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
exports_files([
2+
"size-test.json",
3+
])

goldens/size-test.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"material/list": 224424
3+
}

integration/size-test/BUILD.bazel

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
load("//tools:defaults.bzl", "ts_library")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
exports_files([
6+
"rollup.config.js",
7+
"terser-config.json",
8+
])
9+
10+
ts_library(
11+
name = "check-size",
12+
srcs = ["check-size.ts"],
13+
deps = [
14+
"@npm//@types/node",
15+
"@npm//chalk",
16+
],
17+
)

integration/size-test/check-size.ts

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Script that measures the size of a given test file and compares it
3+
* with an entry in a file size golden. If the size deviates by certain
4+
* amount, the script will fail with a non-zero exit code.
5+
*/
6+
7+
import {statSync, readFileSync, writeFileSync} from 'fs';
8+
import chalk from 'chalk';
9+
10+
const [testId, testFileRootpath, isApprove] = process.argv.slice(2);
11+
const testFilePath = require.resolve(`angular_material/${testFileRootpath}`);
12+
const goldenFilePath = require.resolve('../../goldens/size-test.json');
13+
14+
const golden = JSON.parse(readFileSync(goldenFilePath, 'utf8'));
15+
const fileStat = statSync(testFilePath);
16+
const actualSize = fileStat.size;
17+
18+
// If in approve mode, update the golden to reflect the new size.
19+
if (isApprove) {
20+
golden[testId] = actualSize;
21+
writeFileSync(goldenFilePath, JSON.stringify(golden, null, 2));
22+
console.info(chalk.green(`Approved golden size for "${testId}"`));
23+
process.exit(0);
24+
}
25+
26+
// If no size has been collected for the test id, report an error.
27+
if (golden[testId] === undefined) {
28+
console.error(`No golden size for "${testId}". Please create a new entry.`);
29+
printApproveCommand();
30+
process.exit(1);
31+
}
32+
33+
const expectedSize = Number(golden[testId]);
34+
const absoluteSizeDiff = Math.abs(actualSize - expectedSize);
35+
36+
// Whether the size deviates by 1% from the expected.
37+
const deviatedByPercentage = absoluteSizeDiff > expectedSize / 100;
38+
// Whether the size deviates by 500 bytes from the expected.
39+
const deviatedByAbsoluteDiff = absoluteSizeDiff > 500;
40+
41+
if (deviatedByPercentage) {
42+
console.error(`Actual size deviates by more than 1% of the expected size. ` +
43+
`(expected: ${expectedSize}, actual: ${actualSize}).`);
44+
printApproveCommand();
45+
process.exit(1);
46+
} else if (deviatedByAbsoluteDiff) {
47+
console.error(`Actual size deviates by more than 500 bytes from the expected. ` +
48+
`(expected: ${expectedSize}, actual: ${actualSize}).`);
49+
printApproveCommand();
50+
process.exit(1);
51+
}
52+
53+
/** Prints the command for approving the current test. */
54+
function printApproveCommand() {
55+
console.info(chalk.yellow('You can approve the golden by running the following command:'));
56+
console.info(chalk.yellow(` bazel run ${process.env.BAZEL_TARGET}.approve`));
57+
}

integration/size-test/index.bzl

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary", "nodejs_test")
2+
load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
3+
load("@npm_bazel_terser//:index.bzl", "terser_minified")
4+
load("//tools:defaults.bzl", "ng_module")
5+
6+
"""
7+
Performs size measurements for the specified file. The file will be built as part
8+
of a `ng_module` and then will be optimized with build-optimizer, rollup and Terser.
9+
10+
The resulting size will be validated against a golden file to ensure that we don't
11+
regress in payload size, or that we can improvements to payload size.
12+
"""
13+
14+
def size_test(name, file, deps):
15+
# Generates an unique id for the given size test. e.g. if the macro is called
16+
# within the `integration/size-test/material` package with `name = list`, then
17+
# the unique id will be set to `material/list`.
18+
test_id = "%s/%s" % (native.package_name()[len("integration/size-test/"):], name)
19+
20+
ng_module(
21+
name = "%s_lib" % name,
22+
srcs = ["%s.ts" % name],
23+
testonly = True,
24+
deps = [
25+
"@npm//@angular/core",
26+
"@npm//@angular/platform-browser-dynamic",
27+
] + deps,
28+
)
29+
30+
rollup_bundle(
31+
name = "%s_bundle" % name,
32+
config_file = "//integration/size-test:rollup.config.js",
33+
testonly = True,
34+
entry_points = {
35+
(file): "%s_bundled" % name,
36+
},
37+
deps = [
38+
":%s_lib" % name,
39+
"@npm//rollup-plugin-node-resolve",
40+
"@npm//@angular-devkit/build-optimizer",
41+
],
42+
sourcemap = "false",
43+
)
44+
45+
terser_minified(
46+
testonly = True,
47+
name = "%s_bundle_min" % name,
48+
src = ":%s_bundle" % name,
49+
config_file = "//integration/size-test:terser-config.json",
50+
sourcemap = False,
51+
)
52+
53+
nodejs_test(
54+
name = name,
55+
data = [
56+
"//goldens:size-test.json",
57+
"//integration/size-test:check-size",
58+
":%s_bundle_min" % name,
59+
],
60+
entry_point = "//integration/size-test:check-size.ts",
61+
args = [test_id, "$(rootpath :%s_bundle_min)" % name],
62+
)
63+
64+
nodejs_binary(
65+
name = "%s.approve" % name,
66+
testonly = True,
67+
data = [
68+
"//goldens:size-test.json",
69+
"//integration/size-test:check-size",
70+
":%s_bundle_min" % name,
71+
],
72+
entry_point = "//integration/size-test:check-size.ts",
73+
args = [test_id, "$(rootpath :%s_bundle_min)" % name, "true"],
74+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
load("//integration/size-test:index.bzl", "size_test")
2+
3+
size_test(
4+
name = "list",
5+
file = "list.ts",
6+
deps = ["//src/material/list"],
7+
)
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {Component, NgModule} from '@angular/core';
2+
import {platformBrowser} from '@angular/platform-browser';
3+
import {MatListModule} from '@angular/material/list';
4+
5+
@Component({
6+
template: `
7+
<mat-nav-list>
8+
<mat-list-item>
9+
hello
10+
</mat-list-item>
11+
</mat-nav-list>
12+
`,
13+
})
14+
export class TestComponent {}
15+
16+
@NgModule({
17+
imports: [MatListModule],
18+
declarations: [TestComponent],
19+
bootstrap: [TestComponent],
20+
})
21+
export class AppModule {}
22+
23+
platformBrowser().bootstrapModule(AppModule);
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const {buildOptimizer} = require('@angular-devkit/build-optimizer');
2+
const node = require('rollup-plugin-node-resolve');
3+
4+
const buildOptimizerPlugin = {
5+
name: 'build-optimizer',
6+
transform: (content, id) => {
7+
const {content: code, sourceMap: map} = buildOptimizer({
8+
content,
9+
inputFilePath: id,
10+
emitSourceMap: true,
11+
isSideEffectFree: true,
12+
isAngularCoreFile: false,
13+
});
14+
if (!code) {
15+
return null;
16+
}
17+
if (!map) {
18+
throw new Error('No sourcemap produced by build optimizer');
19+
}
20+
return {code, map};
21+
},
22+
};
23+
24+
module.exports = {
25+
plugins: [
26+
buildOptimizerPlugin,
27+
node({
28+
mainFields: ['es2015_ivy_ngcc', 'module_ivy_ngcc','es2015', 'module'],
29+
}),
30+
],
31+
};
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"output": {
3+
"ecma": "es2015",
4+
"comments": false
5+
},
6+
"compress": {
7+
"global_defs": {
8+
"ngDevMode": false,
9+
"ngI18nClosureMode": false,
10+
"ngJitMode": false
11+
},
12+
"passes": 3,
13+
"pure_getters": true
14+
},
15+
"mangle": true
16+
}

integration/size-test/tsconfig.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"compilerOptions": {
3+
"lib": ["es2015"],
4+
"types": ["node"]
5+
}
6+
}

integration/ts-compat/BUILD.bazel

+3-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ typescript_version_packages = [
4040
],
4141
entry_point = "test.js",
4242
tags = [
43-
"integration",
43+
# These tests run in view engine only as the release packages are
44+
# built with View Engine and we want to reproduce this here.
45+
"view-engine-only",
4446
],
4547
)
4648
for ts_pkg_name in typescript_version_packages

package.json

+9-6
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444
"ts-circular-deps:check": "yarn -s ts-circular-deps check --config ./src/circular-deps-test.conf.js",
4545
"ts-circular-deps:approve": "yarn -s ts-circular-deps approve --config ./src/circular-deps-test.conf.js",
4646
"merge": "ng-dev pr merge",
47-
"approve-api": "node ./scripts/approve-api-golden.js"
47+
"approve-api": "node ./scripts/approve-api-golden.js",
48+
"integration-tests": "bazel test //integration/... --test_tag_filters=-view-engine-only --build_tests_only",
49+
"integration-tests:view-engine": "bazel test //integration/... --test_tag_filters=view-engine-only --build_tests_only --config=view-engine"
4850
},
4951
"version": "10.0.0-rc.0",
5052
"dependencies": {
@@ -79,11 +81,12 @@
7981
"@bazel/bazelisk": "^1.4.0",
8082
"@bazel/buildifier": "^2.2.1",
8183
"@bazel/ibazel": "^0.13.0",
82-
"@bazel/jasmine": "^1.6.0",
83-
"@bazel/karma": "^1.6.0",
84-
"@bazel/protractor": "^1.6.0",
85-
"@bazel/terser": "^1.4.1",
86-
"@bazel/typescript": "^1.6.0",
84+
"@bazel/jasmine": "^1.7.0",
85+
"@bazel/karma": "^1.7.0",
86+
"@bazel/protractor": "^1.7.0",
87+
"@bazel/rollup": "^1.7.0",
88+
"@bazel/terser": "^1.7.0",
89+
"@bazel/typescript": "^1.7.0",
8790
"@firebase/app-types": "^0.3.2",
8891
"@octokit/rest": "16.28.7",
8992
"@schematics/angular": "^10.0.0-rc.2",

yarn.lock

+22-17
Original file line numberDiff line numberDiff line change
@@ -400,37 +400,42 @@
400400
resolved "https://registry.yarnpkg.com/@bazel/ibazel/-/ibazel-0.13.0.tgz#2307bdf9ba0ad9835ee603b8fb521822bf3b8a11"
401401
integrity sha512-SFyS6oiibp8KW9BgAU+Tk0DhrH/sJx9om/OlB/Mg4hDEn8F8dj2QRqJE6CVhBCtLDcZDeD7WIAZKQDxKRYIShw==
402402

403-
"@bazel/jasmine@^1.6.0":
404-
version "1.6.0"
405-
resolved "https://registry.yarnpkg.com/@bazel/jasmine/-/jasmine-1.6.0.tgz#c469ab8725d9a2e48c0c3c965861ff8add9272ac"
406-
integrity sha512-WtOQDtIMHKTxlp0+FcdrADV6LMrpJV7eEGZippSNFPL5YhwwrPfCSOs5WkooatsrjL5YEszswzqQXFjvC7EZKQ==
403+
"@bazel/jasmine@^1.7.0":
404+
version "1.7.0"
405+
resolved "https://registry.yarnpkg.com/@bazel/jasmine/-/jasmine-1.7.0.tgz#429df76e6628aa139176340434729cc091e371d7"
406+
integrity sha512-LXq6nfBBEczjsDLwFW9kesGdewRrnFiAOZzXAAivCV3xtq516xK4QnVWA9tQGq+R1DnY50IaODpCJhh8PDezdg==
407407
dependencies:
408408
jasmine "~3.5.0"
409409
jasmine-core "~3.5.0"
410410
jasmine-reporters "~2.3.2"
411411
v8-coverage "1.0.9"
412412

413-
"@bazel/karma@^1.6.0":
414-
version "1.6.0"
415-
resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.6.0.tgz#98950b71114dd9ec169e6778a35d31ae1f578655"
416-
integrity sha512-9cX0E1SiMWwA70ZMFnMzeqSRn3biduGx03bGV77FSUYKocZpyfU2cOEygYGfxAqHnyM7x4cS8nflRv3+ZE0Aqg==
413+
"@bazel/karma@^1.7.0":
414+
version "1.7.0"
415+
resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.7.0.tgz#ec7e97a2629f5af0b2abe9a99ae30363a34af97d"
416+
integrity sha512-mGYVD9DldB3v/DjxJpS39X1vUD6M32Al96DMoilwW3TSAazcRWwUAC6HY9z5Wtyeqwxyk8BY1Mg1/berWpoTxg==
417417
dependencies:
418418
tmp "0.1.0"
419419

420-
"@bazel/protractor@^1.6.0":
421-
version "1.6.0"
422-
resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.6.0.tgz#cf095a1dbc038def7031c513a3b87f4e79bedb00"
423-
integrity sha512-gPiRv0oUJbVPpQ9nrwe5vjkffAc8VsYJhpTGgG+8aPdOaTLWgmBP/sy4BdfijU9O1Z/mNYojQCZgMzQz6kAvdg==
420+
"@bazel/protractor@^1.7.0":
421+
version "1.7.0"
422+
resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.7.0.tgz#1ced325a64d77bccca4bf881e62982d017d6b639"
423+
integrity sha512-sLbejWwmwTupCS3JKdBeiZMUbylLpJxJdlrz8sZ9t4KV6YiFAXNOloCScrrdOkeiJz5QQZRG3p3rqHbIszUAwQ==
424+
425+
"@bazel/rollup@^1.7.0":
426+
version "1.7.0"
427+
resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.7.0.tgz#5c0f0d51d2f3f14e78781a4b9e6a9ffba87f1579"
428+
integrity sha512-Pp5aCJw3gwu77zn6/fQgZ39ArrWEI5O3dja5wKadBnfOQ66PImIEr+bf7JgROoWvACH1kGxaS423rq51fiuCsA==
424429

425-
"@bazel/terser@^1.4.1":
430+
"@bazel/terser@^1.7.0":
426431
version "1.7.0"
427432
resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.7.0.tgz#c43e711e13b9a71c7abd3ade04fb4650d547ad01"
428433
integrity sha512-u/UXk0WUinvkk1g5xxfqGieBz3r12Bj2y2m25lC5GjHBgCpGk7DyeGGi9H3QQNO1Wmpw51QSE9gaPzKzjUVGug==
429434

430-
"@bazel/typescript@^1.6.0":
431-
version "1.6.0"
432-
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.6.0.tgz#8dfd29e71bcf917d5f9cb67f19ac4dcfc9082439"
433-
integrity sha512-vAKuwy1Hgl+t3M3sH/G0oqHRYN35TdENj+0lsCI3x7EbSzyI6cbA3YQrLrlyvdScksqOpZa3PZ3UBGqfJJq2DA==
435+
"@bazel/typescript@^1.7.0":
436+
version "1.7.0"
437+
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.7.0.tgz#8dc02b8a161f4fff3285186066b5f73666793452"
438+
integrity sha512-M6JPXJZ+W6457QZfPHmGg/Mejnp7//YTnffGmnmeK9vDqybXeCCRWW1/iEOwopLJYQViBHfaoulde0VXelx9sA==
434439
dependencies:
435440
protobufjs "6.8.8"
436441
semver "5.6.0"

0 commit comments

Comments
 (0)