blob: 19520b5bbbfa5dbfb21eeea40e5a11bbbdb3dbb4 [file] [log] [blame]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import copy
import json
import os
import shutil
import subprocess
import tempfile
import unittest
import loading_trace
import page_track
import sandwich_metrics as puller
import sandwich_runner
import request_track
import tracing_track
_BLINK_CAT = 'blink.user_timing'
_MEM_CAT = sandwich_runner.MEMORY_DUMP_CATEGORY
_START = 'requestStart'
_LOADS = 'loadEventStart'
_LOADE = 'loadEventEnd'
_NAVIGATION_START = 'navigationStart'
_PAINT = 'firstContentfulPaint'
_LAYOUT = 'firstLayout'
_MINIMALIST_TRACE_EVENTS = [
{'ph': 'R', 'cat': _BLINK_CAT, 'name': _NAVIGATION_START, 'ts': 10000,
'args': {'frame': '0'}},
{'ph': 'R', 'cat': _BLINK_CAT, 'name': _START, 'ts': 20000,
'args': {}},
{'cat': _MEM_CAT, 'name': 'periodic_interval', 'pid': 1, 'ph': 'v',
'ts': 1, 'args': {'dumps': {'allocators': {'malloc': {'attrs': {'size':{
'units': 'bytes', 'value': '1af2', }}}}}}},
{'ph': 'R', 'cat': _BLINK_CAT, 'name': _LAYOUT, 'ts': 24000,
'args': {'frame': '0'}},
{'ph': 'R', 'cat': _BLINK_CAT, 'name': _PAINT, 'ts': 31000,
'args': {'frame': '0'}},
{'ph': 'R', 'cat': _BLINK_CAT, 'name': _LOADS, 'ts': 35000,
'args': {'frame': '0'}},
{'ph': 'R', 'cat': _BLINK_CAT, 'name': _LOADE, 'ts': 40000,
'args': {'frame': '0'}},
{'cat': _MEM_CAT, 'name': 'periodic_interval', 'pid': 1, 'ph': 'v',
'ts': 1, 'args': {'dumps': {'allocators': {'malloc': {'attrs': {'size':{
'units': 'bytes', 'value': 'd704', }}}}}}},
{'ph': 'M', 'cat': '__metadata', 'pid': 1, 'name': 'process_name', 'ts': 1,
'args': {'name': 'Browser'}}]
def TracingTrack(events):
return tracing_track.TracingTrack.FromJsonDict({
'events': events,
'categories': (sandwich_runner._TRACING_CATEGORIES +
[sandwich_runner.MEMORY_DUMP_CATEGORY])})
def LoadingTrace(events):
return loading_trace.LoadingTrace('http://a.com/', {},
page_track.PageTrack(None),
request_track.RequestTrack(None),
TracingTrack(events))
class PageTrackTest(unittest.TestCase):
def testGetBrowserPID(self):
def RunHelper(expected, events):
self.assertEquals(expected, puller._GetBrowserPID(TracingTrack(events)))
RunHelper(123, [
{'ph': 'M', 'ts': 0, 'pid': 354, 'cat': 'whatever0'},
{'ph': 'M', 'ts': 0, 'pid': 354, 'cat': 'whatever1'},
{'ph': 'M', 'ts': 0, 'pid': 354, 'cat': '__metadata',
'name': 'thread_name'},
{'ph': 'M', 'ts': 0, 'pid': 354, 'cat': '__metadata',
'name': 'process_name', 'args': {'name': 'Renderer'}},
{'ph': 'M', 'ts': 0, 'pid': 123, 'cat': '__metadata',
'name': 'process_name', 'args': {'name': 'Browser'}},
{'ph': 'M', 'ts': 0, 'pid': 354, 'cat': 'whatever0'}])
with self.assertRaises(ValueError):
RunHelper(123, [
{'ph': 'M', 'ts': 0, 'pid': 354, 'cat': 'whatever0'},
{'ph': 'M', 'ts': 0, 'pid': 354, 'cat': 'whatever1'}])
def testGetBrowserDumpEvents(self):
NAME = 'periodic_interval'
def RunHelper(trace_events, browser_pid):
trace_events = copy.copy(trace_events)
trace_events.append({
'pid': browser_pid,
'cat': '__metadata',
'name': 'process_name',
'ph': 'M',
'ts': 0,
'args': {'name': 'Browser'}})
return puller._GetBrowserDumpEvents(TracingTrack(trace_events))
TRACE_EVENTS = [
{'pid': 354, 'ts': 1000, 'cat': _MEM_CAT, 'ph': 'v', 'name': NAME},
{'pid': 354, 'ts': 2000, 'cat': _MEM_CAT, 'ph': 'V'},
{'pid': 672, 'ts': 3000, 'cat': _MEM_CAT, 'ph': 'v', 'name': NAME},
{'pid': 123, 'ts': 4000, 'cat': _MEM_CAT, 'ph': 'v', 'name': 'foo'},
{'pid': 123, 'ts': 5000, 'cat': _MEM_CAT, 'ph': 'v', 'name': NAME},
{'pid': 123, 'ts': 6000, 'cat': _MEM_CAT, 'ph': 'V'},
{'pid': 672, 'ts': 7000, 'cat': _MEM_CAT, 'ph': 'v', 'name': NAME},
{'pid': 354, 'ts': 8000, 'cat': _MEM_CAT, 'ph': 'v', 'name': 'foo'},
{'pid': 123, 'ts': 9000, 'cat': 'whatever1', 'ph': 'v', 'name': NAME},
{'pid': 123, 'ts': 10000, 'cat': _MEM_CAT, 'ph': 'v', 'name': NAME},
{'pid': 354, 'ts': 11000, 'cat': 'whatever0', 'ph': 'R'},
{'pid': 672, 'ts': 12000, 'cat': _MEM_CAT, 'ph': 'v', 'name': NAME}]
bump_events = RunHelper(TRACE_EVENTS, 123)
self.assertEquals(2, len(bump_events))
self.assertEquals(5, bump_events[0].start_msec)
self.assertEquals(10, bump_events[1].start_msec)
bump_events = RunHelper(TRACE_EVENTS, 354)
self.assertEquals(1, len(bump_events))
self.assertEquals(1, bump_events[0].start_msec)
bump_events = RunHelper(TRACE_EVENTS, 672)
self.assertEquals(3, len(bump_events))
self.assertEquals(3, bump_events[0].start_msec)
self.assertEquals(7, bump_events[1].start_msec)
self.assertEquals(12, bump_events[2].start_msec)
with self.assertRaises(ValueError):
RunHelper(TRACE_EVENTS, 895)
def testGetWebPageTrackedEvents(self):
trace_events = puller._GetWebPageTrackedEvents(TracingTrack([
{'ph': 'R', 'ts': 0000, 'args': {}, 'cat': 'whatever',
'name': _START},
{'ph': 'R', 'ts': 1000, 'args': {'frame': '0'}, 'cat': 'whatever',
'name': _LOADS},
{'ph': 'R', 'ts': 2000, 'args': {'frame': '0'}, 'cat': 'whatever',
'name': _LOADE},
{'ph': 'R', 'ts': 3000, 'args': {}, 'cat': _BLINK_CAT,
'name': _START},
{'ph': 'R', 'ts': 4000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _LOADS},
{'ph': 'R', 'ts': 5000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _LOADE},
{'ph': 'R', 'ts': 7000, 'args': {}, 'cat': _BLINK_CAT,
'name': _START},
{'ph': 'R', 'ts': 8000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _LOADS},
{'ph': 'R', 'ts': 9000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _LOADE},
{'ph': 'R', 'ts': 11000, 'args': {'frame': '0'}, 'cat': 'whatever',
'name': _START},
{'ph': 'R', 'ts': 12000, 'args': {'frame': '0'}, 'cat': 'whatever',
'name': _LOADS},
{'ph': 'R', 'ts': 13000, 'args': {'frame': '0'}, 'cat': 'whatever',
'name': _LOADE},
{'ph': 'R', 'ts': 14000, 'args': {}, 'cat': _BLINK_CAT,
'name': _START},
{'ph': 'R', 'ts': 10000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _NAVIGATION_START}, # Event out of |start_msec| order.
{'ph': 'R', 'ts': 6000, 'args': {'frame': '0'}, 'cat': 'whatever',
'name': _NAVIGATION_START},
{'ph': 'R', 'ts': 15000, 'args': {}, 'cat': _BLINK_CAT,
'name': _START},
{'ph': 'R', 'ts': 16000, 'args': {'frame': '1'}, 'cat': _BLINK_CAT,
'name': _LOADS},
{'ph': 'R', 'ts': 17000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _LOADS},
{'ph': 'R', 'ts': 18000, 'args': {'frame': '1'}, 'cat': _BLINK_CAT,
'name': _LOADE},
{'ph': 'R', 'ts': 19000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _LOADE},
{'ph': 'R', 'ts': 20000, 'args': {}, 'cat': 'whatever',
'name': _START},
{'ph': 'R', 'ts': 21000, 'args': {'frame': '0'}, 'cat': 'whatever',
'name': _LOADS},
{'ph': 'R', 'ts': 22000, 'args': {'frame': '0'}, 'cat': 'whatever',
'name': _LOADE},
{'ph': 'R', 'ts': 23000, 'args': {}, 'cat': _BLINK_CAT,
'name': _START},
{'ph': 'R', 'ts': 24000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _LOADS},
{'ph': 'R', 'ts': 25000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _LOADE}]))
self.assertEquals(3, len(trace_events))
self.assertEquals(14, trace_events['requestStart'].start_msec)
self.assertEquals(17, trace_events['loadEventStart'].start_msec)
self.assertEquals(19, trace_events['loadEventEnd'].start_msec)
def testExtractDefaultMetrics(self):
metrics = puller._ExtractDefaultMetrics(LoadingTrace(
_MINIMALIST_TRACE_EVENTS))
self.assertEquals(4, len(metrics))
self.assertEquals(20, metrics['total_load'])
self.assertEquals(5, metrics['js_onload_event'])
self.assertEquals(4, metrics['first_layout'])
self.assertEquals(11, metrics['first_contentful_paint'])
def testExtractDefaultMetricsBestEffort(self):
metrics = puller._ExtractDefaultMetrics(LoadingTrace([
{'ph': 'R', 'ts': 10000, 'args': {'frame': '0'}, 'cat': _BLINK_CAT,
'name': _NAVIGATION_START},
{'ph': 'R', 'ts': 11000, 'args': {'frame': '0'}, 'cat': 'whatever',
'name': _START}]))
self.assertEquals(4, len(metrics))
self.assertEquals(puller._FAILED_CSV_VALUE, metrics['total_load'])
self.assertEquals(puller._FAILED_CSV_VALUE, metrics['js_onload_event'])
self.assertEquals(puller._FAILED_CSV_VALUE, metrics['first_layout'])
self.assertEquals(puller._FAILED_CSV_VALUE,
metrics['first_contentful_paint'])
def testExtractMemoryMetrics(self):
metrics = puller._ExtractMemoryMetrics(LoadingTrace(
_MINIMALIST_TRACE_EVENTS))
self.assertEquals(2, len(metrics))
self.assertEquals(30971, metrics['browser_malloc_avg'])
self.assertEquals(55044, metrics['browser_malloc_max'])
def testComputeSpeedIndex(self):
def point(time, frame_completeness):
return puller.CompletenessPoint(time=time,
frame_completeness=frame_completeness)
completness_record = [
point(0, 0.0),
point(120, 0.4),
point(190, 0.75),
point(280, 1.0),
point(400, 1.0),
]
self.assertEqual(120 + 70 * 0.6 + 90 * 0.25,
puller._ComputeSpeedIndex(completness_record))
completness_record = [
point(70, 0.0),
point(150, 0.3),
point(210, 0.6),
point(220, 0.9),
point(240, 1.0),
]
self.assertEqual(80 + 60 * 0.7 + 10 * 0.4 + 20 * 0.1,
puller._ComputeSpeedIndex(completness_record))
completness_record = [
point(90, 0.0),
point(200, 0.6),
point(150, 0.3),
point(230, 1.0),
]
with self.assertRaises(ValueError):
puller._ComputeSpeedIndex(completness_record)
if __name__ == '__main__':
unittest.main()