Home | History | Annotate | Download | only in functional
      1 #!/usr/bin/env python
      2 # Copyright (c) 2011 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 import os
      7 import sys
      8 import time
      9 
     10 import pyauto_functional  # Must be imported before pyauto
     11 import pyauto
     12 import test_utils
     13 
     14 
     15 class MemoryTest(pyauto.PyUITest):
     16   """Tests for memory usage of Chrome-related processes.
     17 
     18   These tests are meant to be used manually, not as part of the continuous
     19   test cycle.  This is because each test starts up and periodically
     20   measures/records the memory usage of a relevant Chrome process, doing so
     21   repeatedly until the test is manually killed.  Currently, this script only
     22   works in Linux and ChromeOS, as it uses a Linux shell command to query the
     23   system for process memory usage info (test_utils.GetMemoryUsageOfProcess()).
     24 
     25   The tests in this suite produce the following output files (relative to the
     26   current working directory):
     27 
     28   testTabRendererProcessMemoryUsage: 'renderer_process_mem.txt'
     29   testExtensionProcessMemoryUsage:   'extension_process_mem.txt'
     30   """
     31 
     32   # Constants for all tests in this suite.
     33   NUM_SECONDS_BETWEEN_MEASUREMENTS = 10
     34   MEASUREMENT_LOG_MESSAGE_TEMPLATE = '[%s] %.2f MB (pid: %d)'
     35   LOG_TO_OUTPUT_FILE = True
     36 
     37   # Constants for testTabRendererProcessMemoryUsage.
     38   RENDERER_PROCESS_URL = 'http://chrome.angrybirds.com'
     39   RENDERER_PROCESS_OUTPUT_FILE = 'renderer_process_mem.txt'
     40 
     41   # Constants for testExtensionProcessMemoryUsage.
     42   EXTENSION_LOCATION = os.path.abspath(os.path.join(
     43       pyauto.PyUITest.DataDir(), 'extensions', 'google_talk.crx'))
     44   EXTENSION_PROCESS_NAME = 'Google Talk'
     45   EXTENSION_PROCESS_OUTPUT_FILE = 'extension_process_mem.txt'
     46 
     47   def _GetPidOfExtensionProcessByName(self, name):
     48     """Identifies the process ID of an extension process, given its name.
     49 
     50     Args:
     51       name: The string name of an extension process, as returned by the function
     52             GetBrowserInfo().
     53 
     54     Returns:
     55       The integer process identifier (PID) for the specified process, or
     56       None if the PID cannot be identified.
     57     """
     58     info = self.GetBrowserInfo()['extension_views']
     59     pid = [x['pid'] for x in info if x['name'] == '%s' % name]
     60     if pid:
     61       return pid[0]
     62     return None
     63 
     64   def _LogMessage(self, log_file, msg):
     65     """Logs a message to the screen, and to a log file if necessary.
     66 
     67     Args:
     68       log_file: The string name of a log file to which to write.
     69       msg: The message to log.
     70     """
     71     print msg
     72     sys.stdout.flush()
     73     if self.LOG_TO_OUTPUT_FILE:
     74       print >>open(log_file, 'a'), msg
     75 
     76   def testTabRendererProcessMemoryUsage(self):
     77     """Test the memory usage of the renderer process for a tab.
     78 
     79     This test periodically queries the system for the current memory usage
     80     of a tab's renderer process.  The test will take measurements forever; you
     81     must manually kill the test to terminate it.
     82     """
     83     if (self.LOG_TO_OUTPUT_FILE and
     84         os.path.exists(self.RENDERER_PROCESS_OUTPUT_FILE)):
     85       os.remove(self.RENDERER_PROCESS_OUTPUT_FILE)
     86     self.NavigateToURL(self.RENDERER_PROCESS_URL)
     87     self._LogMessage(
     88         self.RENDERER_PROCESS_OUTPUT_FILE,
     89         'Memory usage for renderer process of a tab navigated to: "%s"' % (
     90             self.RENDERER_PROCESS_URL))
     91 
     92     # A user must manually kill this test to terminate the following loop.
     93     while True:
     94       pid = self.GetBrowserInfo()['windows'][0]['tabs'][0]['renderer_pid']
     95       usage = test_utils.GetMemoryUsageOfProcess(pid)
     96       current_time = time.asctime(time.localtime(time.time()))
     97       self._LogMessage(
     98           self.RENDERER_PROCESS_OUTPUT_FILE,
     99           self.MEASUREMENT_LOG_MESSAGE_TEMPLATE % (current_time, usage, pid))
    100       time.sleep(self.NUM_SECONDS_BETWEEN_MEASUREMENTS)
    101 
    102   def testExtensionProcessMemoryUsage(self):
    103     """Test the memory usage of an extension process.
    104 
    105     This test periodically queries the system for the current memory usage
    106     of an extension process.  The test will take measurements forever; you
    107     must manually kill the test to terminate it.
    108     """
    109     if (self.LOG_TO_OUTPUT_FILE and
    110         os.path.exists(self.EXTENSION_PROCESS_OUTPUT_FILE)):
    111       os.remove(self.EXTENSION_PROCESS_OUTPUT_FILE)
    112     self.InstallExtension(self.EXTENSION_LOCATION)
    113     # The PID is 0 until the extension has a chance to start up.
    114     self.WaitUntil(
    115         lambda: self._GetPidOfExtensionProcessByName(
    116                     self.EXTENSION_PROCESS_NAME) not in [0, None])
    117     self._LogMessage(
    118         self.EXTENSION_PROCESS_OUTPUT_FILE,
    119         'Memory usage for extension process with name: "%s"' % (
    120             self.EXTENSION_PROCESS_NAME))
    121 
    122     # A user must manually kill this test to terminate the following loop.
    123     while True:
    124       pid = self._GetPidOfExtensionProcessByName(self.EXTENSION_PROCESS_NAME)
    125       usage = test_utils.GetMemoryUsageOfProcess(pid)
    126       current_time = time.asctime(time.localtime(time.time()))
    127       self._LogMessage(
    128           self.EXTENSION_PROCESS_OUTPUT_FILE,
    129           self.MEASUREMENT_LOG_MESSAGE_TEMPLATE % (current_time, usage, pid))
    130       time.sleep(self.NUM_SECONDS_BETWEEN_MEASUREMENTS)
    131 
    132 
    133 if __name__ == '__main__':
    134   pyauto_functional.Main()
    135