Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Side by Side Diff: chrome/test/functional/perf_endure.py

Issue 222873002: Remove pyauto tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: sync Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/test/functional/perf/endure_setup.py ('k') | chrome/test/functional/prefetch.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Performance tests for Chrome Endure (long-running perf tests on Chrome).
7
8 This module accepts the following environment variable inputs:
9 TEST_LENGTH: The number of seconds in which to run each test.
10 PERF_STATS_INTERVAL: The number of seconds to wait in-between each sampling
11 of performance/memory statistics.
12
13 The following variables are related to the Deep Memory Profiler.
14 DEEP_MEMORY_PROFILE: Enable the Deep Memory Profiler if it's set to 'True'.
15 DEEP_MEMORY_PROFILE_SAVE: Don't clean up dump files if it's set to 'True'.
16 DEEP_MEMORY_PROFILE_UPLOAD: Upload dumped files if the variable has a Google
17 Storage bucket like gs://chromium-endure/. The 'gsutil' script in $PATH
18 is used by default, or set a variable 'GSUTIL' to specify a path to the
19 'gsutil' script. A variable 'REVISION' (or 'BUILDBOT_GOT_REVISION') is
20 used as a subdirectory in the destination if it is set.
21 GSUTIL: A path to the 'gsutil' script. Not mandatory.
22 REVISION: A string that represents the revision or some build configuration.
23 Not mandatory.
24 BUILDBOT_GOT_REVISION: Similar to 'REVISION', but checked only if 'REVISION'
25 is not specified. Not mandatory.
26 """
27
28 from datetime import datetime
29 import json
30 import logging
31 import os
32 import re
33 import subprocess
34 import tempfile
35 import time
36
37 import perf
38 import pyauto_functional # Must be imported before pyauto.
39 import pyauto
40 import pyauto_errors
41 import pyauto_utils
42 import remote_inspector_client
43 import selenium.common.exceptions
44 from selenium.webdriver.support.ui import WebDriverWait
45
46
47 class NotSupportedEnvironmentError(RuntimeError):
48 """Represent an error raised since the environment (OS) is not supported."""
49 pass
50
51
52 class DeepMemoryProfiler(object):
53 """Controls Deep Memory Profiler (dmprof) for endurance tests."""
54 DEEP_MEMORY_PROFILE = False
55 DEEP_MEMORY_PROFILE_SAVE = False
56 DEEP_MEMORY_PROFILE_UPLOAD = ''
57
58 _WORKDIR_PATTERN = re.compile('endure\.[0-9]+\.[0-9]+\.[A-Za-z0-9]+')
59 _SAVED_WORKDIRS = 8
60
61 _DMPROF_DIR_PATH = os.path.abspath(os.path.join(
62 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
63 'tools', 'deep_memory_profiler'))
64 _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof')
65 _POLICIES = ['l0', 'l1', 'l2', 't0']
66
67 def __init__(self):
68 self._enabled = self.GetEnvironmentVariable(
69 'DEEP_MEMORY_PROFILE', bool, self.DEEP_MEMORY_PROFILE)
70 self._save = self.GetEnvironmentVariable(
71 'DEEP_MEMORY_PROFILE_SAVE', bool, self.DEEP_MEMORY_PROFILE_SAVE)
72 self._upload = self.GetEnvironmentVariable(
73 'DEEP_MEMORY_PROFILE_UPLOAD', str, self.DEEP_MEMORY_PROFILE_UPLOAD)
74 if self._upload and not self._upload.endswith('/'):
75 self._upload += '/'
76
77 self._revision = ''
78 self._gsutil = ''
79 self._json_file = None
80 self._last_json_filename = ''
81 self._proc = None
82 self._last_time = {}
83 for policy in self._POLICIES:
84 self._last_time[policy] = -1.0
85
86 def __nonzero__(self):
87 return self._enabled
88
89 @staticmethod
90 def GetEnvironmentVariable(env_name, converter, default):
91 """Returns a converted environment variable for Deep Memory Profiler.
92
93 Args:
94 env_name: A string name of an environment variable.
95 converter: A function taking a string to convert an environment variable.
96 default: A value used if the environment variable is not specified.
97
98 Returns:
99 A value converted from the environment variable with 'converter'.
100 """
101 return converter(os.environ.get(env_name, default))
102
103 def SetUp(self, is_linux, revision, gsutil):
104 """Sets up Deep Memory Profiler settings for a Chrome process.
105
106 It sets environment variables and makes a working directory.
107 """
108 if not self._enabled:
109 return
110
111 if not is_linux:
112 raise NotSupportedEnvironmentError(
113 'Deep Memory Profiler is not supported in this environment (OS).')
114
115 self._revision = revision
116 self._gsutil = gsutil
117
118 # Remove old dumped files with keeping latest _SAVED_WORKDIRS workdirs.
119 # It keeps the latest workdirs not to miss data by failure in uploading
120 # and other operations. Dumped files are no longer available if they are
121 # removed. Re-execution doesn't generate the same files.
122 tempdir = tempfile.gettempdir()
123 saved_workdirs = 0
124 for filename in sorted(os.listdir(tempdir), reverse=True):
125 if self._WORKDIR_PATTERN.match(filename):
126 saved_workdirs += 1
127 if saved_workdirs > self._SAVED_WORKDIRS:
128 fullpath = os.path.abspath(os.path.join(tempdir, filename))
129 logging.info('Removing an old workdir: %s' % fullpath)
130 pyauto_utils.RemovePath(fullpath)
131
132 dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S')
133 self._workdir = tempfile.mkdtemp(prefix=dir_prefix, dir=tempdir)
134 os.environ['HEAPPROFILE'] = os.path.join(self._workdir, 'endure')
135 os.environ['HEAP_PROFILE_MMAP'] = '1'
136 os.environ['DEEP_HEAP_PROFILE'] = '1'
137
138 def TearDown(self):
139 """Tear down Deep Memory Profiler settings for the Chrome process.
140
141 It removes the environment variables and the temporary directory.
142 Call it after Chrome finishes. Chrome may dump last files at the end.
143 """
144 if not self._enabled:
145 return
146
147 del os.environ['DEEP_HEAP_PROFILE']
148 del os.environ['HEAP_PROFILE_MMAP']
149 del os.environ['HEAPPROFILE']
150 if not self._save and self._workdir:
151 pyauto_utils.RemovePath(self._workdir)
152
153 def LogFirstMessage(self):
154 """Logs first messages."""
155 if not self._enabled:
156 return
157
158 logging.info('Running with the Deep Memory Profiler.')
159 if self._save:
160 logging.info(' Dumped files won\'t be cleaned.')
161 else:
162 logging.info(' Dumped files will be cleaned up after every test.')
163
164 def StartProfiler(self, proc_info, is_last, webapp_name, test_description):
165 """Starts Deep Memory Profiler in background."""
166 if not self._enabled:
167 return
168
169 logging.info(' Profiling with the Deep Memory Profiler...')
170
171 # Wait for a running dmprof process for last _GetPerformanceStat call to
172 # cover last dump files.
173 if is_last:
174 logging.info(' Waiting for the last dmprof.')
175 self._WaitForDeepMemoryProfiler()
176
177 if self._proc and self._proc.poll() is None:
178 logging.info(' Last dmprof is still running.')
179 else:
180 if self._json_file:
181 self._last_json_filename = self._json_file.name
182 self._json_file.close()
183 self._json_file = None
184 first_dump = ''
185 last_dump = ''
186 for filename in sorted(os.listdir(self._workdir)):
187 if re.match('^endure.%05d.\d+.heap$' % proc_info['tab_pid'],
188 filename):
189 logging.info(' Profiled dump file: %s' % filename)
190 last_dump = filename
191 if not first_dump:
192 first_dump = filename
193 if first_dump:
194 logging.info(' First dump file: %s' % first_dump)
195 matched = re.match('^endure.\d+.(\d+).heap$', last_dump)
196 last_sequence_id = matched.group(1)
197 self._json_file = open(
198 os.path.join(self._workdir,
199 'endure.%05d.%s.json' % (proc_info['tab_pid'],
200 last_sequence_id)), 'w+')
201 self._proc = subprocess.Popen(
202 '%s json %s' % (self._DMPROF_SCRIPT_PATH,
203 os.path.join(self._workdir, first_dump)),
204 shell=True, stdout=self._json_file)
205 if is_last:
206 # Wait only when it is the last profiling. dmprof may take long time.
207 self._WaitForDeepMemoryProfiler()
208
209 # Upload the dumped files.
210 if first_dump and self._upload and self._gsutil:
211 if self._revision:
212 destination_path = '%s%s/' % (self._upload, self._revision)
213 else:
214 destination_path = self._upload
215 destination_path += '%s-%s-%s.zip' % (
216 webapp_name,
217 test_description,
218 os.path.basename(self._workdir))
219 gsutil_command = '%s upload --gsutil %s %s %s' % (
220 self._DMPROF_SCRIPT_PATH,
221 self._gsutil,
222 os.path.join(self._workdir, first_dump),
223 destination_path)
224 logging.info('Uploading: %s' % gsutil_command)
225 try:
226 returncode = subprocess.call(gsutil_command, shell=True)
227 logging.info(' Return code: %d' % returncode)
228 except OSError, e:
229 logging.error(' Error while uploading: %s', e)
230 else:
231 logging.info('Note that the dumped files are not uploaded.')
232 else:
233 logging.info(' No dump files.')
234
235 def ParseResultAndOutputPerfGraphValues(
236 self, webapp_name, test_description, output_perf_graph_value):
237 """Parses Deep Memory Profiler result, and outputs perf graph values."""
238 if not self._enabled:
239 return
240
241 results = {}
242 for policy in self._POLICIES:
243 if self._last_json_filename:
244 json_data = {}
245 with open(self._last_json_filename) as json_f:
246 json_data = json.load(json_f)
247 if json_data['version'] == 'JSON_DEEP_1':
248 results[policy] = json_data['snapshots']
249 elif json_data['version'] == 'JSON_DEEP_2':
250 results[policy] = json_data['policies'][policy]['snapshots']
251 for policy, result in results.iteritems():
252 if result and result[-1]['second'] > self._last_time[policy]:
253 started = False
254 for legend in json_data['policies'][policy]['legends']:
255 if legend == 'FROM_HERE_FOR_TOTAL':
256 started = True
257 elif legend == 'UNTIL_HERE_FOR_TOTAL':
258 break
259 elif started:
260 output_perf_graph_value(
261 legend.encode('utf-8'), [
262 (int(round(snapshot['second'])), snapshot[legend] / 1024)
263 for snapshot in result
264 if snapshot['second'] > self._last_time[policy]],
265 'KB',
266 graph_name='%s%s-%s-DMP' % (
267 webapp_name, test_description, policy),
268 units_x='seconds', is_stacked=True)
269 self._last_time[policy] = result[-1]['second']
270
271 def _WaitForDeepMemoryProfiler(self):
272 """Waits for the Deep Memory Profiler to finish if running."""
273 if not self._enabled or not self._proc:
274 return
275
276 self._proc.wait()
277 self._proc = None
278 if self._json_file:
279 self._last_json_filename = self._json_file.name
280 self._json_file.close()
281 self._json_file = None
282
283
284 class ChromeEndureBaseTest(perf.BasePerfTest):
285 """Implements common functionality for all Chrome Endure tests.
286
287 All Chrome Endure test classes should inherit from this class.
288 """
289
290 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours.
291 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes.
292 # TODO(dennisjeffrey): Do we still need to tolerate errors?
293 _ERROR_COUNT_THRESHOLD = 50 # Number of errors to tolerate.
294 _REVISION = ''
295 _GSUTIL = 'gsutil'
296
297 def setUp(self):
298 # The environment variables for the Deep Memory Profiler must be set
299 # before perf.BasePerfTest.setUp() to inherit them to Chrome.
300 self._dmprof = DeepMemoryProfiler()
301 self._revision = str(os.environ.get('REVISION', self._REVISION))
302 if not self._revision:
303 self._revision = str(os.environ.get('BUILDBOT_GOT_REVISION',
304 self._REVISION))
305 self._gsutil = str(os.environ.get('GSUTIL', self._GSUTIL))
306 if self._dmprof:
307 self._dmprof.SetUp(self.IsLinux(), self._revision, self._gsutil)
308
309 perf.BasePerfTest.setUp(self)
310
311 self._test_length_sec = int(
312 os.environ.get('TEST_LENGTH', self._DEFAULT_TEST_LENGTH_SEC))
313 self._get_perf_stats_interval = int(
314 os.environ.get('PERF_STATS_INTERVAL', self._GET_PERF_STATS_INTERVAL))
315
316 logging.info('Running test for %d seconds.', self._test_length_sec)
317 logging.info('Gathering perf stats every %d seconds.',
318 self._get_perf_stats_interval)
319
320 if self._dmprof:
321 self._dmprof.LogFirstMessage()
322
323 # Set up a remote inspector client associated with tab 0.
324 logging.info('Setting up connection to remote inspector...')
325 self._remote_inspector_client = (
326 remote_inspector_client.RemoteInspectorClient())
327 logging.info('Connection to remote inspector set up successfully.')
328
329 self._test_start_time = 0
330 self._num_errors = 0
331 self._events_to_output = []
332
333 def tearDown(self):
334 logging.info('Terminating connection to remote inspector...')
335 self._remote_inspector_client.Stop()
336 logging.info('Connection to remote inspector terminated.')
337
338 # Must be done at end of this function except for post-cleaning after
339 # Chrome finishes.
340 perf.BasePerfTest.tearDown(self)
341
342 # Must be done after perf.BasePerfTest.tearDown()
343 if self._dmprof:
344 self._dmprof.TearDown()
345
346 def ExtraChromeFlags(self):
347 """Ensures Chrome is launched with custom flags.
348
349 Returns:
350 A list of extra flags to pass to Chrome when it is launched.
351 """
352 # The same with setUp, but need to fetch the environment variable since
353 # ExtraChromeFlags is called before setUp.
354 deep_memory_profile = DeepMemoryProfiler.GetEnvironmentVariable(
355 'DEEP_MEMORY_PROFILE', bool, DeepMemoryProfiler.DEEP_MEMORY_PROFILE)
356
357 # Ensure Chrome enables remote debugging on port 9222. This is required to
358 # interact with Chrome's remote inspector.
359 # Also, enable the memory benchmarking V8 extension for heap dumps.
360 extra_flags = ['--remote-debugging-port=9222',
361 '--enable-memory-benchmarking']
362 if deep_memory_profile:
363 extra_flags.append('--no-sandbox')
364 return perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags
365
366 def _OnTimelineEvent(self, event_info):
367 """Invoked by the Remote Inspector Client when a timeline event occurs.
368
369 Args:
370 event_info: A dictionary containing raw information associated with a
371 timeline event received from Chrome's remote inspector. Refer to
372 chrome/src/third_party/WebKit/Source/WebCore/inspector/Inspector.json
373 for the format of this dictionary.
374 """
375 elapsed_time = int(round(time.time() - self._test_start_time))
376
377 if event_info['type'] == 'GCEvent':
378 self._events_to_output.append({
379 'type': 'GarbageCollection',
380 'time': elapsed_time,
381 'data':
382 {'collected_bytes': event_info['data']['usedHeapSizeDelta']},
383 })
384
385 def _RunEndureTest(self, webapp_name, tab_title_substring, test_description,
386 do_scenario, frame_xpath=''):
387 """The main test harness function to run a general Chrome Endure test.
388
389 After a test has performed any setup work and has navigated to the proper
390 starting webpage, this function should be invoked to run the endurance test.
391
392 Args:
393 webapp_name: A string name for the webapp being tested. Should not
394 include spaces. For example, 'Gmail', 'Docs', or 'Plus'.
395 tab_title_substring: A unique substring contained within the title of
396 the tab to use, for identifying the appropriate tab.
397 test_description: A string description of what the test does, used for
398 outputting results to be graphed. Should not contain spaces. For
399 example, 'ComposeDiscard' for Gmail.
400 do_scenario: A callable to be invoked that implements the scenario to be
401 performed by this test. The callable is invoked iteratively for the
402 duration of the test.
403 frame_xpath: The string xpath of the frame in which to inject javascript
404 to clear chromedriver's cache (a temporary workaround until the
405 WebDriver team changes how they handle their DOM node cache).
406 """
407 self._num_errors = 0
408 self._test_start_time = time.time()
409 last_perf_stats_time = time.time()
410 if self._dmprof:
411 self.HeapProfilerDump('renderer', 'Chrome Endure (first)')
412 self._GetPerformanceStats(
413 webapp_name, test_description, tab_title_substring)
414 self._iteration_num = 0 # Available to |do_scenario| if needed.
415
416 self._remote_inspector_client.StartTimelineEventMonitoring(
417 self._OnTimelineEvent)
418
419 while time.time() - self._test_start_time < self._test_length_sec:
420 self._iteration_num += 1
421
422 if self._num_errors >= self._ERROR_COUNT_THRESHOLD:
423 logging.error('Error count threshold (%d) reached. Terminating test '
424 'early.' % self._ERROR_COUNT_THRESHOLD)
425 break
426
427 if time.time() - last_perf_stats_time >= self._get_perf_stats_interval:
428 last_perf_stats_time = time.time()
429 if self._dmprof:
430 self.HeapProfilerDump('renderer', 'Chrome Endure')
431 self._GetPerformanceStats(
432 webapp_name, test_description, tab_title_substring)
433
434 if self._iteration_num % 10 == 0:
435 remaining_time = self._test_length_sec - (time.time() -
436 self._test_start_time)
437 logging.info('Chrome interaction #%d. Time remaining in test: %d sec.' %
438 (self._iteration_num, remaining_time))
439
440 do_scenario()
441 # Clear ChromeDriver's DOM node cache so its growth doesn't affect the
442 # results of Chrome Endure.
443 # TODO(dennisjeffrey): Once the WebDriver team implements changes to
444 # handle their DOM node cache differently, we need to revisit this. It
445 # may no longer be necessary at that point to forcefully delete the cache.
446 # Additionally, the Javascript below relies on an internal property of
447 # WebDriver that may change at any time. This is only a temporary
448 # workaround to stabilize the Chrome Endure test results.
449 js = """
450 (function() {
451 delete document.$wdc_;
452 window.domAutomationController.send('done');
453 })();
454 """
455 try:
456 self.ExecuteJavascript(js, frame_xpath=frame_xpath)
457 except pyauto_errors.AutomationCommandTimeout:
458 self._num_errors += 1
459 logging.warning('Logging an automation timeout: delete chromedriver '
460 'cache.')
461
462 self._remote_inspector_client.StopTimelineEventMonitoring()
463
464 if self._dmprof:
465 self.HeapProfilerDump('renderer', 'Chrome Endure (last)')
466 self._GetPerformanceStats(
467 webapp_name, test_description, tab_title_substring, is_last=True)
468
469 def _GetProcessInfo(self, tab_title_substring):
470 """Gets process info associated with an open browser/tab.
471
472 Args:
473 tab_title_substring: A unique substring contained within the title of
474 the tab to use; needed for locating the tab info.
475
476 Returns:
477 A dictionary containing information about the browser and specified tab
478 process:
479 {
480 'browser_private_mem': integer, # Private memory associated with the
481 # browser process, in KB.
482 'tab_private_mem': integer, # Private memory associated with the tab
483 # process, in KB.
484 'tab_pid': integer, # Process ID of the tab process.
485 }
486 """
487 browser_process_name = (
488 self.GetBrowserInfo()['properties']['BrowserProcessExecutableName'])
489 info = self.GetProcessInfo()
490
491 # Get the information associated with the browser process.
492 browser_proc_info = []
493 for browser_info in info['browsers']:
494 if browser_info['process_name'] == browser_process_name:
495 for proc_info in browser_info['processes']:
496 if proc_info['child_process_type'] == 'Browser':
497 browser_proc_info.append(proc_info)
498 self.assertEqual(len(browser_proc_info), 1,
499 msg='Expected to find 1 Chrome browser process, but found '
500 '%d instead.\nCurrent process info:\n%s.' % (
501 len(browser_proc_info), self.pformat(info)))
502
503 # Get the process information associated with the specified tab.
504 tab_proc_info = []
505 for browser_info in info['browsers']:
506 for proc_info in browser_info['processes']:
507 if (proc_info['child_process_type'] == 'Tab' and
508 [x for x in proc_info['titles'] if tab_title_substring in x]):
509 tab_proc_info.append(proc_info)
510 self.assertEqual(len(tab_proc_info), 1,
511 msg='Expected to find 1 %s tab process, but found %d '
512 'instead.\nCurrent process info:\n%s.' % (
513 tab_title_substring, len(tab_proc_info),
514 self.pformat(info)))
515
516 browser_proc_info = browser_proc_info[0]
517 tab_proc_info = tab_proc_info[0]
518 return {
519 'browser_private_mem': browser_proc_info['working_set_mem']['priv'],
520 'tab_private_mem': tab_proc_info['working_set_mem']['priv'],
521 'tab_pid': tab_proc_info['pid'],
522 }
523
524 def _GetPerformanceStats(self, webapp_name, test_description,
525 tab_title_substring, is_last=False):
526 """Gets performance statistics and outputs the results.
527
528 Args:
529 webapp_name: A string name for the webapp being tested. Should not
530 include spaces. For example, 'Gmail', 'Docs', or 'Plus'.
531 test_description: A string description of what the test does, used for
532 outputting results to be graphed. Should not contain spaces. For
533 example, 'ComposeDiscard' for Gmail.
534 tab_title_substring: A unique substring contained within the title of
535 the tab to use, for identifying the appropriate tab.
536 is_last: A boolean value which should be True if it's the last call of
537 _GetPerformanceStats. The default is False.
538 """
539 logging.info('Gathering performance stats...')
540 elapsed_time = int(round(time.time() - self._test_start_time))
541
542 memory_counts = self._remote_inspector_client.GetMemoryObjectCounts()
543 proc_info = self._GetProcessInfo(tab_title_substring)
544
545 if self._dmprof:
546 self._dmprof.StartProfiler(
547 proc_info, is_last, webapp_name, test_description)
548
549 # DOM node count.
550 dom_node_count = memory_counts['DOMNodeCount']
551 self._OutputPerfGraphValue(
552 'TotalDOMNodeCount', [(elapsed_time, dom_node_count)], 'nodes',
553 graph_name='%s%s-Nodes-DOM' % (webapp_name, test_description),
554 units_x='seconds')
555
556 # Event listener count.
557 event_listener_count = memory_counts['EventListenerCount']
558 self._OutputPerfGraphValue(
559 'EventListenerCount', [(elapsed_time, event_listener_count)],
560 'listeners',
561 graph_name='%s%s-EventListeners' % (webapp_name, test_description),
562 units_x='seconds')
563
564 # Browser process private memory.
565 self._OutputPerfGraphValue(
566 'BrowserPrivateMemory',
567 [(elapsed_time, proc_info['browser_private_mem'])], 'KB',
568 graph_name='%s%s-BrowserMem-Private' % (webapp_name, test_description),
569 units_x='seconds')
570
571 # Tab process private memory.
572 self._OutputPerfGraphValue(
573 'TabPrivateMemory',
574 [(elapsed_time, proc_info['tab_private_mem'])], 'KB',
575 graph_name='%s%s-TabMem-Private' % (webapp_name, test_description),
576 units_x='seconds')
577
578 # V8 memory used.
579 v8_info = self.GetV8HeapStats() # First window, first tab.
580 v8_mem_used = v8_info['v8_memory_used'] / 1024.0 # Convert to KB.
581 self._OutputPerfGraphValue(
582 'V8MemoryUsed', [(elapsed_time, v8_mem_used)], 'KB',
583 graph_name='%s%s-V8MemUsed' % (webapp_name, test_description),
584 units_x='seconds')
585
586 # V8 memory allocated.
587 v8_mem_allocated = v8_info['v8_memory_allocated'] / 1024.0 # Convert to KB.
588 self._OutputPerfGraphValue(
589 'V8MemoryAllocated', [(elapsed_time, v8_mem_allocated)], 'KB',
590 graph_name='%s%s-V8MemAllocated' % (webapp_name, test_description),
591 units_x='seconds')
592
593 if self._dmprof:
594 self._dmprof.ParseResultAndOutputPerfGraphValues(
595 webapp_name, test_description, self._OutputPerfGraphValue)
596
597 logging.info(' Total DOM node count: %d nodes' % dom_node_count)
598 logging.info(' Event listener count: %d listeners' % event_listener_count)
599 logging.info(' Browser process private memory: %d KB' %
600 proc_info['browser_private_mem'])
601 logging.info(' Tab process private memory: %d KB' %
602 proc_info['tab_private_mem'])
603 logging.info(' V8 memory used: %f KB' % v8_mem_used)
604 logging.info(' V8 memory allocated: %f KB' % v8_mem_allocated)
605
606 # Output any new timeline events that have occurred.
607 if self._events_to_output:
608 logging.info('Logging timeline events...')
609 event_type_to_value_list = {}
610 for event_info in self._events_to_output:
611 if not event_info['type'] in event_type_to_value_list:
612 event_type_to_value_list[event_info['type']] = []
613 event_type_to_value_list[event_info['type']].append(
614 (event_info['time'], event_info['data']))
615 for event_type, value_list in event_type_to_value_list.iteritems():
616 self._OutputEventGraphValue(event_type, value_list)
617 self._events_to_output = []
618 else:
619 logging.info('No new timeline events to log.')
620
621 def _GetElement(self, find_by, value):
622 """Gets a WebDriver element object from the webpage DOM.
623
624 Args:
625 find_by: A callable that queries WebDriver for an element from the DOM.
626 value: A string value that can be passed to the |find_by| callable.
627
628 Returns:
629 The identified WebDriver element object, if found in the DOM, or
630 None, otherwise.
631 """
632 try:
633 return find_by(value)
634 except selenium.common.exceptions.NoSuchElementException:
635 return None
636
637 def _ClickElementByXpath(self, driver, xpath):
638 """Given the xpath for a DOM element, clicks on it using WebDriver.
639
640 Args:
641 driver: A WebDriver object, as returned by self.NewWebDriver().
642 xpath: The string xpath associated with the DOM element to click.
643
644 Returns:
645 True, if the DOM element was found and clicked successfully, or
646 False, otherwise.
647 """
648 try:
649 self.WaitForDomNode(xpath)
650 except (pyauto_errors.JSONInterfaceError,
651 pyauto_errors.JavascriptRuntimeError) as e:
652 logging.exception('PyAuto exception: %s' % e)
653 return False
654
655 try:
656 element = self._GetElement(driver.find_element_by_xpath, xpath)
657 element.click()
658 except (selenium.common.exceptions.StaleElementReferenceException,
659 selenium.common.exceptions.TimeoutException) as e:
660 logging.exception('WebDriver exception: %s' % e)
661 return False
662
663 return True
664
665
666 class ChromeEndureControlTest(ChromeEndureBaseTest):
667 """Control tests for Chrome Endure."""
668
669 _WEBAPP_NAME = 'Control'
670 _TAB_TITLE_SUBSTRING = 'Chrome Endure Control Test'
671
672 def testControlAttachDetachDOMTree(self):
673 """Continually attach and detach a DOM tree from a basic document."""
674 test_description = 'AttachDetachDOMTree'
675 url = self.GetHttpURLForDataPath('chrome_endure', 'endurance_control.html')
676 self.NavigateToURL(url)
677 loaded_tab_title = self.GetActiveTabTitle()
678 self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title,
679 msg='Loaded tab title does not contain "%s": "%s"' %
680 (self._TAB_TITLE_SUBSTRING, loaded_tab_title))
681
682 def scenario():
683 # Just sleep. Javascript in the webpage itself does the work.
684 time.sleep(5)
685
686 self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING,
687 test_description, scenario)
688
689 def testControlAttachDetachDOMTreeWebDriver(self):
690 """Use WebDriver to attach and detach a DOM tree from a basic document."""
691 test_description = 'AttachDetachDOMTreeWebDriver'
692 url = self.GetHttpURLForDataPath('chrome_endure',
693 'endurance_control_webdriver.html')
694 self.NavigateToURL(url)
695 loaded_tab_title = self.GetActiveTabTitle()
696 self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title,
697 msg='Loaded tab title does not contain "%s": "%s"' %
698 (self._TAB_TITLE_SUBSTRING, loaded_tab_title))
699
700 driver = self.NewWebDriver()
701
702 def scenario(driver):
703 # Click the "attach" button to attach a large DOM tree (with event
704 # listeners) to the document, wait half a second, click "detach" to detach
705 # the DOM tree from the document, wait half a second.
706 self._ClickElementByXpath(driver, 'id("attach")')
707 time.sleep(0.5)
708 self._ClickElementByXpath(driver, 'id("detach")')
709 time.sleep(0.5)
710
711 self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING,
712 test_description, lambda: scenario(driver))
713
714
715 class IndexedDBOfflineTest(ChromeEndureBaseTest):
716 """Long-running performance tests for IndexedDB, modeling offline usage."""
717
718 _WEBAPP_NAME = 'IndexedDBOffline'
719 _TAB_TITLE_SUBSTRING = 'IndexedDB Offline'
720
721 def setUp(self):
722 ChromeEndureBaseTest.setUp(self)
723
724 url = self.GetHttpURLForDataPath('indexeddb', 'endure', 'app.html')
725 self.NavigateToURL(url)
726 loaded_tab_title = self.GetActiveTabTitle()
727 self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title,
728 msg='Loaded tab title does not contain "%s": "%s"' %
729 (self._TAB_TITLE_SUBSTRING, loaded_tab_title))
730
731 self._driver = self.NewWebDriver()
732
733 def testOfflineOnline(self):
734 """Simulates user input while offline and sync while online.
735
736 This test alternates between a simulated "Offline" state (where user
737 input events are queued) and an "Online" state (where user input events
738 are dequeued, sync data is staged, and sync data is unstaged).
739 """
740 test_description = 'OnlineOfflineSync'
741
742 def scenario():
743 # Click the "Online" button and let simulated sync run for 1 second.
744 if not self._ClickElementByXpath(self._driver, 'id("online")'):
745 self._num_errors += 1
746 logging.warning('Logging an automation error: click "online" button.')
747
748 try:
749 self.WaitForDomNode('id("state")[text()="online"]')
750 except (pyauto_errors.JSONInterfaceError,
751 pyauto_errors.JavascriptRuntimeError):
752 self._num_errors += 1
753 logging.warning('Logging an automation error: wait for "online".')
754
755 time.sleep(1)
756
757 # Click the "Offline" button and let user input occur for 1 second.
758 if not self._ClickElementByXpath(self._driver, 'id("offline")'):
759 self._num_errors += 1
760 logging.warning('Logging an automation error: click "offline" button.')
761
762 try:
763 self.WaitForDomNode('id("state")[text()="offline"]')
764 except (pyauto_errors.JSONInterfaceError,
765 pyauto_errors.JavascriptRuntimeError):
766 self._num_errors += 1
767 logging.warning('Logging an automation error: wait for "offline".')
768
769 time.sleep(1)
770
771 self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING,
772 test_description, scenario)
773
774
775 if __name__ == '__main__':
776 pyauto_functional.Main()
OLDNEW
« no previous file with comments | « chrome/test/functional/perf/endure_setup.py ('k') | chrome/test/functional/prefetch.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698