From 29613538730b4f36fb2066afd986033a808b2c93 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 3 Jun 2024 17:25:40 -0700 Subject: [PATCH 01/12] wip --- .../byte-efficiency/byte-efficiency-audit.js | 2 +- core/computed/metrics/lantern-metric.js | 1 + core/lib/lantern/page-dependency-graph.js | 15 ++++++++++++++- .../byte-efficiency/byte-efficiency-audit-test.js | 10 +++++----- .../byte-efficiency/duplicated-javascript-test.js | 5 ----- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/core/audits/byte-efficiency/byte-efficiency-audit.js b/core/audits/byte-efficiency/byte-efficiency-audit.js index 22fbde1286f3..84efbcfd29ed 100644 --- a/core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/core/audits/byte-efficiency/byte-efficiency-audit.js @@ -120,7 +120,7 @@ class ByteEfficiencyAudit extends Audit { const originalTransferSizes = new Map(); graph.traverse(node => { if (node.type !== 'network') return; - const wastedBytes = wastedBytesByUrl.get(node.record.url); + const wastedBytes = wastedBytesByUrl.get(node.request.url); if (!wastedBytes) return; const original = node.request.transferSize; diff --git a/core/computed/metrics/lantern-metric.js b/core/computed/metrics/lantern-metric.js index 6c3c91f7d681..a3c08cd12cd4 100644 --- a/core/computed/metrics/lantern-metric.js +++ b/core/computed/metrics/lantern-metric.js @@ -52,6 +52,7 @@ async function getComputationDataParamsFromTrace(data, context) { } const {trace, URL} = data; + // TODO(15841): use computed artifact. const {graph} = await createGraphFromTrace(URL, trace, context); const processedNavigation = await ProcessedNavigation.request(data.trace, context); const simulator = data.simulator || (await LoadSimulator.request(data, context)); diff --git a/core/lib/lantern/page-dependency-graph.js b/core/lib/lantern/page-dependency-graph.js index 8e4a52d76d8d..c81fddeb9c17 100644 --- a/core/lib/lantern/page-dependency-graph.js +++ b/core/lib/lantern/page-dependency-graph.js @@ -712,6 +712,19 @@ class PageDependencyGraph { if (request.args.data.initiator?.fetchType === 'xmlhttprequest') { // @ts-expect-error yes XHR is a valid ResourceType. TypeScript const enums are so unhelpful. resourceType = 'XHR'; + } else if (request.args.data.initiator?.fetchType === 'fetch') { + // @ts-expect-error yes Fetch is a valid ResourceType. TypeScript const enums are so unhelpful. + resourceType = 'Fetch'; + } + + // TODO: set decodedBodyLength for data urls in Trace Engine. + let resourceSize = request.args.data.decodedBodyLength; + if (url.protocol === 'data:' && resourceSize === 0) { + const needle = 'base64,'; + const index = url.pathname.indexOf(needle); + if (index !== -1) { + resourceSize = atob(url.pathname.substring(index + needle.length)).length; + } } return { @@ -727,7 +740,7 @@ class PageDependencyGraph { responseHeadersEndTime: request.args.data.syntheticData.downloadStart / 1000, networkEndTime: request.args.data.syntheticData.finishTime / 1000, transferSize: request.args.data.encodedDataLength, - resourceSize: request.args.data.decodedBodyLength, + resourceSize, fromDiskCache: request.args.data.syntheticData.isDiskCached, fromMemoryCache: request.args.data.syntheticData.isMemoryCached, isLinkPreload: request.args.data.isLinkPreload, diff --git a/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js b/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js index 8f96f5e1582a..1fc78524384d 100644 --- a/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js +++ b/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js @@ -18,11 +18,6 @@ const trace = readJson('../../fixtures/artifacts/paul/trace.json', import.meta); const devtoolsLog = readJson('../../fixtures/artifacts/paul/devtoolslog.json', import.meta); describe('Byte efficiency base audit', () => { - // TODO(15841): investigate failures - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - let simulator; let metricComputationInput; @@ -122,6 +117,11 @@ describe('Byte efficiency base audit', () => { }); it('should estimate the FCP & LCP impact', async () => { + // TODO(15841): investigate failures + if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { + return; + } + const result = await ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, items: [ diff --git a/core/test/audits/byte-efficiency/duplicated-javascript-test.js b/core/test/audits/byte-efficiency/duplicated-javascript-test.js index 57f334627dfc..dddafc0a8670 100644 --- a/core/test/audits/byte-efficiency/duplicated-javascript-test.js +++ b/core/test/audits/byte-efficiency/duplicated-javascript-test.js @@ -313,11 +313,6 @@ describe('DuplicatedJavascript computed artifact', () => { }); it('.audit', async () => { - // TODO(15841): investigate failures - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - const artifacts = await loadArtifacts(`${LH_ROOT}/core/test/fixtures/artifacts/cnn`); const ultraSlowThrottling = {rttMs: 150, throughputKbps: 100, cpuSlowdownMultiplier: 8}; const settings = {throttlingMethod: 'simulate', throttling: ultraSlowThrottling}; From 508b46a597a1b5de618d1fcd9d455d938db355b8 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 3 Jun 2024 17:28:48 -0700 Subject: [PATCH 02/12] more --- core/audits/byte-efficiency/render-blocking-resources.js | 8 ++++---- core/audits/uses-rel-preconnect.js | 4 ++-- .../byte-efficiency/render-blocking-resources-test.js | 5 ----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/core/audits/byte-efficiency/render-blocking-resources.js b/core/audits/byte-efficiency/render-blocking-resources.js index 752de48fbaf5..31ae41d6f998 100644 --- a/core/audits/byte-efficiency/render-blocking-resources.js +++ b/core/audits/byte-efficiency/render-blocking-resources.js @@ -94,7 +94,7 @@ function computeStackSpecificTiming(node, nodeTiming, Stacks) { // https://github.com/ampproject/amphtml/blob/8e03ac2f315774070651584a7e046ff24212c9b1/src/font-stylesheet-timeout.js#L54-L59 // Any potential savings must only include time spent on AMP stylesheet nodes before 2.1 seconds. if (node.type === BaseNode.TYPES.NETWORK && - node.record.resourceType === NetworkRequest.TYPES.Stylesheet && + node.request.resourceType === NetworkRequest.TYPES.Stylesheet && nodeTiming.endTime > 2100) { stackSpecificTiming.endTime = Math.max(nodeTiming.startTime, 2100); stackSpecificTiming.duration = stackSpecificTiming.endTime - stackSpecificTiming.startTime; @@ -228,11 +228,11 @@ class RenderBlockingResources extends Audit { if (node.type !== BaseNode.TYPES.NETWORK) return !canDeferRequest; const isStylesheet = - node.record.resourceType === NetworkRequest.TYPES.Stylesheet; + node.request.resourceType === NetworkRequest.TYPES.Stylesheet; if (canDeferRequest && isStylesheet) { // We'll inline the used bytes of the stylesheet and assume the rest can be deferred - const wastedBytes = wastedCssBytesByUrl.get(node.record.url) || 0; - totalChildNetworkBytes += (node.record.transferSize || 0) - wastedBytes; + const wastedBytes = wastedCssBytesByUrl.get(node.request.url) || 0; + totalChildNetworkBytes += (node.request.transferSize || 0) - wastedBytes; } return !canDeferRequest; }); diff --git a/core/audits/uses-rel-preconnect.js b/core/audits/uses-rel-preconnect.js index c6908fd4c0e9..dc1a0c38bb91 100644 --- a/core/audits/uses-rel-preconnect.js +++ b/core/audits/uses-rel-preconnect.js @@ -146,14 +146,14 @@ class UsesRelPreconnectAudit extends Audit { /** @type {Set} */ const lcpGraphURLs = new Set(); lcpGraph.traverse(node => { - if (node.type === 'network') lcpGraphURLs.add(node.record.url); + if (node.type === 'network') lcpGraphURLs.add(node.request.url); }); const fcpGraph = await LanternFirstContentfulPaint.getPessimisticGraph(pageGraph, processedNavigation); const fcpGraphURLs = new Set(); fcpGraph.traverse(node => { - if (node.type === 'network') fcpGraphURLs.add(node.record.url); + if (node.type === 'network') fcpGraphURLs.add(node.request.url); }); /** @type {Map} */ diff --git a/core/test/audits/byte-efficiency/render-blocking-resources-test.js b/core/test/audits/byte-efficiency/render-blocking-resources-test.js index 7f970d36a495..403a69ec5b83 100644 --- a/core/test/audits/byte-efficiency/render-blocking-resources-test.js +++ b/core/test/audits/byte-efficiency/render-blocking-resources-test.js @@ -20,11 +20,6 @@ const devtoolsLog = readJson('../../fixtures/artifacts/render-blocking/devtoolsl const mobileSlow4G = constants.throttling.mobileSlow4G; describe('Render blocking resources audit', () => { - // TODO(15841): investigate failures - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - it('evaluates render blocking input correctly', async () => { const artifacts = { URL: getURLArtifactFromDevtoolsLog(devtoolsLog), From aa54aa2282455cecbccc0fcbe793e85581998940 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 3 Jun 2024 17:37:24 -0700 Subject: [PATCH 03/12] more --- core/audits/byte-efficiency/offscreen-images.js | 2 +- core/audits/byte-efficiency/render-blocking-resources.js | 2 +- core/audits/dobetterweb/uses-http2.js | 4 ++-- core/audits/prioritize-lcp-image.js | 2 +- core/lib/lantern/network-node.js | 1 - core/lib/lantern/simulator/simulator.js | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/audits/byte-efficiency/offscreen-images.js b/core/audits/byte-efficiency/offscreen-images.js index 2e88b7e29b76..5c2549de5bb5 100644 --- a/core/audits/byte-efficiency/offscreen-images.js +++ b/core/audits/byte-efficiency/offscreen-images.js @@ -128,7 +128,7 @@ class OffscreenImages extends ByteEfficiencyAudit { if (node.type === 'cpu' && timing.duration >= 50) { lastLongTaskStartTime = Math.max(lastLongTaskStartTime, timing.startTime); } else if (node.type === 'network') { - startTimesByURL.set(node.record.url, timing.startTime); + startTimesByURL.set(node.request.url, timing.startTime); } } diff --git a/core/audits/byte-efficiency/render-blocking-resources.js b/core/audits/byte-efficiency/render-blocking-resources.js index 31ae41d6f998..e0ba123e6a50 100644 --- a/core/audits/byte-efficiency/render-blocking-resources.js +++ b/core/audits/byte-efficiency/render-blocking-resources.js @@ -247,7 +247,7 @@ class RenderBlockingResources extends Audit { )); // Add the inlined bytes to the HTML response - const originalTransferSize = minimalFCPGraph.record.transferSize; + const originalTransferSize = minimalFCPGraph.request.transferSize; const safeTransferSize = originalTransferSize || 0; minimalFCPGraph.request.transferSize = safeTransferSize + totalChildNetworkBytes; const estimateAfterInline = simulator.simulate(minimalFCPGraph).timeInMs; diff --git a/core/audits/dobetterweb/uses-http2.js b/core/audits/dobetterweb/uses-http2.js index 1b4180d6f668..a06eced68420 100644 --- a/core/audits/dobetterweb/uses-http2.js +++ b/core/audits/dobetterweb/uses-http2.js @@ -87,9 +87,9 @@ class UsesHTTP2Audit extends Audit { const originalProtocols = new Map(); graph.traverse(node => { if (node.type !== 'network') return; - if (!urlsToChange.has(node.record.url)) return; + if (!urlsToChange.has(node.request.url)) return; - originalProtocols.set(node.request.requestId, node.record.protocol); + originalProtocols.set(node.request.requestId, node.request.protocol); node.request.protocol = 'h2'; }); diff --git a/core/audits/prioritize-lcp-image.js b/core/audits/prioritize-lcp-image.js index cd6f1c308c68..82b41f208af7 100644 --- a/core/audits/prioritize-lcp-image.js +++ b/core/audits/prioritize-lcp-image.js @@ -222,7 +222,7 @@ class PrioritizeLcpImage extends Audit { wastedMs, results: [{ node: Audit.makeNodeItem(lcpElement.node), - url: lcpNode.record.url, + url: lcpNode.request.url, wastedMs, }], }; diff --git a/core/lib/lantern/network-node.js b/core/lib/lantern/network-node.js index b445117cb44b..adb7760a6edc 100644 --- a/core/lib/lantern/network-node.js +++ b/core/lib/lantern/network-node.js @@ -18,7 +18,6 @@ const NON_NETWORK_SCHEMES = [ ]; /** - * Use `NetworkRequest.isNonNetworkRequest(req)` if working with a request. * Note: the `protocol` field from CDP can be 'h2', 'http', (not 'https'!) or it'll be url's scheme. * https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/protocol/network_handler.cc;l=598-611;drc=56d4a9a9deb30be73adcee8737c73bcb2a5ab64f * However, a `new URL(href).protocol` has a colon suffix. diff --git a/core/lib/lantern/simulator/simulator.js b/core/lib/lantern/simulator/simulator.js index b24ca43008d1..79a3b594a4ff 100644 --- a/core/lib/lantern/simulator/simulator.js +++ b/core/lib/lantern/simulator/simulator.js @@ -34,7 +34,7 @@ const NodeState = { Complete: 3, }; -/** @type {Record} */ +/** @type {Record} */ const PriorityStartTimePenalty = { VeryHigh: 0, High: 0.25, From 19dd64dbfba2baf1316a2599d5a26e1307d43ca4 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 3 Jun 2024 18:27:24 -0700 Subject: [PATCH 04/12] fix dom-size test --- core/test/audits/dobetterweb/dom-size-test.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/core/test/audits/dobetterweb/dom-size-test.js b/core/test/audits/dobetterweb/dom-size-test.js index 1027075366db..8e8d62b4bbf6 100644 --- a/core/test/audits/dobetterweb/dom-size-test.js +++ b/core/test/audits/dobetterweb/dom-size-test.js @@ -18,19 +18,17 @@ describe('DOMSize audit', () => { beforeEach(() => { const mainDocumentUrl = 'https://example.com/'; - + const networkRecords = [{url: mainDocumentUrl, priority: 'High'}]; const trace = createTestTrace({ topLevelTasks: [ {ts: 1000, duration: 1000, children: [ {ts: 1100, duration: 200, eventName: 'ScheduleStyleRecalculation'}, ]}, ], + networkRecords, }); - const devtoolsLog = networkRecordsToDevtoolsLog([{ - url: mainDocumentUrl, - priority: 'High', - }]); + const devtoolsLog = networkRecordsToDevtoolsLog(networkRecords); artifacts = { DOMStats: { @@ -55,11 +53,6 @@ describe('DOMSize audit', () => { }); it('calculates score hitting mid distribution', async () => { - // TODO(15841): investigate failures - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - const auditResult = await DOMSize.audit(artifacts, context); assert.equal(auditResult.score, 0.43); assert.equal(auditResult.numericValue, 1500); From a942e0a6681de9bf6fcae7e4cff5a980879c3c3c Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 3 Jun 2024 19:08:14 -0700 Subject: [PATCH 05/12] misc --- core/lib/lantern/page-dependency-graph.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/lib/lantern/page-dependency-graph.js b/core/lib/lantern/page-dependency-graph.js index c81fddeb9c17..b04a99459785 100644 --- a/core/lib/lantern/page-dependency-graph.js +++ b/core/lib/lantern/page-dependency-graph.js @@ -718,7 +718,7 @@ class PageDependencyGraph { } // TODO: set decodedBodyLength for data urls in Trace Engine. - let resourceSize = request.args.data.decodedBodyLength; + let resourceSize = request.args.data.decodedBodyLength ?? 0; if (url.protocol === 'data:' && resourceSize === 0) { const needle = 'base64,'; const index = url.pathname.indexOf(needle); @@ -830,6 +830,9 @@ class PageDependencyGraph { redirectedRequest.parsedURL = this._createParsedUrl(redirect.url); // TODO: TraceEngine is not retaining the actual status code. redirectedRequest.statusCode = 302; + redirectedRequest.resourceType = undefined; + // TODO: TraceEngine is not retaining transfer size of redirected request. + redirectedRequest.transferSize = 400; requestChain.push(redirectedRequest); lanternRequests.push(redirectedRequest); } From 2a12f9aea065466a36236122b19402740974f9e2 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 3 Jun 2024 19:46:30 -0700 Subject: [PATCH 06/12] handle redirects better --- core/lib/lantern/page-dependency-graph.js | 42 +++++++++++++------ .../largest-contentful-paint-element-test.js | 12 +++--- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/core/lib/lantern/page-dependency-graph.js b/core/lib/lantern/page-dependency-graph.js index b04a99459785..a7de6562bb95 100644 --- a/core/lib/lantern/page-dependency-graph.js +++ b/core/lib/lantern/page-dependency-graph.js @@ -813,19 +813,35 @@ class PageDependencyGraph { const requestChain = []; for (const redirect of redirects) { const redirectedRequest = structuredClone(request); - if (redirectedRequest.timing) { - // TODO: These are surely wrong for when there is more than one redirect. Would be - // simpler if each redirect remembered it's `timing` object in this `redirects` array. - redirectedRequest.timing.requestTime = redirect.ts / 1000 / 1000; - redirectedRequest.timing.receiveHeadersStart -= redirect.dur / 1000 / 1000; - redirectedRequest.timing.receiveHeadersEnd -= redirect.dur / 1000 / 1000; - redirectedRequest.rendererStartTime = redirect.ts / 1000; - redirectedRequest.networkRequestTime = redirect.ts / 1000; - redirectedRequest.networkEndTime = (redirect.ts + redirect.dur) / 1000; - redirectedRequest.responseHeadersEndTime = - redirectedRequest.timing.requestTime * 1000 + - redirectedRequest.timing.receiveHeadersEnd; - } + + redirectedRequest.networkRequestTime = redirect.ts / 1000; + redirectedRequest.rendererStartTime = redirectedRequest.networkRequestTime; + + redirectedRequest.networkEndTime = (redirect.ts + redirect.dur) / 1000; + redirectedRequest.responseHeadersEndTime = redirectedRequest.networkEndTime; + + redirectedRequest.timing = { + requestTime: redirectedRequest.networkRequestTime / 1000, + receiveHeadersStart: redirectedRequest.responseHeadersEndTime, + receiveHeadersEnd: redirectedRequest.responseHeadersEndTime, + proxyStart: -1, + proxyEnd: -1, + dnsStart: -1, + dnsEnd: -1, + connectStart: -1, + connectEnd: -1, + sslStart: -1, + sslEnd: -1, + sendStart: -1, + sendEnd: -1, + workerStart: -1, + workerReady: -1, + workerFetchStart: -1, + workerRespondWithSettled: -1, + pushStart: -1, + pushEnd: -1, + }; + redirectedRequest.url = redirect.url; redirectedRequest.parsedURL = this._createParsedUrl(redirect.url); // TODO: TraceEngine is not retaining the actual status code. diff --git a/core/test/audits/largest-contentful-paint-element-test.js b/core/test/audits/largest-contentful-paint-element-test.js index dfe5162e7776..d89a573cb07f 100644 --- a/core/test/audits/largest-contentful-paint-element-test.js +++ b/core/test/audits/largest-contentful-paint-element-test.js @@ -22,7 +22,7 @@ function mockNetworkRecords() { isLinkPreload: false, networkRequestTime: 0, networkEndTime: 500, - timing: {sendEnd: 0, receiveHeadersEnd: 500}, + responseHeadersEndTime: 500, responseHeadersTransferSize: 400, transferSize: 400, url: requestedUrl, @@ -110,8 +110,8 @@ describe('Performance: largest-contentful-paint-element audit', () => { expect(auditResult.score).toEqual(0); expect(auditResult.notApplicable).toBeUndefined(); - expect(auditResult.displayValue).toBeDisplayString('5,800\xa0ms'); - expect(auditResult.metricSavings).toEqual({LCP: 3304}); // 5804 - 2500 (p10 mobile) + expect(auditResult.displayValue).toBeDisplayString('5,340\xa0ms'); + expect(auditResult.metricSavings).toEqual({LCP: 2837}); // calculated LCP - 2500 (p10 mobile) expect(auditResult.details.items).toHaveLength(2); expect(auditResult.details.items[0].items).toHaveLength(1); expect(auditResult.details.items[0].items[0].node.path).toEqual('1,HTML,3,BODY,5,DIV,0,HEADER'); @@ -123,11 +123,11 @@ describe('Performance: largest-contentful-paint-element audit', () => { expect(auditResult.details.items[1].items[0].phase).toBeDisplayString('TTFB'); expect(auditResult.details.items[1].items[0].timing).toBeCloseTo(800, 0.1); expect(auditResult.details.items[1].items[1].phase).toBeDisplayString('Load Delay'); - expect(auditResult.details.items[1].items[1].timing).toBeCloseTo(651, 0.1); + expect(auditResult.details.items[1].items[1].timing).toBeCloseTo(534.2, 0.1); expect(auditResult.details.items[1].items[2].phase).toBeDisplayString('Load Time'); - expect(auditResult.details.items[1].items[2].timing).toBeCloseTo(1813.7, 0.1); + expect(auditResult.details.items[1].items[2].timing).toBeCloseTo(1667.8, 0.1); expect(auditResult.details.items[1].items[3].phase).toBeDisplayString('Render Delay'); - expect(auditResult.details.items[1].items[3].timing).toBeCloseTo(2539.2, 0.1); + expect(auditResult.details.items[1].items[3].timing).toBeCloseTo(2334.9, 0.1); }); it('doesn\'t throw an error when there is nothing to show', async () => { From ed0ef5c5e9c5bfe7585f0f12abcc85c14b7121cd Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 3 Jun 2024 19:57:08 -0700 Subject: [PATCH 07/12] fix initiator in createTestTrace --- core/test/audits/largest-contentful-paint-element-test.js | 5 ----- core/test/create-test-trace.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/core/test/audits/largest-contentful-paint-element-test.js b/core/test/audits/largest-contentful-paint-element-test.js index d89a573cb07f..c16e680727dd 100644 --- a/core/test/audits/largest-contentful-paint-element-test.js +++ b/core/test/audits/largest-contentful-paint-element-test.js @@ -68,11 +68,6 @@ function mockNetworkRecords() { } describe('Performance: largest-contentful-paint-element audit', () => { - // TODO(15841): investigate failures - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - it('correctly surfaces the LCP element', async () => { const networkRecords = mockNetworkRecords(); const artifacts = { diff --git a/core/test/create-test-trace.js b/core/test/create-test-trace.js index 5b753316db3a..c09617a97455 100644 --- a/core/test/create-test-trace.js +++ b/core/test/create-test-trace.js @@ -289,7 +289,6 @@ function createTestTrace(options) { data: { requestId, frame: record.frameId, - initiator: record.initiator ?? {type: 'other'}, }, }, }); @@ -307,6 +306,7 @@ function createTestTrace(options) { data: { requestId, frame: record.frameId, + initiator: record.initiator ?? {type: 'other'}, priority: record.priority, requestMethod: record.requestMethod, resourceType: record.resourceType ?? 'Document', From 4730552d0e1af31a4352e3c32cd4ba95a202eb7f Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Mon, 3 Jun 2024 20:47:14 -0700 Subject: [PATCH 08/12] update test result cuz of new graph edge --- .../audits/byte-efficiency/byte-efficiency-audit-test.js | 7 +------ core/test/create-test-trace.js | 7 ------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js b/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js index 1fc78524384d..361dfa272f85 100644 --- a/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js +++ b/core/test/audits/byte-efficiency/byte-efficiency-audit-test.js @@ -117,11 +117,6 @@ describe('Byte efficiency base audit', () => { }); it('should estimate the FCP & LCP impact', async () => { - // TODO(15841): investigate failures - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - const result = await ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, items: [ @@ -131,7 +126,7 @@ describe('Byte efficiency base audit', () => { }, simulator, metricComputationInput, {computedCache: new Map()}); assert.equal(result.metricSavings.FCP, 900); - assert.equal(result.metricSavings.LCP, 1350); + assert.equal(result.metricSavings.LCP, 900); }); it('should use LCP request savings if larger than LCP graph savings', async () => { diff --git a/core/test/create-test-trace.js b/core/test/create-test-trace.js index c09617a97455..f45ec21c827d 100644 --- a/core/test/create-test-trace.js +++ b/core/test/create-test-trace.js @@ -246,13 +246,6 @@ function createTestTrace(options) { traceEvents.push(getTopLevelTask({ts: options.traceEnd - 1, duration: 1})); } - // TODO(15841): why does "should estimate the FCP & LCP impact" in byte-efficiency-audit-test.js - // fail even when not creating records from trace? For now... just don't emit these events for - // when using CDP. - if (!process.env.INTERNAL_LANTERN_USE_TRACE) { - options.networkRecords = undefined; - } - const networkRecords = options.networkRecords || []; for (const record of networkRecords) { // `requestId` is optional in the input test records. From 7f85f19bdde6abf922c9e3a385aa5ac63deb5175 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Tue, 4 Jun 2024 09:05:40 -0700 Subject: [PATCH 09/12] more tests are working now --- core/test/audits/prioritize-lcp-image-test.js | 5 ----- core/test/computed/metrics/lcp-breakdown-test.js | 5 ----- 2 files changed, 10 deletions(-) diff --git a/core/test/audits/prioritize-lcp-image-test.js b/core/test/audits/prioritize-lcp-image-test.js index 392353289fed..46f1ffe3c24d 100644 --- a/core/test/audits/prioritize-lcp-image-test.js +++ b/core/test/audits/prioritize-lcp-image-test.js @@ -16,11 +16,6 @@ const scriptUrl = 'http://www.example.com/script.js'; const imageUrl = 'http://www.example.com/image.png'; describe('Performance: prioritize-lcp-image audit', () => { - // TODO(15841): fix createTestTrace, cycles - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - const mockArtifacts = (networkRecords, URL) => { return { GatherContext: {gatherMode: 'navigation'}, diff --git a/core/test/computed/metrics/lcp-breakdown-test.js b/core/test/computed/metrics/lcp-breakdown-test.js index 04e8a49d912a..c6fdcee5b8ea 100644 --- a/core/test/computed/metrics/lcp-breakdown-test.js +++ b/core/test/computed/metrics/lcp-breakdown-test.js @@ -130,11 +130,6 @@ describe('LCPBreakdown', () => { }); it('returns breakdown for image LCP', async () => { - // TODO(15841): fix createTestTrace, cycles - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - const networkRecords = mockNetworkRecords(); const data = mockData(networkRecords); From 6ccb99099adaeeb47ed178baab39d21699ffd842 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Tue, 4 Jun 2024 09:30:30 -0700 Subject: [PATCH 10/12] cover more tests in 3p facades --- core/test/audits/third-party-facades-test.js | 88 ++++++++++++-------- 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/core/test/audits/third-party-facades-test.js b/core/test/audits/third-party-facades-test.js index d16a6b1821ba..004f8a73407a 100644 --- a/core/test/audits/third-party-facades-test.js +++ b/core/test/audits/third-party-facades-test.js @@ -33,21 +33,17 @@ function youtubeResourceUrl(id) { return `https://i.ytimg.com/${id}/maxresdefault.jpg`; } describe('Third party facades audit', () => { - // TODO(15841): traces needs updating. - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - it('correctly identifies a third party product with facade alternative', async () => { + const networkRecords = [ + {transferSize: 2000, url: 'https://example.com', priority: 'High'}, + {transferSize: 4000, url: intercomProductUrl('1')}, + {transferSize: 8000, url: intercomResourceUrl('a')}, + ]; const artifacts = { devtoolsLogs: { - defaultPass: networkRecordsToDevtoolsLog([ - {transferSize: 2000, url: 'https://example.com', priority: 'High'}, - {transferSize: 4000, url: intercomProductUrl('1')}, - {transferSize: 8000, url: intercomResourceUrl('a')}, - ]), + defaultPass: networkRecordsToDevtoolsLog(networkRecords), }, - traces: {defaultPass: createTestTrace({timeOrigin: 0, traceEnd: 2000})}, + traces: {defaultPass: createTestTrace({timeOrigin: 0, traceEnd: 2000, networkRecords})}, URL: { requestedUrl: 'https://example.com', mainDocumentUrl: 'https://example.com', @@ -91,17 +87,18 @@ describe('Third party facades audit', () => { }); it('handles multiple products with facades', async () => { + const networkRecords = [ + {transferSize: 2000, url: 'https://example.com', priority: 'High'}, + {transferSize: 4000, url: intercomProductUrl('1')}, + {transferSize: 3000, url: youtubeProductUrl('2')}, + {transferSize: 8000, url: intercomResourceUrl('a')}, + {transferSize: 7000, url: youtubeResourceUrl('b')}, + ]; const artifacts = { devtoolsLogs: { - defaultPass: networkRecordsToDevtoolsLog([ - {transferSize: 2000, url: 'https://example.com', priority: 'High'}, - {transferSize: 4000, url: intercomProductUrl('1')}, - {transferSize: 3000, url: youtubeProductUrl('2')}, - {transferSize: 8000, url: intercomResourceUrl('a')}, - {transferSize: 7000, url: youtubeResourceUrl('b')}, - ]), + defaultPass: networkRecordsToDevtoolsLog(networkRecords), }, - traces: {defaultPass: createTestTrace({timeOrigin: 0, traceEnd: 2000})}, + traces: {defaultPass: createTestTrace({timeOrigin: 0, traceEnd: 2000, networkRecords})}, URL: { requestedUrl: 'https://example.com', mainDocumentUrl: 'https://example.com', @@ -167,16 +164,17 @@ describe('Third party facades audit', () => { }); it('handle multiple requests to same product resource', async () => { + const networkRecords = [ + {transferSize: 2000, url: 'https://example.com', priority: 'High'}, + {transferSize: 2000, url: intercomProductUrl('1')}, + {transferSize: 8000, url: intercomResourceUrl('a')}, + {transferSize: 2000, url: intercomProductUrl('1')}, + ]; const artifacts = { devtoolsLogs: { - defaultPass: networkRecordsToDevtoolsLog([ - {transferSize: 2000, url: 'https://example.com', priority: 'High'}, - {transferSize: 2000, url: intercomProductUrl('1')}, - {transferSize: 8000, url: intercomResourceUrl('a')}, - {transferSize: 2000, url: intercomProductUrl('1')}, - ]), + defaultPass: networkRecordsToDevtoolsLog(networkRecords), }, - traces: {defaultPass: createTestTrace({timeOrigin: 0, traceEnd: 2000})}, + traces: {defaultPass: createTestTrace({timeOrigin: 0, traceEnd: 2000, networkRecords})}, URL: { requestedUrl: 'https://example.com', mainDocumentUrl: 'https://example.com', @@ -219,14 +217,15 @@ describe('Third party facades audit', () => { }); it('does not report first party resources', async () => { + const networkRecords = [ + {transferSize: 2000, url: 'https://intercomcdn.com', priority: 'High'}, + {transferSize: 4000, url: intercomProductUrl('1')}, + ]; const artifacts = { devtoolsLogs: { - defaultPass: networkRecordsToDevtoolsLog([ - {transferSize: 2000, url: 'https://intercomcdn.com', priority: 'High'}, - {transferSize: 4000, url: intercomProductUrl('1')}, - ]), + defaultPass: networkRecordsToDevtoolsLog(networkRecords), }, - traces: {defaultPass: createTestTrace({timeOrigin: 0, traceEnd: 2000})}, + traces: {defaultPass: createTestTrace({timeOrigin: 0, traceEnd: 2000, networkRecords})}, URL: { requestedUrl: 'https://intercomcdn.com', mainDocumentUrl: 'https://intercomcdn.com', @@ -246,6 +245,11 @@ describe('Third party facades audit', () => { }); it('only reports resources which have facade alternatives', async () => { + // TODO(15841): traces needs updating. + if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { + return; + } + const artifacts = { // This devtools log has third party requests but none have facades devtoolsLogs: {defaultPass: pwaDevtoolsLog}, @@ -265,11 +269,17 @@ describe('Third party facades audit', () => { }); it('not applicable when no third party resources are present', async () => { + // TODO(15841): traces needs updating. + if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { + return; + } + + const networkRecords = [ + {transferSize: 2000, url: 'https://example.com', priority: 'High'}, + ]; const artifacts = { devtoolsLogs: { - defaultPass: networkRecordsToDevtoolsLog([ - {transferSize: 2000, url: 'https://example.com', priority: 'High'}, - ]), + defaultPass: networkRecordsToDevtoolsLog(networkRecords), }, traces: {defaultPass: noThirdPartyTrace}, URL: { @@ -291,6 +301,11 @@ describe('Third party facades audit', () => { }); it('handles real trace', async () => { + // TODO(15841): traces needs updating. + if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { + return; + } + const artifacts = { devtoolsLogs: {defaultPass: videoEmbedsDevtolsLog}, traces: {defaultPass: videoEmbedsTrace}, @@ -439,6 +454,11 @@ Array [ }); it('handles real trace that blocks the main thread', async () => { + // TODO(15841): traces needs updating. + if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { + return; + } + const artifacts = { devtoolsLogs: {defaultPass: blockingWidgetDevtoolsLog}, traces: {defaultPass: blockingWidgetTrace}, From 67f55a62a0d2b8dc07a7e7e346a8f964679bd41f Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Tue, 4 Jun 2024 09:31:55 -0700 Subject: [PATCH 11/12] offscreen image tests are working --- core/test/audits/byte-efficiency/offscreen-images-test.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/test/audits/byte-efficiency/offscreen-images-test.js b/core/test/audits/byte-efficiency/offscreen-images-test.js index 6b3620cf4397..11f6617b7727 100644 --- a/core/test/audits/byte-efficiency/offscreen-images-test.js +++ b/core/test/audits/byte-efficiency/offscreen-images-test.js @@ -60,11 +60,6 @@ function generateImage({ } describe('OffscreenImages audit', () => { - // TODO(15841): investigate test failures - if (process.env.INTERNAL_LANTERN_USE_TRACE !== undefined) { - return; - } - let context; const DEFAULT_DIMENSIONS = {innerWidth: 1920, innerHeight: 1080}; From 38c36f408a03a9d53c8490c299822fa1d0823e4d Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Tue, 4 Jun 2024 10:37:28 -0700 Subject: [PATCH 12/12] core(lantern): rename NetworkRequest record to rawRequest --- core/audits/uses-rel-preload.js | 18 +-- core/computed/critical-request-chains.js | 6 +- core/lib/lantern-trace-saver.js | 2 +- core/lib/lantern/network-node.js | 6 +- core/lib/lantern/page-dependency-graph.js | 88 +++++++-------- core/lib/lantern/simulator/connection-pool.js | 44 ++++---- core/lib/lantern/simulator/dns-cache.js | 2 +- .../lib/lantern/simulator/network-analyzer.js | 106 +++++++++--------- core/lib/lantern/simulator/simulator.js | 32 +++--- core/lib/lantern/types/lantern.d.ts | 11 +- core/lib/network-request.js | 2 +- .../lib/lantern/page-dependency-graph-test.js | 78 ++++++------- .../lantern/simulator/connection-pool-test.js | 78 ++++++------- .../lib/lantern/simulator/dns-cache-test.js | 2 +- .../simulator/network-analyzer-test.js | 62 +++++----- .../lib/lantern/simulator/simulator-test.js | 12 +- 16 files changed, 275 insertions(+), 274 deletions(-) diff --git a/core/audits/uses-rel-preload.js b/core/audits/uses-rel-preload.js index 4bb79844e7bf..c74dd1b20269 100644 --- a/core/audits/uses-rel-preload.js +++ b/core/audits/uses-rel-preload.js @@ -61,8 +61,8 @@ class UsesRelPreloadAudit extends Audit { if (node.type !== 'network') return; // Don't include the node itself or any CPU nodes in the initiatorPath const path = traversalPath.slice(1).filter(initiator => initiator.type === 'network'); - if (!UsesRelPreloadAudit.shouldPreloadRequest(node.record, mainResource, path)) return; - urls.add(node.record.url); + if (!UsesRelPreloadAudit.shouldPreloadRequest(node.rawRequest, mainResource, path)) return; + urls.add(node.rawRequest.url); }); return urls; @@ -77,7 +77,7 @@ class UsesRelPreloadAudit extends Audit { static getURLsFailedToPreload(graph) { /** @type {Array} */ const requests = []; - graph.traverse(node => node.type === 'network' && requests.push(node.record)); + graph.traverse(node => node.type === 'network' && requests.push(node.rawRequest)); const preloadRequests = requests.filter(req => req.isLinkPreload); const preloadURLsByFrame = new Map(); @@ -157,7 +157,7 @@ class UsesRelPreloadAudit extends Audit { if (node.isMainDocument()) { mainDocumentNode = node; - } else if (node.record && urls.has(node.record.url)) { + } else if (node.rawRequest && urls.has(node.rawRequest.url)) { nodesToPreload.push(node); } }); @@ -176,20 +176,20 @@ class UsesRelPreloadAudit extends Audit { // Once we've modified the dependencies, simulate the new graph. const simulationAfterChanges = simulator.simulate(modifiedGraph); - const originalNodesByRecord = Array.from(simulationBeforeChanges.nodeTimings.keys()) - // @ts-expect-error we don't care if all nodes without a record collect on `undefined` - .reduce((map, node) => map.set(node.record, node), new Map()); + const originalNodesByRequest = Array.from(simulationBeforeChanges.nodeTimings.keys()) + // @ts-expect-error we don't care if all nodes without a request collect on `undefined` + .reduce((map, node) => map.set(node.request, node), new Map()); const results = []; for (const node of nodesToPreload) { - const originalNode = originalNodesByRecord.get(node.record); + const originalNode = originalNodesByRequest.get(node.request); const timingAfter = simulationAfterChanges.nodeTimings.get(node); const timingBefore = simulationBeforeChanges.nodeTimings.get(originalNode); if (!timingBefore || !timingAfter) throw new Error('Missing preload node'); const wastedMs = Math.round(timingBefore.endTime - timingAfter.endTime); if (wastedMs < THRESHOLD_IN_MS) continue; - results.push({url: node.record.url, wastedMs}); + results.push({url: node.rawRequest.url, wastedMs}); } if (!results.length) { diff --git a/core/computed/critical-request-chains.js b/core/computed/critical-request-chains.js index 05635f16a9e7..9fe8b7755a09 100644 --- a/core/computed/critical-request-chains.js +++ b/core/computed/critical-request-chains.js @@ -105,19 +105,19 @@ class CriticalRequestChains { graph.traverse((node, traversalPath) => { seenNodes.add(node); if (node.type !== 'network') return; - if (!CriticalRequestChains.isCritical(node.record, mainResource)) return; + if (!CriticalRequestChains.isCritical(node.rawRequest, mainResource)) return; const networkPath = traversalPath .filter(/** @return {n is LH.Gatherer.Simulation.GraphNetworkNode} */ n => n.type === 'network') .reverse() - .map(node => node.record); + .map(node => node.rawRequest); // Ignore if some ancestor is not a critical request. if (networkPath.some(r => !CriticalRequestChains.isCritical(r, mainResource))) return; // Ignore non-network things (like data urls). - if (NetworkRequest.isNonNetworkRequest(node.record)) return; + if (NetworkRequest.isNonNetworkRequest(node.rawRequest)) return; addChain(networkPath); }, getNextNodes); diff --git a/core/lib/lantern-trace-saver.js b/core/lib/lantern-trace-saver.js index ec3a98952290..82cab33e4b02 100644 --- a/core/lib/lantern-trace-saver.js +++ b/core/lib/lantern-trace-saver.js @@ -33,7 +33,7 @@ function convertNodeTimingsToTrace(nodeTimings) { traceEvents.push(...createFakeTaskEvents(node, timing)); } else { /** @type {LH.Artifacts.NetworkRequest} */ - const record = node.record; + const record = node.rawRequest; // Ignore data URIs as they don't really add much value if (/^data/.test(record.url)) continue; traceEvents.push(...createFakeNetworkEvents(requestId, record, timing)); diff --git a/core/lib/lantern/network-node.js b/core/lib/lantern/network-node.js index adb7760a6edc..a95bb73f98ea 100644 --- a/core/lib/lantern/network-node.js +++ b/core/lib/lantern/network-node.js @@ -68,8 +68,8 @@ class NetworkNode extends BaseNode { /** * @return {Readonly} */ - get record() { - return /** @type {Required} */ (this._request.record); + get rawRequest() { + return /** @type {Required} */ (this._request.rawRequest); } /** @@ -104,7 +104,7 @@ class NetworkNode extends BaseNode { } /** - * Returns whether this network record can be downloaded without a TCP connection. + * Returns whether this network request can be downloaded without a TCP connection. * During simulation we treat data coming in over a network connection separately from on-device data. * @return {boolean} */ diff --git a/core/lib/lantern/page-dependency-graph.js b/core/lib/lantern/page-dependency-graph.js index a7de6562bb95..b0311d4b08c1 100644 --- a/core/lib/lantern/page-dependency-graph.js +++ b/core/lib/lantern/page-dependency-graph.js @@ -32,19 +32,19 @@ const IGNORED_MIME_TYPES_REGEX = /^video/; class PageDependencyGraph { /** - * @param {Lantern.NetworkRequest} record + * @param {Lantern.NetworkRequest} request * @return {Array} */ - static getNetworkInitiators(record) { - if (!record.initiator) return []; - if (record.initiator.url) return [record.initiator.url]; - if (record.initiator.type === 'script') { + static getNetworkInitiators(request) { + if (!request.initiator) return []; + if (request.initiator.url) return [request.initiator.url]; + if (request.initiator.type === 'script') { // Script initiators have the stack of callFrames from all functions that led to this request. // If async stacks are enabled, then the stack will also have the parent functions that asynchronously // led to this request chained in the `parent` property. /** @type {Set} */ const scriptURLs = new Set(); - let stack = record.initiator.stack; + let stack = request.initiator.stack; while (stack) { const callFrames = stack.callFrames || []; for (const frame of callFrames) { @@ -61,10 +61,10 @@ class PageDependencyGraph { } /** - * @param {Array} networkRecords + * @param {Array} networkRequests * @return {NetworkNodeOutput} */ - static getNetworkNodeOutput(networkRecords) { + static getNetworkNodeOutput(networkRequests) { /** @type {Array} */ const nodes = []; /** @type {Map} */ @@ -74,35 +74,35 @@ class PageDependencyGraph { /** @type {Map} */ const frameIdToNodeMap = new Map(); - networkRecords.forEach(record => { - if (IGNORED_MIME_TYPES_REGEX.test(record.mimeType)) return; - if (record.fromWorker) return; + networkRequests.forEach(request => { + if (IGNORED_MIME_TYPES_REGEX.test(request.mimeType)) return; + if (request.fromWorker) return; - // Network record requestIds can be duplicated for an unknown reason - // Suffix all subsequent records with `:duplicate` until it's unique + // Network requestIds can be duplicated for an unknown reason + // Suffix all subsequent requests with `:duplicate` until it's unique // NOTE: This should never happen with modern NetworkRequest library, but old fixtures // might still have this issue. - while (idToNodeMap.has(record.requestId)) { - record.requestId += ':duplicate'; + while (idToNodeMap.has(request.requestId)) { + request.requestId += ':duplicate'; } - const node = new NetworkNode(record); + const node = new NetworkNode(request); nodes.push(node); - const urlList = urlToNodeMap.get(record.url) || []; + const urlList = urlToNodeMap.get(request.url) || []; urlList.push(node); - idToNodeMap.set(record.requestId, node); - urlToNodeMap.set(record.url, urlList); + idToNodeMap.set(request.requestId, node); + urlToNodeMap.set(request.url, urlList); // If the request was for the root document of an iframe, save an entry in our // map so we can link up the task `args.data.frame` dependencies later in graph creation. - if (record.frameId && - record.resourceType === NetworkRequestTypes.Document && - record.documentURL === record.url) { + if (request.frameId && + request.resourceType === NetworkRequestTypes.Document && + request.documentURL === request.url) { // If there's ever any ambiguity, permanently set the value to `false` to avoid loops in the graph. - const value = frameIdToNodeMap.has(record.frameId) ? null : node; - frameIdToNodeMap.set(record.frameId, value); + const value = frameIdToNodeMap.has(request.frameId) ? null : node; + frameIdToNodeMap.set(request.frameId, value); } }); @@ -427,7 +427,7 @@ class PageDependencyGraph { } for (const r of lanternRequests) { - delete r.record; + delete r.rawRequest; if (r.initiatorRequest) { // @ts-expect-error r.initiatorRequest = {id: r.initiatorRequest.requestId}; @@ -508,25 +508,25 @@ class PageDependencyGraph { /** * @param {LH.TraceEvent[]} mainThreadEvents - * @param {Array} networkRecords + * @param {Lantern.NetworkRequest[]} networkRequests * @param {URLArtifact} URL * @return {Node} */ - static createGraph(mainThreadEvents, networkRecords, URL) { + static createGraph(mainThreadEvents, networkRequests, URL) { // This is for debugging trace/devtoolslog network records. - // const debug = PageDependencyGraph._debugNormalizeRequests(networkRecords); - const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRecords); + // const debug = PageDependencyGraph._debugNormalizeRequests(networkRequests); + const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRequests); const cpuNodes = PageDependencyGraph.getCPUNodes(mainThreadEvents); const {requestedUrl, mainDocumentUrl} = URL; if (!requestedUrl) throw new Error('requestedUrl is required to get the root request'); if (!mainDocumentUrl) throw new Error('mainDocumentUrl is required to get the main resource'); - const rootRequest = NetworkAnalyzer.findResourceForUrl(networkRecords, requestedUrl); + const rootRequest = NetworkAnalyzer.findResourceForUrl(networkRequests, requestedUrl); if (!rootRequest) throw new Error('rootRequest not found'); const rootNode = networkNodeOutput.idToNodeMap.get(rootRequest.requestId); if (!rootNode) throw new Error('rootNode not found'); const mainDocumentRequest = - NetworkAnalyzer.findLastDocumentForUrl(networkRecords, mainDocumentUrl); + NetworkAnalyzer.findLastDocumentForUrl(networkRequests, mainDocumentUrl); if (!mainDocumentRequest) throw new Error('mainDocumentRequest not found'); const mainDocumentNode = networkNodeOutput.idToNodeMap.get(mainDocumentRequest.requestId); if (!mainDocumentNode) throw new Error('mainDocumentNode not found'); @@ -543,20 +543,20 @@ class PageDependencyGraph { } /** - * @param {Lantern.NetworkRequest} record The record to find the initiator of - * @param {Map} recordsByURL + * @param {Lantern.NetworkRequest} request The request to find the initiator of + * @param {Map} requestsByURL * @return {Lantern.NetworkRequest|null} */ - static chooseInitiatorRequest(record, recordsByURL) { - if (record.redirectSource) { - return record.redirectSource; + static chooseInitiatorRequest(request, requestsByURL) { + if (request.redirectSource) { + return request.redirectSource; } - const initiatorURL = PageDependencyGraph.getNetworkInitiators(record)[0]; - let candidates = recordsByURL.get(initiatorURL) || []; + const initiatorURL = PageDependencyGraph.getNetworkInitiators(request)[0]; + let candidates = requestsByURL.get(initiatorURL) || []; // The (valid) initiator must come before the initiated request. candidates = candidates.filter(c => { - return c.responseHeadersEndTime <= record.rendererStartTime && + return c.responseHeadersEndTime <= request.rendererStartTime && c.finished && !c.failed; }); if (candidates.length > 1) { @@ -570,12 +570,12 @@ class PageDependencyGraph { } if (candidates.length > 1) { // Disambiguate based on frame. It's likely that the initiator comes from the same frame. - const sameFrameCandidates = candidates.filter(cand => cand.frameId === record.frameId); + const sameFrameCandidates = candidates.filter(cand => cand.frameId === request.frameId); if (sameFrameCandidates.length) { candidates = sameFrameCandidates; } } - if (candidates.length > 1 && record.initiator.type === 'parser') { + if (candidates.length > 1 && request.initiator.type === 'parser') { // Filter to just Documents when initiator type is parser. const documentCandidates = candidates.filter(cand => cand.resourceType === RESOURCE_TYPES.Document); @@ -728,6 +728,7 @@ class PageDependencyGraph { } return { + rawRequest: request, requestId: request.args.data.requestId, connectionId: request.args.data.connectionId, connectionReused: request.args.data.connectionReused, @@ -754,7 +755,6 @@ class PageDependencyGraph { priority: request.args.data.priority, frameId: request.args.data.frame, fromWorker, - record: request, // Set later. redirects: undefined, redirectSource: undefined, @@ -805,9 +805,9 @@ class PageDependencyGraph { // TraceEngine consolidates all redirects into a single request object, but lantern needs // an entry for each redirected request. for (const request of [...lanternRequests]) { - if (!request.record) continue; + if (!request.rawRequest) continue; - const redirects = request.record.args.data.redirects; + const redirects = request.rawRequest.args.data.redirects; if (!redirects.length) continue; const requestChain = []; diff --git a/core/lib/lantern/simulator/connection-pool.js b/core/lib/lantern/simulator/connection-pool.js index b582863ddecb..fd42af55e0c3 100644 --- a/core/lib/lantern/simulator/connection-pool.js +++ b/core/lib/lantern/simulator/connection-pool.js @@ -27,7 +27,7 @@ export class ConnectionPool { /** @type {Map} */ this._connectionsByOrigin = new Map(); /** @type {Map} */ - this._connectionsByRecord = new Map(); + this._connectionsByRequest = new Map(); this._connectionsInUse = new Set(); this._connectionReusedByRequestId = NetworkAnalyzer.estimateIfConnectionWasReused(records, { forceCoarseEstimates: true, @@ -49,16 +49,16 @@ export class ConnectionPool { const serverResponseTimeByOrigin = this._options.serverResponseTimeByOrigin; const recordsByOrigin = NetworkAnalyzer.groupByOrigin(this._records); - for (const [origin, records] of recordsByOrigin.entries()) { + for (const [origin, requests] of recordsByOrigin.entries()) { const connections = []; const additionalRtt = additionalRttByOrigin.get(origin) || 0; const responseTime = serverResponseTimeByOrigin.get(origin) || DEFAULT_SERVER_RESPONSE_TIME; - for (const record of records) { - if (connectionReused.get(record.requestId)) continue; + for (const request of requests) { + if (connectionReused.get(request.requestId)) continue; - const isTLS = TLS_SCHEMES.includes(record.parsedURL.scheme); - const isH2 = record.protocol === 'h2'; + const isTLS = TLS_SCHEMES.includes(request.parsedURL.scheme); + const isH2 = request.protocol === 'h2'; const connection = new TcpConnection( this._options.rtt + additionalRtt, this._options.throughput, @@ -106,47 +106,47 @@ export class ConnectionPool { } /** - * This method finds an available connection to the origin specified by the network record or null + * This method finds an available connection to the origin specified by the network request or null * if no connection was available. If returned, connection will not be available for other network * records until release is called. * - * @param {Lantern.NetworkRequest} record + * @param {Lantern.NetworkRequest} request * @return {?TcpConnection} */ - acquire(record) { - if (this._connectionsByRecord.has(record)) throw new Error('Record already has a connection'); + acquire(request) { + if (this._connectionsByRequest.has(request)) throw new Error('Record already has a connection'); - const origin = record.parsedURL.securityOrigin; + const origin = request.parsedURL.securityOrigin; const connections = this._connectionsByOrigin.get(origin) || []; const connectionToUse = this._findAvailableConnectionWithLargestCongestionWindow(connections); if (!connectionToUse) return null; this._connectionsInUse.add(connectionToUse); - this._connectionsByRecord.set(record, connectionToUse); + this._connectionsByRequest.set(request, connectionToUse); return connectionToUse; } /** - * Return the connection currently being used to fetch a record. If no connection - * currently being used for this record, an error will be thrown. + * Return the connection currently being used to fetch a request. If no connection + * currently being used for this request, an error will be thrown. * - * @param {Lantern.NetworkRequest} record + * @param {Lantern.NetworkRequest} request * @return {TcpConnection} */ - acquireActiveConnectionFromRecord(record) { - const activeConnection = this._connectionsByRecord.get(record); - if (!activeConnection) throw new Error('Could not find an active connection for record'); + acquireActiveConnectionFromRequest(request) { + const activeConnection = this._connectionsByRequest.get(request); + if (!activeConnection) throw new Error('Could not find an active connection for request'); return activeConnection; } /** - * @param {Lantern.NetworkRequest} record + * @param {Lantern.NetworkRequest} request */ - release(record) { - const connection = this._connectionsByRecord.get(record); - this._connectionsByRecord.delete(record); + release(request) { + const connection = this._connectionsByRequest.get(request); + this._connectionsByRequest.delete(request); this._connectionsInUse.delete(connection); } } diff --git a/core/lib/lantern/simulator/dns-cache.js b/core/lib/lantern/simulator/dns-cache.js index 97b451c34536..2598d7038514 100644 --- a/core/lib/lantern/simulator/dns-cache.js +++ b/core/lib/lantern/simulator/dns-cache.js @@ -58,7 +58,7 @@ class DNSCache { } /** - * Forcefully sets the DNS resolution time for a record. + * Forcefully sets the DNS resolution time for a request. * Useful for testing and alternate execution simulations. * * @param {string} domain diff --git a/core/lib/lantern/simulator/network-analyzer.js b/core/lib/lantern/simulator/network-analyzer.js index dcbf114f4515..d790c3a4cd27 100644 --- a/core/lib/lantern/simulator/network-analyzer.js +++ b/core/lib/lantern/simulator/network-analyzer.js @@ -88,30 +88,30 @@ class NetworkAnalyzer { return summaryByKey; } - /** @typedef {{record: Lantern.NetworkRequest, timing: LH.Crdp.Network.ResourceTiming, connectionReused?: boolean}} RequestInfo */ + /** @typedef {{request: Lantern.NetworkRequest, timing: LH.Crdp.Network.ResourceTiming, connectionReused?: boolean}} RequestInfo */ /** - * @param {Lantern.NetworkRequest[]} records + * @param {Lantern.NetworkRequest[]} requests * @param {(e: RequestInfo) => number | number[] | undefined} iteratee * @return {Map} */ - static _estimateValueByOrigin(records, iteratee) { - const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(records); - const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records); + static _estimateValueByOrigin(requests, iteratee) { + const connectionWasReused = NetworkAnalyzer.estimateIfConnectionWasReused(requests); + const groupedByOrigin = NetworkAnalyzer.groupByOrigin(requests); const estimates = new Map(); - for (const [origin, originRecords] of groupedByOrigin.entries()) { + for (const [origin, originRequests] of groupedByOrigin.entries()) { /** @type {number[]} */ let originEstimates = []; - for (const record of originRecords) { - const timing = record.timing; + for (const request of originRequests) { + const timing = request.timing; if (!timing) continue; const value = iteratee({ - record, + request, timing, - connectionReused: connectionWasReused.get(record.requestId), + connectionReused: connectionWasReused.get(request.requestId), }); if (typeof value !== 'undefined') { originEstimates = originEstimates.concat(value); @@ -137,11 +137,11 @@ class NetworkAnalyzer { * @return {number[]|number|undefined} */ static _estimateRTTViaConnectionTiming(info) { - const {timing, connectionReused, record} = info; + const {timing, connectionReused, request} = info; if (connectionReused) return; const {connectStart, sslStart, sslEnd, connectEnd} = timing; - if (connectEnd >= 0 && connectStart >= 0 && record.protocol.startsWith('h3')) { + if (connectEnd >= 0 && connectStart >= 0 && request.protocol.startsWith('h3')) { // These values are equal to sslStart and sslEnd for h3. return connectEnd - connectStart; } else if (sslStart >= 0 && sslEnd >= 0 && sslStart !== connectStart) { @@ -161,17 +161,17 @@ class NetworkAnalyzer { * @return {number|undefined} */ static _estimateRTTViaDownloadTiming(info) { - const {timing, connectionReused, record} = info; + const {timing, connectionReused, request} = info; if (connectionReused) return; // Only look at downloads that went past the initial congestion window - if (record.transferSize <= INITIAL_CWD) return; + if (request.transferSize <= INITIAL_CWD) return; if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return; // Compute the amount of time downloading everything after the first congestion window took - const totalTime = record.networkEndTime - record.networkRequestTime; + const totalTime = request.networkEndTime - request.networkRequestTime; const downloadTimeAfterFirstByte = totalTime - timing.receiveHeadersEnd; - const numberOfRoundTrips = Math.log2(record.transferSize / INITIAL_CWD); + const numberOfRoundTrips = Math.log2(request.transferSize / INITIAL_CWD); // Ignore requests that required a high number of round trips since bandwidth starts to play // a larger role than latency @@ -190,7 +190,7 @@ class NetworkAnalyzer { * @return {number|undefined} */ static _estimateRTTViaSendStartTiming(info) { - const {timing, connectionReused, record} = info; + const {timing, connectionReused, request} = info; if (connectionReused) return; if (!Number.isFinite(timing.sendStart) || timing.sendStart < 0) return; @@ -198,8 +198,8 @@ class NetworkAnalyzer { // Assume everything before sendStart was just DNS + (SSL)? + TCP handshake // 1 RT for DNS, 1 RT (maybe) for SSL, 1 RT for TCP let roundTrips = 1; - if (!record.protocol.startsWith('h3')) roundTrips += 1; // TCP - if (record.parsedURL.scheme === 'https') roundTrips += 1; + if (!request.protocol.startsWith('h3')) roundTrips += 1; // TCP + if (request.parsedURL.scheme === 'https') roundTrips += 1; return timing.sendStart / roundTrips; } @@ -213,12 +213,12 @@ class NetworkAnalyzer { * @return {number|undefined} */ static _estimateRTTViaHeadersEndTiming(info) { - const {timing, connectionReused, record} = info; + const {timing, connectionReused, request} = info; if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return; - if (!record.resourceType) return; + if (!request.resourceType) return; const serverResponseTimePercentage = - SERVER_RESPONSE_PERCENTAGE_OF_TTFB[record.resourceType] || + SERVER_RESPONSE_PERCENTAGE_OF_TTFB[request.resourceType] || DEFAULT_SERVER_RESPONSE_PERCENTAGE; const estimatedServerResponseTime = timing.receiveHeadersEnd * serverResponseTimePercentage; @@ -230,8 +230,8 @@ class NetworkAnalyzer { // TTFB = DNS + (SSL)? + TCP handshake + 1 RT for request + server response time if (!connectionReused) { roundTrips += 1; // DNS - if (!record.protocol.startsWith('h3')) roundTrips += 1; // TCP - if (record.parsedURL.scheme === 'https') roundTrips += 1; // SSL + if (!request.protocol.startsWith('h3')) roundTrips += 1; // TCP + if (request.parsedURL.scheme === 'https') roundTrips += 1; // SSL } // subtract out our estimated server response time @@ -246,28 +246,28 @@ class NetworkAnalyzer { * @return {Map} */ static _estimateResponseTimeByOrigin(records, rttByOrigin) { - return NetworkAnalyzer._estimateValueByOrigin(records, ({record, timing}) => { - if (record.serverResponseTime !== undefined) return record.serverResponseTime; + return NetworkAnalyzer._estimateValueByOrigin(records, ({request, timing}) => { + if (request.serverResponseTime !== undefined) return request.serverResponseTime; if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return; if (!Number.isFinite(timing.sendEnd) || timing.sendEnd < 0) return; const ttfb = timing.receiveHeadersEnd - timing.sendEnd; - const origin = record.parsedURL.securityOrigin; + const origin = request.parsedURL.securityOrigin; const rtt = rttByOrigin.get(origin) || rttByOrigin.get(NetworkAnalyzer.SUMMARY) || 0; return Math.max(ttfb - rtt, 0); }); } /** - * @param {Lantern.NetworkRequest[]} records + * @param {Lantern.NetworkRequest[]} requests * @return {boolean} */ - static canTrustConnectionInformation(records) { + static canTrustConnectionInformation(requests) { const connectionIdWasStarted = new Map(); - for (const record of records) { - const started = connectionIdWasStarted.get(record.connectionId) || !record.connectionReused; - connectionIdWasStarted.set(record.connectionId, started); + for (const request of requests) { + const started = connectionIdWasStarted.get(request.connectionId) || !request.connectionReused; + connectionIdWasStarted.set(request.connectionId, started); } // We probably can't trust the network information if all the connection IDs were the same @@ -289,10 +289,10 @@ class NetworkAnalyzer { // Check if we can trust the connection information coming from the protocol if (!forceCoarseEstimates && NetworkAnalyzer.canTrustConnectionInformation(records)) { - return new Map(records.map(record => [record.requestId, !!record.connectionReused])); + return new Map(records.map(request => [request.requestId, !!request.connectionReused])); } - // Otherwise we're on our own, a record may not have needed a fresh connection if... + // Otherwise we're on our own, a request may not have needed a fresh connection if... // - It was not the first request to the domain // - It was H2 // - It was after the first request to the domain ended @@ -300,13 +300,13 @@ class NetworkAnalyzer { const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records); for (const [_, originRecords] of groupedByOrigin.entries()) { const earliestReusePossible = originRecords - .map(record => record.networkEndTime) + .map(request => request.networkEndTime) .reduce((a, b) => Math.min(a, b), Infinity); - for (const record of originRecords) { + for (const request of originRecords) { connectionWasReused.set( - record.requestId, - record.networkRequestTime >= earliestReusePossible || record.protocol === 'h2' + request.requestId, + request.networkRequestTime >= earliestReusePossible || request.protocol === 'h2' ); } @@ -343,7 +343,7 @@ class NetworkAnalyzer { const groupedByOrigin = NetworkAnalyzer.groupByOrigin(records); const estimatesByOrigin = new Map(); - for (const [origin, originRecords] of groupedByOrigin.entries()) { + for (const [origin, originRequests] of groupedByOrigin.entries()) { /** @type {number[]} */ const originEstimates = []; @@ -352,14 +352,14 @@ class NetworkAnalyzer { */ // eslint-disable-next-line no-inner-declarations function collectEstimates(estimator, multiplier = 1) { - for (const record of originRecords) { - const timing = record.timing; + for (const request of originRequests) { + const timing = request.timing; if (!timing) continue; const estimates = estimator({ - record, + request, timing, - connectionReused: connectionWasReused.get(record.requestId), + connectionReused: connectionWasReused.get(request.requestId), }); if (estimates === undefined) continue; @@ -427,9 +427,9 @@ class NetworkAnalyzer { /** - * Computes the average throughput for the given records in bits/second. + * Computes the average throughput for the given requests in bits/second. * Excludes data URI, failed or otherwise incomplete, and cached requests. - * Returns Infinity if there were no analyzable network records. + * Returns Infinity if there were no analyzable network requests. * * @param {Lantern.NetworkRequest[]} records * @return {number} @@ -438,21 +438,21 @@ class NetworkAnalyzer { let totalBytes = 0; // We will measure throughput by summing the total bytes downloaded by the total time spent - // downloading those bytes. We slice up all the network records into start/end boundaries, so + // downloading those bytes. We slice up all the network requests into start/end boundaries, so // it's easier to deal with the gaps in downloading. - const timeBoundaries = records.reduce((boundaries, record) => { - const scheme = record.parsedURL?.scheme; + const timeBoundaries = records.reduce((boundaries, request) => { + const scheme = request.parsedURL?.scheme; // Requests whose bodies didn't come over the network or didn't completely finish will mess // with the computation, just skip over them. - if (scheme === 'data' || record.failed || !record.finished || - record.statusCode > 300 || !record.transferSize) { + if (scheme === 'data' || request.failed || !request.finished || + request.statusCode > 300 || !request.transferSize) { return boundaries; } // If we've made it this far, all the times we need should be valid (i.e. not undefined/-1). - totalBytes += record.transferSize; - boundaries.push({time: record.responseHeadersEndTime / 1000, isStart: true}); - boundaries.push({time: record.networkEndTime / 1000, isStart: false}); + totalBytes += request.transferSize; + boundaries.push({time: request.responseHeadersEndTime / 1000, isStart: true}); + boundaries.push({time: request.networkEndTime / 1000, isStart: false}); return boundaries; }, /** @type {Array<{time: number, isStart: boolean}>} */([])).sort((a, b) => a.time - b.time); diff --git a/core/lib/lantern/simulator/simulator.js b/core/lib/lantern/simulator/simulator.js index 79a3b594a4ff..2e94b374d9ad 100644 --- a/core/lib/lantern/simulator/simulator.js +++ b/core/lib/lantern/simulator/simulator.js @@ -248,11 +248,11 @@ class Simulator { } /** - * @param {Lantern.NetworkRequest} record + * @param {Lantern.NetworkRequest} request * @return {?TcpConnection} */ - _acquireConnection(record) { - return this._connectionPool.acquire(record); + _acquireConnection(request) { + return this._connectionPool.acquire(request); } /** @@ -342,7 +342,7 @@ class Simulator { * @return {number} */ _estimateNetworkTimeRemaining(networkNode) { - const record = networkNode.request; + const request = networkNode.request; const timingData = this._nodeTimings.getNetworkStarted(networkNode); let timeElapsed = 0; @@ -350,23 +350,23 @@ class Simulator { // Rough access time for seeking to location on disk and reading sequentially. // 8ms per seek + 20ms/MB // @see http://norvig.com/21-days.html#answers - const sizeInMb = (record.resourceSize || 0) / 1024 / 1024; + const sizeInMb = (request.resourceSize || 0) / 1024 / 1024; timeElapsed = 8 + 20 * sizeInMb - timingData.timeElapsed; } else if (networkNode.isNonNetworkProtocol) { // Estimates for the overhead of a data URL in Chromium and the decoding time for base64-encoded data. // 2ms per request + 10ms/MB // @see traces on https://dopiaza.org/tools/datauri/examples/index.php - const sizeInMb = (record.resourceSize || 0) / 1024 / 1024; + const sizeInMb = (request.resourceSize || 0) / 1024 / 1024; timeElapsed = 2 + 10 * sizeInMb - timingData.timeElapsed; } else { - const connection = this._connectionPool.acquireActiveConnectionFromRecord(record); - const dnsResolutionTime = this._dns.getTimeUntilResolution(record, { + const connection = this._connectionPool.acquireActiveConnectionFromRequest(request); + const dnsResolutionTime = this._dns.getTimeUntilResolution(request, { requestedAt: timingData.startTime, shouldUpdateCache: true, }); const timeAlreadyElapsed = timingData.timeElapsed; const calculation = connection.simulateDownloadUntil( - record.transferSize - timingData.bytesDownloaded, + request.transferSize - timingData.bytesDownloaded, {timeAlreadyElapsed, dnsResolutionTime, maximumTimeToElapse: Infinity} ); @@ -410,14 +410,14 @@ class Simulator { if (node.type !== BaseNode.TYPES.NETWORK) throw new Error('Unsupported'); if (!('bytesDownloaded' in timingData)) throw new Error('Invalid timing data'); - const record = node.request; - const connection = this._connectionPool.acquireActiveConnectionFromRecord(record); - const dnsResolutionTime = this._dns.getTimeUntilResolution(record, { + const request = node.request; + const connection = this._connectionPool.acquireActiveConnectionFromRequest(request); + const dnsResolutionTime = this._dns.getTimeUntilResolution(request, { requestedAt: timingData.startTime, shouldUpdateCache: true, }); const calculation = connection.simulateDownloadUntil( - record.transferSize - timingData.bytesDownloaded, + request.transferSize - timingData.bytesDownloaded, { dnsResolutionTime, timeAlreadyElapsed: timingData.timeElapsed, @@ -430,7 +430,7 @@ class Simulator { if (isFinished) { connection.setWarmed(true); - this._connectionPool.release(record); + this._connectionPool.release(request); this._markNodeAsComplete(node, totalElapsedTime, calculation.connectionTiming); } else { timingData.timeElapsed += calculation.timeElapsed; @@ -480,7 +480,7 @@ class Simulator { * * Simulator/connection pool are allowed to deviate from what was * observed in the trace/devtoolsLog and start requests as soon as they are queued (i.e. do not - * wait around for a warm connection to be available if the original record was fetched on a warm + * wait around for a warm connection to be available if the original request was fetched on a warm * connection). * * @param {Node} graph @@ -580,7 +580,7 @@ class Simulator { } /** - * We attempt to start nodes by their observed start time using the record priority as a tie breaker. + * We attempt to start nodes by their observed start time using the request priority as a tie breaker. * When simulating, just because a low priority image started 5ms before a high priority image doesn't mean * it would have happened like that when the network was slower. * @param {Node} node diff --git a/core/lib/lantern/types/lantern.d.ts b/core/lib/lantern/types/lantern.d.ts index 875001b7d027..b52cc58c710b 100644 --- a/core/lib/lantern/types/lantern.d.ts +++ b/core/lib/lantern/types/lantern.d.ts @@ -38,12 +38,13 @@ type LightriderStatistics = { export class NetworkRequest { /** - * The canonical network record. - * Users of Lantern must create NetworkRequests matching this interface, - * but can store the source-of-truth for their network model in this `record` - * property. This is then accessible as a read-only property on NetworkNode. + * Implementation-specifc canoncial data structure that this Lantern NetworkRequest + * was derived from. + * Users of Lantern create a NetworkRequest matching this interface, + * but can store the source-of-truth for their network model in this property. + * This is then accessible as a read-only property on NetworkNode. */ - record?: T; + rawRequest?: T; requestId: string; connectionId: number; diff --git a/core/lib/network-request.js b/core/lib/network-request.js index c9bf74285804..77bf329a04fe 100644 --- a/core/lib/network-request.js +++ b/core/lib/network-request.js @@ -604,10 +604,10 @@ class NetworkRequest { record.fromWorker = record.sessionTargetType === 'worker'; return { + rawRequest: record, ...record, timing, serverResponseTime, - record, }; } diff --git a/core/test/lib/lantern/page-dependency-graph-test.js b/core/test/lib/lantern/page-dependency-graph-test.js index f89347c03c19..9be2461c49c0 100644 --- a/core/test/lib/lantern/page-dependency-graph-test.js +++ b/core/test/lib/lantern/page-dependency-graph-test.js @@ -65,23 +65,23 @@ describe('PageDependencyGraph computed artifact:', () => { const request1 = createRequest(1, 'https://example.com/'); const request2 = createRequest(2, 'https://example.com/page'); const request3 = createRequest(3, 'https://example.com/page'); - const networkRecords = [request1, request2, request3]; + const networkRequests = [request1, request2, request3]; it('should create network nodes', () => { - const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRecords); - for (let i = 0; i < networkRecords.length; i++) { + const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRequests); + for (let i = 0; i < networkRequests.length; i++) { const node = networkNodeOutput.nodes[i]; assert.ok(node, `did not create node at index ${i}`); assert.equal(node.id, i + 1); assert.equal(node.type, 'network'); - assert.equal(node.request, networkRecords[i]); + assert.equal(node.request, networkRequests[i]); } }); it('should ignore worker requests', () => { const workerRequest = createRequest(4, 'https://example.com/worker.js', 0, null, 'Script', true); const recordsWithWorker = [ - ...networkRecords, + ...networkRequests, workerRequest, ]; @@ -92,15 +92,15 @@ describe('PageDependencyGraph computed artifact:', () => { }); it('should index nodes by ID', () => { - const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRecords); + const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRequests); const indexedById = networkNodeOutput.idToNodeMap; - for (const record of networkRecords) { - assert.equal(indexedById.get(record.requestId).request, record); + for (const request of networkRequests) { + assert.equal(indexedById.get(request.requestId).request, request); } }); it('should index nodes by URL', () => { - const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRecords); + const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRequests); const nodes = networkNodeOutput.nodes; const indexedByUrl = networkNodeOutput.urlToNodeMap; assert.deepEqual(indexedByUrl.get('https://example.com/'), [nodes[0]]); @@ -198,11 +198,11 @@ describe('PageDependencyGraph computed artifact:', () => { const request2 = createRequest(2, 'https://example.com/page', 5); const request3 = createRequest(3, 'https://example.com/page2', 5); const request4 = createRequest(4, 'https://example.com/page3', 10, {url: 'https://example.com/page'}); - const networkRecords = [request1, request2, request3, request4]; + const networkRequests = [request1, request2, request3, request4]; addTaskEvents(0, 0, []); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -219,7 +219,7 @@ describe('PageDependencyGraph computed artifact:', () => { const request2 = createRequest(2, 'https://example.com/page', 50); const request3 = createRequest(3, 'https://example.com/page2', 50); const request4 = createRequest(4, 'https://example.com/page3', 300, null, NetworkRequestTypes.XHR); - const networkRecords = [request1, request2, request3, request4]; + const networkRequests = [request1, request2, request3, request4]; addTaskEvents(200, 200, [ {name: 'EvaluateScript', data: {url: 'https://example.com/page'}}, @@ -231,7 +231,7 @@ describe('PageDependencyGraph computed artifact:', () => { {name: 'XHRReadyStateChange', data: {readyState: 4, url: 'https://example.com/page3'}}, ]); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -253,11 +253,11 @@ describe('PageDependencyGraph computed artifact:', () => { const request2 = createRequest(2, 'https://example.com/page', 5); const request3 = createRequest(3, 'https://example.com/page', 5); // duplicate URL const request4 = createRequest(4, 'https://example.com/page3', 10, {url: 'https://example.com/page'}); - const networkRecords = [request1, request2, request3, request4]; + const networkRequests = [request1, request2, request3, request4]; addTaskEvents(0, 0, []); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -275,7 +275,7 @@ describe('PageDependencyGraph computed artifact:', () => { const request3 = createRequest(3, 'https://example.com/page2', 210); const request4 = createRequest(4, 'https://example.com/page3', 590); const request5 = createRequest(5, 'https://example.com/page4', 595, null, NetworkRequestTypes.XHR); - const networkRecords = [request1, request2, request3, request4, request5]; + const networkRequests = [request1, request2, request3, request4, request5]; addTaskEvents(200, 200, [ // CPU 1.2 should depend on Network 1 @@ -296,7 +296,7 @@ describe('PageDependencyGraph computed artifact:', () => { {name: 'ResourceSendRequest', data: {requestId: 5}}, ]); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -314,7 +314,7 @@ describe('PageDependencyGraph computed artifact:', () => { it('should not install timer dependency on itself', () => { const request1 = createRequest(1, 'https://example.com/', 0); - const networkRecords = [request1]; + const networkRequests = [request1]; addTaskEvents(200, 200, [ // CPU 1.2 should depend on Network 1 @@ -324,7 +324,7 @@ describe('PageDependencyGraph computed artifact:', () => { {name: 'TimerFire', data: {timerId: 'timer1'}}, ]); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -341,7 +341,7 @@ describe('PageDependencyGraph computed artifact:', () => { const request2 = createRequest(2, 'https://example.com/page', 200, null, NetworkRequestTypes.XHR); const request3 = createRequest(3, 'https://example.com/page2', 300, null, NetworkRequestTypes.Script); const request4 = createRequest(4, 'https://example.com/page3', 400, null, NetworkRequestTypes.XHR); - const networkRecords = [request0, request1, request2, request3, request4]; + const networkRequests = [request0, request1, request2, request3, request4]; URL = {requestedUrl: 'https://example.com/page0', mainDocumentUrl: 'https://example.com/page0'}; // Long task, should be kept in the output. @@ -358,7 +358,7 @@ describe('PageDependencyGraph computed artifact:', () => { {name: 'XHRReadyStateChange', data: {readyState: 4, url: 'https://example.com/page3'}}, ]); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -390,7 +390,7 @@ describe('PageDependencyGraph computed artifact:', () => { }; const request3 = createRequest(3, 'https://example.com/page2', 300, null, NetworkRequestTypes.XHR); const request4 = createRequest(4, 'https://example.com/page3', 400, null, NetworkRequestTypes.XHR); - const networkRecords = [request0, request1, request2, request3, request4]; + const networkRequests = [request0, request1, request2, request3, request4]; URL = {requestedUrl: 'https://example.com/page0', mainDocumentUrl: 'https://example.com/page0'}; // Short task, evaluates script (2) and sends two XHRs. @@ -404,7 +404,7 @@ describe('PageDependencyGraph computed artifact:', () => { {name: 'XHRReadyStateChange', data: {readyState: 4, url: 'https://example.com/page3'}}, ]); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -424,7 +424,7 @@ describe('PageDependencyGraph computed artifact:', () => { it('should not prune short, first tasks of critical events', () => { const request0 = createRequest(0, 'https://example.com/page0', 0); - const networkRecords = [request0]; + const networkRequests = [request0]; URL = {requestedUrl: 'https://example.com/page0', mainDocumentUrl: 'https://example.com/page0'}; const makeShortEvent = firstEventName => { @@ -444,7 +444,7 @@ describe('PageDependencyGraph computed artifact:', () => { makeShortEvent(eventName); } - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const cpuNodes = []; graph.traverse(node => node.type === 'cpu' && cpuNodes.push(node)); @@ -476,12 +476,12 @@ describe('PageDependencyGraph computed artifact:', () => { // Add in another unrelated + early request to make sure we pick the correct chain const request3 = createRequest(3, 'https://example.com/page2', 0, null, NetworkRequestTypes.Other); request2.redirects = [request1]; - const networkRecords = [request1, request2, request3]; + const networkRequests = [request1, request2, request3]; URL = {requestedUrl: 'https://example.com/', mainDocumentUrl: 'https://example.com/page'}; addTaskEvents(0, 0, []); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -505,11 +505,11 @@ describe('PageDependencyGraph computed artifact:', () => { // Also set the initiatorRequest that Lighthouse's network-recorder.js creates. // This should be ignored and only used as a fallback. request4.initiatorRequest = request1; - const networkRecords = [request1, request2, request3, request4]; + const networkRequests = [request1, request2, request3, request4]; addTaskEvents(0, 0, []); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -530,11 +530,11 @@ describe('PageDependencyGraph computed artifact:', () => { type: 'script', stack: {callFrames: [{url: 'https://example.com/page'}], parent: {parent: {callFrames: [{url: 'https://example.com/page2'}]}}}, }; - const networkRecords = [request1, request2, request3, request4]; + const networkRequests = [request1, request2, request3, request4]; addTaskEvents(0, 0, []); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -558,12 +558,12 @@ describe('PageDependencyGraph computed artifact:', () => { }; // Set the initiatorRequest that it should fallback to. request3.initiatorRequest = request2Fetch; - const networkRecords = [request1, request2Prefetch, request2Fetch, request3]; + const networkRequests = [request1, request2Prefetch, request2Fetch, request3]; URL = {requestedUrl: 'https://a.com/1', mainDocumentUrl: 'https://a.com/1'}; addTaskEvents(0, 0, []); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); @@ -582,12 +582,12 @@ describe('PageDependencyGraph computed artifact:', () => { // jsRequest2 initiated by jsRequest1 const jsRequest1 = createRequest(2, 'https://a.com/js1', 1, {url: 'https://a.com/js2'}); const jsRequest2 = createRequest(3, 'https://a.com/js2', 1, {url: 'https://a.com/js1'}); - const networkRecords = [rootRequest, jsRequest1, jsRequest2]; + const networkRequests = [rootRequest, jsRequest1, jsRequest2]; URL = {requestedUrl: 'https://a.com', mainDocumentUrl: 'https://a.com'}; addTaskEvents(0, 0, []); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); nodes.sort((a, b) => a.id - b.id); @@ -612,12 +612,12 @@ describe('PageDependencyGraph computed artifact:', () => { const jsRequest2 = createRequest(3, 'https://a.com/js2', 1); jsRequest1.initiatorRequest = jsRequest2; jsRequest2.initiatorRequest = jsRequest1; - const networkRecords = [rootRequest, jsRequest1, jsRequest2]; + const networkRequests = [rootRequest, jsRequest1, jsRequest2]; URL = {requestedUrl: 'https://a.com', mainDocumentUrl: 'https://a.com'}; addTaskEvents(0, 0, []); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); nodes.sort((a, b) => a.id - b.id); @@ -632,7 +632,7 @@ describe('PageDependencyGraph computed artifact:', () => { it('should find root if it is not the first node', () => { const request1 = createRequest(1, 'https://example.com/', 0, null, NetworkRequestTypes.Other); const request2 = createRequest(2, 'https://example.com/page', 5, null, NetworkRequestTypes.Document); - const networkRecords = [request1, request2]; + const networkRequests = [request1, request2]; URL = {requestedUrl: 'https://example.com/page', mainDocumentUrl: 'https://example.com/page'}; // Evaluated before root request. @@ -640,7 +640,7 @@ describe('PageDependencyGraph computed artifact:', () => { {name: 'EvaluateScript'}, ]); - const graph = PageDependencyGraph.createGraph(traceEvents, networkRecords, URL); + const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL); const nodes = []; graph.traverse(node => nodes.push(node)); diff --git a/core/test/lib/lantern/simulator/connection-pool-test.js b/core/test/lib/lantern/simulator/connection-pool-test.js index 27f4de888542..84d2e2136dc3 100644 --- a/core/test/lib/lantern/simulator/connection-pool-test.js +++ b/core/test/lib/lantern/simulator/connection-pool-test.js @@ -14,7 +14,7 @@ describe('DependencyGraph/Simulator/ConnectionPool', () => { const throughput = 10000 * 1024; let requestId; - function record(data = {}) { + function request(data = {}) { const url = data.url || 'http://example.com'; const origin = new URL(url).origin; const scheme = url.split(':')[0]; @@ -45,7 +45,7 @@ describe('DependencyGraph/Simulator/ConnectionPool', () => { describe('#constructor', () => { it('should create the pool', () => { - const pool = new ConnectionPool([record()], simulationOptions({rtt, throughput})); + const pool = new ConnectionPool([request()], simulationOptions({rtt, throughput})); // Make sure 6 connections are created for each origin assert.equal(pool._connectionsByOrigin.get('http://example.com').length, 6); // Make sure it populates connectionWasReused @@ -58,14 +58,14 @@ describe('DependencyGraph/Simulator/ConnectionPool', () => { }); it('should set TLS properly', () => { - const recordA = record({url: 'https://example.com'}); + const recordA = request({url: 'https://example.com'}); const pool = new ConnectionPool([recordA], simulationOptions({rtt, throughput})); const connection = pool._connectionsByOrigin.get('https://example.com')[0]; assert.ok(connection._ssl, 'should have set connection TLS'); }); it('should set H2 properly', () => { - const recordA = record({protocol: 'h2'}); + const recordA = request({protocol: 'h2'}); const pool = new ConnectionPool([recordA], simulationOptions({rtt, throughput})); const connection = pool._connectionsByOrigin.get('http://example.com')[0]; assert.ok(connection.isH2(), 'should have set HTTP/2'); @@ -74,7 +74,7 @@ describe('DependencyGraph/Simulator/ConnectionPool', () => { it('should set origin-specific RTT properly', () => { const additionalRttByOrigin = new Map([['http://example.com', 63]]); - const pool = new ConnectionPool([record()], + const pool = new ConnectionPool([request()], simulationOptions({rtt, throughput, additionalRttByOrigin})); const connection = pool._connectionsByOrigin.get('http://example.com')[0]; assert.ok(connection._rtt, rtt + 63); @@ -82,7 +82,7 @@ describe('DependencyGraph/Simulator/ConnectionPool', () => { it('should set origin-specific server latency properly', () => { const serverResponseTimeByOrigin = new Map([['http://example.com', 63]]); - const pool = new ConnectionPool([record()], + const pool = new ConnectionPool([request()], simulationOptions({rtt, throughput, serverResponseTimeByOrigin})); const connection = pool._connectionsByOrigin.get('http://example.com')[0]; assert.ok(connection._serverLatency, 63); @@ -90,40 +90,40 @@ describe('DependencyGraph/Simulator/ConnectionPool', () => { }); describe('.acquire', () => { - it('should remember the connection associated with each record', () => { - const recordA = record(); - const recordB = record(); - const pool = new ConnectionPool([recordA, recordB], simulationOptions({rtt, throughput})); + it('should remember the connection associated with each request', () => { + const requestA = request(); + const requestB = request(); + const pool = new ConnectionPool([requestA, requestB], simulationOptions({rtt, throughput})); - const connectionForA = pool.acquire(recordA); - const connectionForB = pool.acquire(recordB); + const connectionForA = pool.acquire(requestA); + const connectionForB = pool.acquire(requestB); for (let i = 0; i < 10; i++) { - assert.equal(pool.acquireActiveConnectionFromRecord(recordA), connectionForA); - assert.equal(pool.acquireActiveConnectionFromRecord(recordB), connectionForB); + assert.equal(pool.acquireActiveConnectionFromRequest(requestA), connectionForA); + assert.equal(pool.acquireActiveConnectionFromRequest(requestB), connectionForB); } assert.deepStrictEqual(pool.connectionsInUse(), [connectionForA, connectionForB]); }); it('should allocate at least 6 connections', () => { - const pool = new ConnectionPool([record()], simulationOptions({rtt, throughput})); + const pool = new ConnectionPool([request()], simulationOptions({rtt, throughput})); for (let i = 0; i < 6; i++) { - assert.ok(pool.acquire(record()), `did not find connection for ${i}th record`); + assert.ok(pool.acquire(request()), `did not find connection for ${i}th request`); } }); it('should allocate all connections', () => { - const records = new Array(7).fill(undefined, 0, 7).map(() => record()); + const records = new Array(7).fill(undefined, 0, 7).map(() => request()); const pool = new ConnectionPool(records, simulationOptions({rtt, throughput})); - const connections = records.map(record => pool.acquire(record)); - assert.ok(connections[0], 'did not find connection for 1st record'); - assert.ok(connections[5], 'did not find connection for 6th record'); - assert.ok(connections[6], 'did not find connection for 7th record'); + const connections = records.map(request => pool.acquire(request)); + assert.ok(connections[0], 'did not find connection for 1st request'); + assert.ok(connections[5], 'did not find connection for 6th request'); + assert.ok(connections[6], 'did not find connection for 7th request'); }); it('should be oblivious to connection reuse', () => { - const coldRecord = record(); - const warmRecord = record(); + const coldRecord = request(); + const warmRecord = request(); const pool = new ConnectionPool([coldRecord, warmRecord], simulationOptions({rtt, throughput})); pool._connectionReusedByRequestId.set(warmRecord.requestId, true); @@ -137,14 +137,14 @@ describe('DependencyGraph/Simulator/ConnectionPool', () => { } assert.ok(pool.acquire(coldRecord), 'should have acquired connection'); - assert.ok(pool.acquireActiveConnectionFromRecord(warmRecord), + assert.ok(pool.acquireActiveConnectionFromRequest(warmRecord), 'should have acquired connection'); }); it('should acquire in order of warmness', () => { - const recordA = record(); - const recordB = record(); - const recordC = record(); + const recordA = request(); + const recordB = request(); + const recordC = request(); const pool = new ConnectionPool([recordA, recordB, recordC], simulationOptions({rtt, throughput})); pool._connectionReusedByRequestId.set(recordA.requestId, true); @@ -167,27 +167,27 @@ describe('DependencyGraph/Simulator/ConnectionPool', () => { }); describe('.release', () => { - it('noop for record without connection', () => { - const recordA = record(); - const pool = new ConnectionPool([recordA], simulationOptions({rtt, throughput})); - assert.equal(pool.release(recordA), undefined); + it('noop for request without connection', () => { + const requestA = request(); + const pool = new ConnectionPool([requestA], simulationOptions({rtt, throughput})); + assert.equal(pool.release(requestA), undefined); }); it('frees the connection for reissue', () => { - const records = new Array(6).fill(undefined, 0, 7).map(() => record()); - const pool = new ConnectionPool(records, simulationOptions({rtt, throughput})); - records.push(record()); + const requests = new Array(6).fill(undefined, 0, 7).map(() => request()); + const pool = new ConnectionPool(requests, simulationOptions({rtt, throughput})); + requests.push(request()); - records.forEach(record => pool.acquire(record)); + requests.forEach(request => pool.acquire(request)); assert.equal(pool.connectionsInUse().length, 6); - assert.ok(!pool.acquire(records[6]), 'had connection that is in use'); + assert.ok(!pool.acquire(requests[6]), 'had connection that is in use'); - pool.release(records[0]); + pool.release(requests[0]); assert.equal(pool.connectionsInUse().length, 5); - assert.ok(pool.acquire(records[6]), 'could not reissue released connection'); - assert.ok(!pool.acquire(records[0]), 'had connection that is in use'); + assert.ok(pool.acquire(requests[6]), 'could not reissue released connection'); + assert.ok(!pool.acquire(requests[0]), 'had connection that is in use'); }); }); }); diff --git a/core/test/lib/lantern/simulator/dns-cache-test.js b/core/test/lib/lantern/simulator/dns-cache-test.js index cf47322ef4cd..497d9a166621 100644 --- a/core/test/lib/lantern/simulator/dns-cache-test.js +++ b/core/test/lib/lantern/simulator/dns-cache-test.js @@ -61,7 +61,7 @@ describe('DependencyGraph/Simulator/DNSCache', () => { }); describe('.setResolvedAt', () => { - it('should set the DNS resolution time for a record', () => { + it('should set the DNS resolution time for a request', () => { dns.setResolvedAt(request.parsedURL.host, 123); const resolutionTime = dns.getTimeUntilResolution(request); expect(resolutionTime).toEqual(123); diff --git a/core/test/lib/lantern/simulator/network-analyzer-test.js b/core/test/lib/lantern/simulator/network-analyzer-test.js index 8ca0caf72f60..ea360a5edd03 100644 --- a/core/test/lib/lantern/simulator/network-analyzer-test.js +++ b/core/test/lib/lantern/simulator/network-analyzer-test.js @@ -26,7 +26,7 @@ describe('DependencyGraph/Simulator/NetworkAnalyzer', () => { const url = opts.url || 'https://example.com'; if (opts.networkRequestTime) opts.networkRequestTime *= 1000; if (opts.networkEndTime) opts.networkEndTime *= 1000; - const record = Object.assign( + const request = Object.assign( { url, requestId: recordId++, @@ -41,7 +41,7 @@ describe('DependencyGraph/Simulator/NetworkAnalyzer', () => { }, opts ); - return NetworkRequest.asLanternNetworkRequest(record); + return NetworkRequest.asLanternNetworkRequest(request); } beforeEach(() => { @@ -144,56 +144,56 @@ describe('DependencyGraph/Simulator/NetworkAnalyzer', () => { describe('#estimateRTTByOrigin', () => { it('should infer from tcp timing when available', () => { const timing = {connectStart: 0, connectEnd: 99}; - const record = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); - const result = NetworkAnalyzer.estimateRTTByOrigin([record]); + const request = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); + const result = NetworkAnalyzer.estimateRTTByOrigin([request]); const expected = {min: 99, max: 99, avg: 99, median: 99}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); it('should infer only one estimate if tcp and ssl start times are equal', () => { const timing = {connectStart: 0, connectEnd: 99, sslStart: 0, sslEnd: 99}; - const record = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); - const result = NetworkAnalyzer.estimateRTTByOrigin([record]); + const request = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); + const result = NetworkAnalyzer.estimateRTTByOrigin([request]); const expected = {min: 99, max: 99, avg: 99, median: 99}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); it('should infer from tcp and ssl timing when available', () => { const timing = {connectStart: 0, connectEnd: 99, sslStart: 50, sslEnd: 99}; - const record = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); - const result = NetworkAnalyzer.estimateRTTByOrigin([record]); + const request = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); + const result = NetworkAnalyzer.estimateRTTByOrigin([request]); const expected = {min: 49, max: 50, avg: 49.5, median: 49.5}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); it('should infer from connection timing when available for h3 (one estimate)', () => { const timing = {connectStart: 0, connectEnd: 99, sslStart: 1, sslEnd: 99}; - const record = + const request = createRecord({networkRequestTime: 0, networkEndTime: 1, timing, protocol: 'h3'}); - const result = NetworkAnalyzer.estimateRTTByOrigin([record]); + const result = NetworkAnalyzer.estimateRTTByOrigin([request]); const expected = {min: 99, max: 99, avg: 99, median: 99}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); it('should infer from sendStart when available', () => { const timing = {sendStart: 150}; - // this record took 150ms before Chrome could send the request + // this request took 150ms before Chrome could send the request // i.e. DNS (maybe) + queuing (maybe) + TCP handshake took ~100ms // 150ms / 3 round trips ~= 50ms RTT - const record = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); - const result = NetworkAnalyzer.estimateRTTByOrigin([record], {coarseEstimateMultiplier: 1}); + const request = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); + const result = NetworkAnalyzer.estimateRTTByOrigin([request], {coarseEstimateMultiplier: 1}); const expected = {min: 50, max: 50, avg: 50, median: 50}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); it('should infer from download timing when available', () => { const timing = {receiveHeadersEnd: 100}; - // this record took 1000ms after the first byte was received to download the payload + // this request took 1000ms after the first byte was received to download the payload // i.e. it took at least one full additional roundtrip after first byte to download the rest // 1000ms / 1 round trip ~= 1000ms RTT - const record = createRecord({networkRequestTime: 0, networkEndTime: 1.1, + const request = createRecord({networkRequestTime: 0, networkEndTime: 1.1, transferSize: 28 * 1024, timing}); - const result = NetworkAnalyzer.estimateRTTByOrigin([record], { + const result = NetworkAnalyzer.estimateRTTByOrigin([request], { coarseEstimateMultiplier: 1, useHeadersEndEstimates: false, }); @@ -203,13 +203,13 @@ describe('DependencyGraph/Simulator/NetworkAnalyzer', () => { it('should infer from TTFB when available', () => { const timing = {receiveHeadersEnd: 1000}; - const record = createRecord({networkRequestTime: 0, networkEndTime: 1, timing, + const request = createRecord({networkRequestTime: 0, networkEndTime: 1, timing, resourceType: 'Other'}); - const result = NetworkAnalyzer.estimateRTTByOrigin([record], { + const result = NetworkAnalyzer.estimateRTTByOrigin([request], { coarseEstimateMultiplier: 1, }); - // this record's TTFB was 1000ms, it used SSL and was a fresh connection requiring a handshake + // this request's TTFB was 1000ms, it used SSL and was a fresh connection requiring a handshake // which needs ~4 RTs. We don't know its resource type so it'll be assumed that 40% of it was // server response time. // 600 ms / 4 = 150ms @@ -268,16 +268,16 @@ describe('DependencyGraph/Simulator/NetworkAnalyzer', () => { it('should use lrStatistics when needed', () => { global.isLightrider = true; - const record = createRecord({timing: {}, lrStatistics: {TCPMs: 100}}); - const result = NetworkAnalyzer.estimateRTTByOrigin([record]); + const request = createRecord({timing: {}, lrStatistics: {TCPMs: 100}}); + const result = NetworkAnalyzer.estimateRTTByOrigin([request]); const expected = {min: 50, max: 50, avg: 50, median: 50}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); it('should use lrStatistics when needed (h3)', () => { global.isLightrider = true; - const record = createRecord({protocol: 'h3', timing: {}, lrStatistics: {TCPMs: 100}}); - const result = NetworkAnalyzer.estimateRTTByOrigin([record]); + const request = createRecord({protocol: 'h3', timing: {}, lrStatistics: {TCPMs: 100}}); + const result = NetworkAnalyzer.estimateRTTByOrigin([request]); const expected = {min: 100, max: 100, avg: 100, median: 100}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); @@ -286,26 +286,26 @@ describe('DependencyGraph/Simulator/NetworkAnalyzer', () => { describe('#estimateServerResponseTimeByOrigin', () => { it('should estimate server response time using ttfb times', () => { const timing = {sendEnd: 100, receiveHeadersEnd: 200}; - const record = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); + const request = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); const rttByOrigin = new Map([[NetworkAnalyzer.SUMMARY, 0]]); - const result = NetworkAnalyzer.estimateServerResponseTimeByOrigin([record], {rttByOrigin}); + const result = NetworkAnalyzer.estimateServerResponseTimeByOrigin([request], {rttByOrigin}); const expected = {min: 100, max: 100, avg: 100, median: 100}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); it('should subtract out rtt', () => { const timing = {sendEnd: 100, receiveHeadersEnd: 200}; - const record = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); + const request = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); const rttByOrigin = new Map([[NetworkAnalyzer.SUMMARY, 50]]); - const result = NetworkAnalyzer.estimateServerResponseTimeByOrigin([record], {rttByOrigin}); + const result = NetworkAnalyzer.estimateServerResponseTimeByOrigin([request], {rttByOrigin}); const expected = {min: 50, max: 50, avg: 50, median: 50}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); it('should compute rtts when not provided', () => { const timing = {connectStart: 5, connectEnd: 55, sendEnd: 100, receiveHeadersEnd: 200}; - const record = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); - const result = NetworkAnalyzer.estimateServerResponseTimeByOrigin([record]); + const request = createRecord({networkRequestTime: 0, networkEndTime: 1, timing}); + const result = NetworkAnalyzer.estimateServerResponseTimeByOrigin([request]); const expected = {min: 50, max: 50, avg: 50, median: 50}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); @@ -335,8 +335,8 @@ describe('DependencyGraph/Simulator/NetworkAnalyzer', () => { it('should use lrStatistics when needed', () => { global.isLightrider = true; - const record = createRecord({timing: {}, lrStatistics: {TCPMs: 1, requestMs: 100}}); - const result = NetworkAnalyzer.estimateServerResponseTimeByOrigin([record]); + const request = createRecord({timing: {}, lrStatistics: {TCPMs: 1, requestMs: 100}}); + const result = NetworkAnalyzer.estimateServerResponseTimeByOrigin([request]); const expected = {min: 100, max: 100, avg: 100, median: 100}; assert.deepStrictEqual(result.get('https://example.com'), expected); }); diff --git a/core/test/lib/lantern/simulator/simulator-test.js b/core/test/lib/lantern/simulator/simulator-test.js index 650a160c5888..dcb9474d611a 100644 --- a/core/test/lib/lantern/simulator/simulator-test.js +++ b/core/test/lib/lantern/simulator/simulator-test.js @@ -28,7 +28,7 @@ function request(opts) { delete opts.startTime; delete opts.endTime; - const record = Object.assign({ + const request = Object.assign({ requestId: opts.requestId || nextRequestId++, url, transferSize: opts.transferSize || 1000, @@ -38,7 +38,7 @@ function request(opts) { rendererStartTime, networkEndTime, }, opts); - return NetworkRequest.asLanternNetworkRequest(record); + return NetworkRequest.asLanternNetworkRequest(request); } function cpuTask({tid, ts, duration}) { @@ -267,8 +267,8 @@ describe('DependencyGraph/Simulator', () => { ]; for (const imageNode of imageNodes) { - imageNode.record.connectionReused = true; - imageNode.record.connectionId = 1; + imageNode.request.connectionReused = true; + imageNode.request.connectionId = 1; rootNode.addDependent(imageNode); } @@ -297,8 +297,8 @@ describe('DependencyGraph/Simulator', () => { ]; for (const imageNode of imageNodes) { - imageNode.record.connectionReused = true; - imageNode.record.connectionId = 1; + imageNode.request.connectionReused = true; + imageNode.request.connectionId = 1; rootNode.addDependent(imageNode); }