diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js index 71ed759e4b6f..03171611ef6d 100644 --- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js @@ -7,8 +7,7 @@ const Audit = require('../audit'); const ConsistentlyInteractive = require('../../gather/computed/metrics/lantern-consistently-interactive'); // eslint-disable-line max-len -const NetworkAnalysis = require('../../gather/computed/network-analysis'); -const LoadSimulator = require('../../lib/dependency-graph/simulator/simulator.js'); +const Simulator = require('../../lib/dependency-graph/simulator/simulator'); // eslint-disable-line no-unused-vars const KB_IN_BYTES = 1024; @@ -69,33 +68,43 @@ class UnusedBytes extends Audit { } /** - * @param {!Artifacts} artifacts - * @return {!Promise} + * @param {Artifacts} artifacts + * @param {LH.Audit.Context=} context + * @return {Promise} */ - static audit(artifacts) { + static audit(artifacts, context) { const trace = artifacts.traces[Audit.DEFAULT_PASS]; const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; + const settings = context && context.settings || {}; + const simulatorOptions = { + devtoolsLog, + throttlingMethod: settings.throttlingMethod, + throttling: settings.throttling, + }; + return artifacts .requestNetworkRecords(devtoolsLog) .then(networkRecords => Promise.all([ this.audit_(artifacts, networkRecords), artifacts.requestPageDependencyGraph({trace, devtoolsLog}), + artifacts.requestLoadSimulator(simulatorOptions), ]) ) - .then(([result, graph]) => this.createAuditResult(result, graph)); + .then(([result, graph, simulator]) => this.createAuditResult(result, graph, simulator)); } /** * Computes the estimated effect of all the byte savings on the last long task * in the provided graph. * - * @param {!Array<{url: string, wastedBytes: number}>} results The array of byte savings results per resource - * @param {!Node} graph + * @param {Array<{url: string, wastedBytes: number}>} results The array of byte savings results per resource + * @param {Node} graph + * @param {Simulator} simulator * @return {number} */ static computeWasteWithTTIGraph(results, graph, simulator) { - const simulationBeforeChanges = simulator.simulate(); + const simulationBeforeChanges = simulator.simulate(graph); const resultsByUrl = new Map(); for (const result of results) { resultsByUrl.set(result.url, result); @@ -112,7 +121,7 @@ class UnusedBytes extends Audit { node.record._transferSize = Math.max(original - wastedBytes, 0); }); - const simulationAfterChanges = simulator.simulate(); + const simulationAfterChanges = simulator.simulate(graph); // Restore the original transfer size after we've done our simulation graph.traverse(node => { if (node.type !== 'network') return; @@ -131,20 +140,12 @@ class UnusedBytes extends Audit { } /** - * @param {!Audit.HeadingsResult} result - * @param {!Node} graph - * @return {!AuditResult} + * @param {Audit.HeadingsResult} result + * @param {Node} graph + * @param {Simulator} simulator + * @return {AuditResult} */ - static createAuditResult(result, graph) { - const records = []; - graph.traverse(node => node.record && records.push(node.record)); - const simulatorOptions = NetworkAnalysis.computeRTTAndServerResponseTime(records); - // TODO: use rtt/throughput from config.settings instead of defaults - delete simulatorOptions.rtt; - // TODO: calibrate multipliers, see https://github.com/GoogleChrome/lighthouse/issues/820 - Object.assign(simulatorOptions, {cpuSlowdownMultiplier: 1, layoutTaskMultiplier: 1}); - const simulator = new LoadSimulator(graph, simulatorOptions); - + static createAuditResult(result, graph, simulator) { const debugString = result.debugString; const results = result.results.sort((itemA, itemB) => itemB.wastedBytes - itemA.wastedBytes); diff --git a/lighthouse-core/config/constants.js b/lighthouse-core/config/constants.js index f7ce023b58af..6e8a8a74e325 100644 --- a/lighthouse-core/config/constants.js +++ b/lighthouse-core/config/constants.js @@ -14,6 +14,8 @@ const DEVTOOLS_RTT_ADJUSTMENT_FACTOR = 3.75; const DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR = 0.9; const throttling = { + DEVTOOLS_RTT_ADJUSTMENT_FACTOR, + DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR, mobile3G: { rttMs: 150, throughputKbps: 1.6 * 1024, diff --git a/lighthouse-core/gather/computed/load-simulator.js b/lighthouse-core/gather/computed/load-simulator.js new file mode 100644 index 000000000000..0f5b5c7a3439 --- /dev/null +++ b/lighthouse-core/gather/computed/load-simulator.js @@ -0,0 +1,66 @@ +/** + * @license Copyright 2018 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ +'use strict'; + +const ComputedArtifact = require('./computed-artifact'); +const constants = require('../../config/constants'); +const Simulator = require('../../lib/dependency-graph/simulator/simulator'); + +class LoadSimulatorArtifact extends ComputedArtifact { + get name() { + return 'LoadSimulator'; + } + + /** + * @param {{devtoolsLog: Array, settings: LH.ConfigSettings|undefined}} data + * @param {!Artifacts} artifacts + * @return {!Promise} + */ + async compute_(data, artifacts) { + const {throttlingMethod, throttling} = data.settings || {}; + const networkAnalysis = await artifacts.requestNetworkAnalysis(data.devtoolsLog); + + const options = { + additionalRttByOrigin: networkAnalysis.additionalRttByOrigin, + serverResponseTimeByOrigin: networkAnalysis.serverResponseTimeByOrigin, + }; + + switch (throttlingMethod) { + case 'provided': + options.rtt = networkAnalysis.rtt; + options.throughput = networkAnalysis.throughput; + options.cpuSlowdownMultiplier = 1; + options.layoutTaskMultiplier = 1; + break; + case 'devtools': + if (throttling) { + options.rtt = + throttling.requestLatencyMs / constants.throttling.DEVTOOLS_RTT_ADJUSTMENT_FACTOR; + options.throughput = + throttling.downloadThroughputKbps * 1024 / + constants.throttling.DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR; + } + + options.cpuSlowdownMultiplier = 1; + options.layoutTaskMultiplier = 1; + break; + case 'simulate': + if (throttling) { + options.rtt = throttling.rttMs; + options.throughput = throttling.throughputKbps * 1024; + options.cpuSlowdownMultiplier = throttling.cpuSlowdownMultiplier; + } + break; + default: + // intentionally fallback to simulator defaults + break; + } + + return new Simulator(options); + } +} + +module.exports = LoadSimulatorArtifact; diff --git a/lighthouse-core/gather/computed/metrics/lantern-metric.js b/lighthouse-core/gather/computed/metrics/lantern-metric.js index 578fa0e8312a..da998e1ed5de 100644 --- a/lighthouse-core/gather/computed/metrics/lantern-metric.js +++ b/lighthouse-core/gather/computed/metrics/lantern-metric.js @@ -8,7 +8,7 @@ const ComputedArtifact = require('../computed-artifact'); const Node = require('../../../lib/dependency-graph/node'); const NetworkNode = require('../../../lib/dependency-graph/network-node'); // eslint-disable-line no-unused-vars -const Simulator = require('../../../lib/dependency-graph/simulator/simulator'); +const Simulator = require('../../../lib/dependency-graph/simulator/simulator'); // eslint-disable-line no-unused-vars const WebInspector = require('../../../lib/web-inspector'); class LanternMetricArtifact extends ComputedArtifact { @@ -75,19 +75,15 @@ class LanternMetricArtifact extends ComputedArtifact { const {trace, devtoolsLog} = data; const graph = await artifacts.requestPageDependencyGraph({trace, devtoolsLog}); const traceOfTab = await artifacts.requestTraceOfTab(trace); - const networkAnalysis = await artifacts.requestNetworkAnalysis(devtoolsLog); + // TODO(phulce): passthrough settings once all metrics are converted to computed artifacts + /** @type {Simulator} */ + const simulator = await artifacts.requestLoadSimulator({devtoolsLog}); const optimisticGraph = this.getOptimisticGraph(graph, traceOfTab); const pessimisticGraph = this.getPessimisticGraph(graph, traceOfTab); - // TODO(phulce): use rtt and throughput from config.settings instead of defaults - const options = { - additionalRttByOrigin: networkAnalysis.additionalRttByOrigin, - serverResponseTimeByOrigin: networkAnalysis.serverResponseTimeByOrigin, - }; - - const optimisticSimulation = new Simulator(optimisticGraph, options).simulate(); - const pessimisticSimulation = new Simulator(pessimisticGraph, options).simulate(); + const optimisticSimulation = simulator.simulate(optimisticGraph); + const pessimisticSimulation = simulator.simulate(pessimisticGraph); const optimisticEstimate = this.getEstimateFromSimulation( optimisticSimulation, diff --git a/lighthouse-core/gather/computed/network-analysis.js b/lighthouse-core/gather/computed/network-analysis.js index d16d683afd6b..bf003a21172c 100644 --- a/lighthouse-core/gather/computed/network-analysis.js +++ b/lighthouse-core/gather/computed/network-analysis.js @@ -50,7 +50,7 @@ class NetworkAnalysis extends ComputedArtifact { const records = await artifacts.requestNetworkRecords(devtoolsLog); const throughput = await artifacts.requestNetworkThroughput(devtoolsLog); const rttAndServerResponseTime = NetworkAnalysis.computeRTTAndServerResponseTime(records); - rttAndServerResponseTime.throughput = throughput; + rttAndServerResponseTime.throughput = throughput * 8; // convert from KBps to Kbps return rttAndServerResponseTime; } } diff --git a/lighthouse-core/gather/computed/network-throughput.js b/lighthouse-core/gather/computed/network-throughput.js index ee233fe0ed70..9523018169ce 100644 --- a/lighthouse-core/gather/computed/network-throughput.js +++ b/lighthouse-core/gather/computed/network-throughput.js @@ -65,6 +65,7 @@ class NetworkThroughput extends ComputedArtifact { * @return {!Promise} */ compute_(devtoolsLog, artifacts) { + // TODO(phulce): migrate this to network-analysis computed artifact return artifacts.requestNetworkRecords(devtoolsLog) .then(NetworkThroughput.getThroughput); } diff --git a/lighthouse-core/gather/gather-runner.js b/lighthouse-core/gather/gather-runner.js index 433cb424e07b..b6ddcfbfc8e2 100644 --- a/lighthouse-core/gather/gather-runner.js +++ b/lighthouse-core/gather/gather-runner.js @@ -444,8 +444,8 @@ class GatherRunner { .then(_ => GatherRunner.disposeDriver(driver)) .then(_ => GatherRunner.collectArtifacts(gathererResults)) .then(artifacts => { - // Add tracing data to the artifacts object. - return Object.assign(artifacts, tracingData); + // Add tracing data and settings used to the artifacts object. + return Object.assign(artifacts, tracingData, {settings: options.settings}); }) // cleanup on error .catch(err => { diff --git a/lighthouse-core/lib/dependency-graph/simulator/simulator.js b/lighthouse-core/lib/dependency-graph/simulator/simulator.js index 959d415f7216..38980326454e 100644 --- a/lighthouse-core/lib/dependency-graph/simulator/simulator.js +++ b/lighthouse-core/lib/dependency-graph/simulator/simulator.js @@ -28,11 +28,9 @@ const NodeState = { class Simulator { /** - * @param {Node} graph * @param {LH.Gatherer.Simulation.Options} [options] */ - constructor(graph, options) { - this._graph = graph; + constructor(options) { this._options = Object.assign( { rtt: mobile3G.rttMs, @@ -46,13 +44,14 @@ class Simulator { this._rtt = this._options.rtt; this._throughput = this._options.throughput; - this._maximumConcurrentRequests = Math.min( + this._maximumConcurrentRequests = Math.max(Math.min( TcpConnection.maximumSaturatedConnections(this._rtt, this._throughput), this._options.maximumConcurrentRequests - ); + ), 1); this._cpuSlowdownMultiplier = this._options.cpuSlowdownMultiplier; this._layoutTaskMultiplier = this._cpuSlowdownMultiplier * this._options.layoutTaskMultiplier; + // Properties reset on every `.simulate` call but duplicated here for type checking this._nodeTiming = new Map(); this._numberInProgressByType = new Map(); this._nodes = {}; @@ -61,12 +60,12 @@ class Simulator { } /** - * + * @param {Node} graph */ - _initializeConnectionPool() { + _initializeConnectionPool(graph) { /** @type {LH.WebInspector.NetworkRequest[]} */ const records = []; - this._graph.getRootNode().traverse(node => { + graph.getRootNode().traverse(node => { if (node.type === Node.TYPES.NETWORK) { records.push(/** @type {NetworkNode} */ (node).record); } @@ -281,18 +280,19 @@ class Simulator { /** * Estimates the time taken to process all of the graph's nodes. + * @param {Node} graph * @return {LH.Gatherer.Simulation.Result} */ - simulate() { + simulate(graph) { // initialize the necessary data containers - this._initializeConnectionPool(); + this._initializeConnectionPool(graph); this._initializeAuxiliaryData(); const nodesNotReadyToStart = this._nodes[NodeState.NotReadyToStart]; const nodesReadyToStart = this._nodes[NodeState.ReadyToStart]; const nodesInProgress = this._nodes[NodeState.InProgress]; - const rootNode = this._graph.getRootNode(); + const rootNode = graph.getRootNode(); rootNode.traverse(node => nodesNotReadyToStart.add(node)); let totalElapsedTime = 0; diff --git a/lighthouse-core/runner.js b/lighthouse-core/runner.js index 9c4fa6eb1b79..65cdfb76c82e 100644 --- a/lighthouse-core/runner.js +++ b/lighthouse-core/runner.js @@ -6,6 +6,7 @@ // @ts-nocheck 'use strict'; +const isDeepEqual = require('lodash.isequal'); const Driver = require('./gather/driver.js'); const GatherRunner = require('./gather/gather-runner'); const ReportScoring = require('./scoring'); @@ -177,12 +178,23 @@ class Runner { artifacts = Object.assign(Runner.instantiateComputedArtifacts(), artifacts || opts.config.artifacts); + if (artifacts.settings) { + const overrides = {gatherMode: undefined, auditMode: undefined}; + const normalizedGatherSettings = Object.assign({}, artifacts.settings, overrides); + const normalizedAuditSettings = Object.assign({}, opts.settings, overrides); + + // TODO(phulce): allow change of throttling method to `simulate` + if (!isDeepEqual(normalizedGatherSettings, normalizedAuditSettings)) { + throw new Error('Cannot change settings between gathering and auditing'); + } + } + // Run each audit sequentially const auditResults = []; let promise = Promise.resolve(); for (const auditDefn of opts.config.audits) { promise = promise.then(_ => { - return Runner._runAudit(auditDefn, artifacts).then(ret => auditResults.push(ret)); + return Runner._runAudit(auditDefn, artifacts, opts).then(ret => auditResults.push(ret)); }); } return promise.then(_ => { @@ -204,10 +216,11 @@ class Runner { * Otherwise returns error audit result. * @param {!Audit} audit * @param {!Artifacts} artifacts + * @param {{settings: LH.ConfigSettings}} opts * @return {!Promise} * @private */ - static _runAudit(auditDefn, artifacts) { + static _runAudit(auditDefn, artifacts, opts) { const audit = auditDefn.implementation; const status = `Evaluating: ${audit.meta.description}`; @@ -248,7 +261,7 @@ class Runner { } } // all required artifacts are in good shape, so we proceed - return audit.audit(artifacts, {options: auditDefn.options || {}}); + return audit.audit(artifacts, {options: auditDefn.options || {}, settings: opts.settings}); // Fill remaining audit result fields. }).then(auditResult => Audit.generateAuditResult(audit, auditResult)) .catch(err => { diff --git a/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js b/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js index 1a52da01e54e..b4957763c817 100644 --- a/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js +++ b/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js @@ -9,6 +9,7 @@ const Runner = require('../../../runner'); const ByteEfficiencyAudit = require('../../../audits/byte-efficiency/byte-efficiency-audit'); const NetworkNode = require('../../../lib/dependency-graph/network-node'); const CPUNode = require('../../../lib/dependency-graph/cpu-node'); +const Simulator = require('../../../lib/dependency-graph/simulator/simulator'); const trace = require('../../fixtures/traces/progressive-app-m60.json'); const devtoolsLog = require('../../fixtures/traces/progressive-app-m60.devtools.log.json'); @@ -18,6 +19,7 @@ const assert = require('assert'); describe('Byte efficiency base audit', () => { let graph; + let simulator; beforeEach(() => { const networkRecord = { @@ -37,6 +39,7 @@ describe('Byte efficiency base audit', () => { graph = new NetworkNode(networkRecord); // add a CPU node to force improvement to TTI graph.addDependent(new CPUNode({tid: 1, ts: 0, dur: 100 * 1000})); + simulator = new Simulator({}); }); const baseHeadings = [ @@ -76,7 +79,7 @@ describe('Byte efficiency base audit', () => { const result = ByteEfficiencyAudit.createAuditResult({ headings: baseHeadings, results: [], - }, graph); + }, graph, simulator); assert.deepEqual(result.details.items, []); }); @@ -89,7 +92,8 @@ describe('Byte efficiency base audit', () => { {url: 'http://example.com/', wastedBytes: 200 * 1000}, ], }, - graph + graph, + simulator ); // 900ms savings comes from the graph calculation @@ -100,22 +104,22 @@ describe('Byte efficiency base audit', () => { const perfectResult = ByteEfficiencyAudit.createAuditResult({ headings: baseHeadings, results: [{url: 'http://example.com/', wastedBytes: 1 * 1000}], - }, graph); + }, graph, simulator); const goodResult = ByteEfficiencyAudit.createAuditResult({ headings: baseHeadings, results: [{url: 'http://example.com/', wastedBytes: 20 * 1000}], - }, graph); + }, graph, simulator); const averageResult = ByteEfficiencyAudit.createAuditResult({ headings: baseHeadings, results: [{url: 'http://example.com/', wastedBytes: 100 * 1000}], - }, graph); + }, graph, simulator); const failingResult = ByteEfficiencyAudit.createAuditResult({ headings: baseHeadings, results: [{url: 'http://example.com/', wastedBytes: 400 * 1000}], - }, graph); + }, graph, simulator); assert.equal(perfectResult.score, 1, 'scores perfect wastedMs'); assert.ok(goodResult.score > 0.75 && goodResult.score < 1, 'scores good wastedMs'); @@ -139,7 +143,7 @@ describe('Byte efficiency base audit', () => { {wastedBytes: 2048, totalBytes: 4096, wastedPercent: 50}, {wastedBytes: 1986, totalBytes: 5436}, ], - }, graph); + }, graph, simulator); assert.equal(result.details.items[0].wastedBytes, 2048); assert.equal(result.details.items[0].totalBytes, 4096); @@ -155,7 +159,7 @@ describe('Byte efficiency base audit', () => { {wastedBytes: 450, totalBytes: 1000, wastedPercent: 50}, {wastedBytes: 400, totalBytes: 450, wastedPercent: 50}, ], - }, graph); + }, graph, simulator); assert.equal(result.details.items[0].wastedBytes, 450); assert.equal(result.details.items[1].wastedBytes, 400); @@ -170,25 +174,28 @@ describe('Byte efficiency base audit', () => { {wastedBytes: 512, totalBytes: 1000, wastedPercent: 50}, {wastedBytes: 1024, totalBytes: 1200, wastedPercent: 50}, ], - }, graph); + }, graph, simulator); assert.ok(result.displayValue.includes('2048 bytes'), 'contains correct bytes'); }); - it('should work on real graphs', () => { + it('should work on real graphs', async () => { + const throttling = {rttMs: 150, throughputKbps: 1600, cpuSlowdownMultiplier: 1}; + const settings = {throttlingMethod: 'simulate', throttling}; const artifacts = Runner.instantiateComputedArtifacts(); - return artifacts.requestPageDependencyGraph({trace, devtoolsLog}).then(graph => { - const result = ByteEfficiencyAudit.createAuditResult( - { - headings: [{key: 'value', text: 'Label'}], - results: [ - {url: 'https://www.googletagmanager.com/gtm.js?id=GTM-Q5SW', wastedBytes: 30 * 1024}, - ], - }, - graph - ); - - assert.equal(result.rawValue, 300); - }); + const graph = await artifacts.requestPageDependencyGraph({trace, devtoolsLog}); + const simulator = await artifacts.requestLoadSimulator({devtoolsLog, settings}); + const result = ByteEfficiencyAudit.createAuditResult( + { + headings: [{key: 'value', text: 'Label'}], + results: [ + {url: 'https://www.googletagmanager.com/gtm.js?id=GTM-Q5SW', wastedBytes: 30 * 1024}, + ], + }, + graph, + simulator + ); + + assert.equal(result.rawValue, 300); }); }); diff --git a/lighthouse-core/test/gather/computed/load-simulator-test.js b/lighthouse-core/test/gather/computed/load-simulator-test.js new file mode 100644 index 000000000000..8d412face716 --- /dev/null +++ b/lighthouse-core/test/gather/computed/load-simulator-test.js @@ -0,0 +1,58 @@ +/** + * @license Copyright 2018 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ +'use strict'; + +/* eslint-env mocha */ + +const Runner = require('../../../runner'); +const assert = require('assert'); +const devtoolsLog = require('../../fixtures/traces/progressive-app-m60.devtools.log.json'); + +describe('Simulator artifact', () => { + it('returns a simulator for "provided" throttling', async () => { + const artifacts = Runner.instantiateComputedArtifacts(); + + const simulator = await artifacts.requestLoadSimulator({ + devtoolsLog, + settings: {throttlingMethod: 'provided'}, + }); + + assert.equal(Math.round(simulator._rtt), 3); + assert.equal(Math.round(simulator._throughput / 1024), 1590); + assert.equal(simulator._cpuSlowdownMultiplier, 1); + assert.equal(simulator._layoutTaskMultiplier, 1); + }); + + it('returns a simulator for "devtools" throttling', async () => { + const artifacts = Runner.instantiateComputedArtifacts(); + + const throttling = {requestLatencyMs: 375, downloadThroughputKbps: 900}; + const simulator = await artifacts.requestLoadSimulator({ + devtoolsLog, + settings: {throttlingMethod: 'devtools', throttling}, + }); + + assert.equal(simulator._rtt, 100); + assert.equal(simulator._throughput / 1024, 1000); + assert.equal(simulator._cpuSlowdownMultiplier, 1); + assert.equal(simulator._layoutTaskMultiplier, 1); + }); + + it('returns a simulator for "simulate" throttling', async () => { + const artifacts = Runner.instantiateComputedArtifacts(); + + const throttling = {rttMs: 120, throughputKbps: 1000, cpuSlowdownMultiplier: 3}; + const simulator = await artifacts.requestLoadSimulator({ + devtoolsLog, + settings: {throttlingMethod: 'simulate', throttling}, + }); + + assert.equal(simulator._rtt, 120); + assert.equal(simulator._throughput / 1024, 1000); + assert.equal(simulator._cpuSlowdownMultiplier, 3); + assert.equal(simulator._layoutTaskMultiplier, 1.5); + }); +}); diff --git a/lighthouse-core/test/lib/dependency-graph/simulator/simulator-test.js b/lighthouse-core/test/lib/dependency-graph/simulator/simulator-test.js index 853a24b5bf47..3c53b59f6e66 100644 --- a/lighthouse-core/test/lib/dependency-graph/simulator/simulator-test.js +++ b/lighthouse-core/test/lib/dependency-graph/simulator/simulator-test.js @@ -49,8 +49,8 @@ describe('DependencyGraph/Simulator', () => { it('should simulate basic network graphs', () => { const rootNode = new NetworkNode(request({})); - const simulator = new Simulator(rootNode, {serverResponseTimeByOrigin}); - const result = simulator.simulate(); + const simulator = new Simulator({serverResponseTimeByOrigin}); + const result = simulator.simulate(rootNode); // should be 2 RTTs and 500ms for the server response time assert.equal(result.timeInMs, 300 + 500); assertNodeTiming(result, rootNode, {startTime: 0, endTime: 800}); @@ -61,11 +61,11 @@ describe('DependencyGraph/Simulator', () => { const cpuNode = new CpuNode(cpuTask({duration: 200})); cpuNode.addDependency(rootNode); - const simulator = new Simulator(rootNode, { + const simulator = new Simulator({ serverResponseTimeByOrigin, cpuSlowdownMultiplier: 5, }); - const result = simulator.simulate(); + const result = simulator.simulate(rootNode); // should be 2 RTTs and 500ms for the server response time + 200 CPU assert.equal(result.timeInMs, 300 + 500 + 200); assertNodeTiming(result, rootNode, {startTime: 0, endTime: 800}); @@ -82,8 +82,8 @@ describe('DependencyGraph/Simulator', () => { nodeB.addDependent(nodeC); nodeC.addDependent(nodeD); - const simulator = new Simulator(nodeA, {serverResponseTimeByOrigin}); - const result = simulator.simulate(); + const simulator = new Simulator({serverResponseTimeByOrigin}); + const result = simulator.simulate(nodeA); // should be 800ms each for A, B, C, D assert.equal(result.timeInMs, 3200); assertNodeTiming(result, nodeA, {startTime: 0, endTime: 800}); @@ -102,11 +102,11 @@ describe('DependencyGraph/Simulator', () => { nodeA.addDependent(nodeC); nodeA.addDependent(nodeD); - const simulator = new Simulator(nodeA, { + const simulator = new Simulator({ serverResponseTimeByOrigin, cpuSlowdownMultiplier: 5, }); - const result = simulator.simulate(); + const result = simulator.simulate(nodeA); // should be 800ms A, then 1000 ms total for B, C, D in serial assert.equal(result.timeInMs, 1800); assertNodeTiming(result, nodeA, {startTime: 0, endTime: 800}); @@ -129,11 +129,11 @@ describe('DependencyGraph/Simulator', () => { nodeC.addDependent(nodeD); nodeC.addDependent(nodeF); // finishes 400 ms after D - const simulator = new Simulator(nodeA, { + const simulator = new Simulator({ serverResponseTimeByOrigin, cpuSlowdownMultiplier: 5, }); - const result = simulator.simulate(); + const result = simulator.simulate(nodeA); // should be 800ms each for A, B, C, D, with F finishing 400 ms after D assert.equal(result.timeInMs, 3600); }); @@ -148,8 +148,8 @@ describe('DependencyGraph/Simulator', () => { nodeA.addDependent(nodeC); nodeA.addDependent(nodeD); - const simulator = new Simulator(nodeA, {serverResponseTimeByOrigin}); - const result = simulator.simulate(); + const simulator = new Simulator({serverResponseTimeByOrigin}); + const result = simulator.simulate(nodeA); // should be 800ms for A and 950ms for C (2 round trips of downloading) assert.equal(result.timeInMs, 800 + 950); }); @@ -164,8 +164,8 @@ describe('DependencyGraph/Simulator', () => { nodeA.addDependent(nodeC); nodeA.addDependent(nodeD); - const simulator = new Simulator(nodeA, {serverResponseTimeByOrigin}); - const result = simulator.simulate(); + const simulator = new Simulator({serverResponseTimeByOrigin}); + const result = simulator.simulate(nodeA); // should be 800ms for A and 650ms for the next 3 assert.equal(result.timeInMs, 800 + 650 * 3); }); @@ -180,10 +180,38 @@ describe('DependencyGraph/Simulator', () => { nodeA.addDependent(nodeC); nodeA.addDependent(nodeD); - const simulator = new Simulator(nodeA, {serverResponseTimeByOrigin}); - const result = simulator.simulate(); + const simulator = new Simulator({serverResponseTimeByOrigin}); + const result = simulator.simulate(nodeA); // should be 800ms for A and 950ms for C (2 round trips of downloading) assert.equal(result.timeInMs, 800 + 950); }); + + it('should simulate two graphs in a row', () => { + const simulator = new Simulator({serverResponseTimeByOrigin}); + + const nodeA = new NetworkNode(request({})); + const nodeB = new NetworkNode(request({})); + const nodeC = new NetworkNode(request({transferSize: 15000})); + const nodeD = new NetworkNode(request({})); + + nodeA.addDependent(nodeB); + nodeA.addDependent(nodeC); + nodeA.addDependent(nodeD); + + const resultA = simulator.simulate(nodeA); + // should be 800ms for A and 950ms for C (2 round trips of downloading) + assert.equal(resultA.timeInMs, 800 + 950); + + const nodeE = new NetworkNode(request({})); + const nodeF = new NetworkNode(request({})); + const nodeG = new NetworkNode(request({})); + + nodeE.addDependent(nodeF); + nodeE.addDependent(nodeG); + + const resultB = simulator.simulate(nodeE); + // should be 800ms for E and 800ms for F/G + assert.equal(resultB.timeInMs, 800 + 800); + }); }); }); diff --git a/lighthouse-core/test/runner-test.js b/lighthouse-core/test/runner-test.js index 0dfb553714f0..3dc3f61b7f6d 100644 --- a/lighthouse-core/test/runner-test.js +++ b/lighthouse-core/test/runner-test.js @@ -83,6 +83,17 @@ describe('Runner', () => { }); }); + it('-A throws if the settings change', async () => { + const settings = {auditMode: artifactsPath, disableDeviceEmulation: true}; + const opts = {url, config: generateConfig(settings), driverMock}; + try { + await Runner.run(null, opts); + assert.fail('should have thrown'); + } catch (err) { + assert.ok(/Cannot change settings/.test(err.message), 'should have prevented run'); + } + }); + it('-GA is a normal run but it saves artifacts to disk', () => { const settings = {auditMode: artifactsPath, gatherMode: artifactsPath}; const opts = {url, config: generateConfig(settings), driverMock}; diff --git a/typings/audit.d.ts b/typings/audit.d.ts index 768c27108699..5483f02275e4 100644 --- a/typings/audit.d.ts +++ b/typings/audit.d.ts @@ -6,6 +6,11 @@ declare global { module LH.Audit { + export interface Context { + options: Object; // audit options + settings: ConfigSettings; + } + export interface ScoringModes { NUMERIC: 'numeric'; BINARY: 'binary';