chromium/android_webview/tools/run_simpleperf_test.py

#!/usr/bin/env vpython3
#
# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Unit tests for run_simpleperf.py"""

import os
import unittest

import mock  # pylint: disable=import-error

from run_simpleperf import SimplePerfRunner
from run_simpleperf import StackAddressInterpreter

_EXAMPLE_WEBVIEW_PACKAGE_NAME = "com.google.android.webview"

_EXAMPLE_STACK_SCRIPT_INPUT = [
    ("11-15 00:00:00.000 11111 11111 E chromium: #00 0x0000001111111111 "
     "/data/app/com.google.android.webview--8E2vMMZTpLVeEKY7ZgoHQ=="
     "/lib/arm64/libwebviewchromium.so+0x00000000083a4db8"),
    ("11-15 00:00:00.000 11111 11111 E chromium: #00 0x0000001111111111 "
     "/data/app/com.google.android.webview--8E2vMMZTpLVeEKY7ZgoHQ=="
     "/lib/arm64/libwebviewchromium.so+0x00000000083db114"),
    ("11-15 00:00:00.000 11111 11111 E chromium: #00 0x0000001111111111 "
     "/data/app/com.google.android.webview--8E2vMMZTpLVeEKY7ZgoHQ=="
     "/lib/arm64/libwebviewchromium.so+0x000000000abcdef0")]

_EXAMPLE_STACK_SCRIPT_OUTPUT = [
    "Stack Trace:",
    "  RELADDR   FUNCTION",
    ("  00000000083a4db8  mojo::core::ports::(anonymous namespace)::UpdateTLS("
     "mojo::core::ports::PortLocker*, mojo::core::ports::PortLocker*)  "
     "../../mojo/core/ports/port_locker.cc:26:3"),
    "",
    "-----------------------------------------------------",
    "",
    "Stack Trace:",
    "  RELADDR   FUNCTION",
    ("  00000000083db114  viz::GLRenderer::SetUseProgram(viz::ProgramKey "
     "const&, gfx::ColorSpace const&, gfx::ColorSpace const&)  "
     "../../components/viz/service/display/gl_renderer.cc:3267:14"),
    "",
    "-----------------------------------------------------"]

_ADDRESSES = ['83a4db8', '83db114', 'abcdef0']  # 3rd one is ignored
_WEBVIEW_LIB_NAME = 'libwebviewchromium.so'
_WEBVIEW_LIB_PATH = (
    '/data/app/com.google.android.webview'
    '--8E2vMMZTpLVeEKY7ZgoHQ==/lib/arm64/libwebviewchromium.so')

_EXAMPLE_INTERPRETER_OUTPUT = [
    ('83a4db8',
     ('mojo::core::ports::(anonymous namespace)::UpdateTLS('
      'mojo::core::ports::PortLocker*, mojo::core::ports::PortLocker*)')),
    ('83db114',
     ('viz::GLRenderer::SetUseProgram(viz::ProgramKey const&, '
      'gfx::ColorSpace const&, gfx::ColorSpace const&)'))]

_EXAMPLE_INTERPRETER_OUTPUT_WITH_FILE_NAME_LINE = [
    ('83a4db8',
     ('mojo::core::ports::(anonymous namespace)::UpdateTLS('
      'mojo::core::ports::PortLocker*, mojo::core::ports::PortLocker*)'
      ' | ../../mojo/core/ports/port_locker.cc:26:3')),
    ('83db114',
     ('viz::GLRenderer::SetUseProgram(viz::ProgramKey const&, '
      'gfx::ColorSpace const&, gfx::ColorSpace const&)'
      ' | ../../components/viz/service/display/gl_renderer.cc:3267:14'))]

_MOCK_ORIGINAL_REPORT = [
    '"442": {"l": 28, "f": "libwebviewchromium.so[+3db7d84]"},',
    '"443": {"l": 28, "f": "libwebviewchromium.so[+3db7a5c]"},',
    '"444": {"l": 28, "f": "libwebviewchromium.so[+aaaaaaa]"},'
]

_MOCK_ADDRESSES = ['3db7d84', '3db7a5c', 'aaaaaaa']

_MOCK_ADDRESS_FUNCTION_NAME_PAIRS = [
    ('3db7d84', 'MyClass::FirstMethod(const char*)'),
    ('3db7a5c', 'MyClass::SecondMethod(int)')]

_MOCK_FINAL_REPORT = [
    ('"442": {"l": 28, "f": "libwebviewchromium.so[MyClass::'
     'FirstMethod(const char*)]"},'),
    ('"443": {"l": 28, "f": "libwebviewchromium.so[MyClass::'
     'SecondMethod(int)]"},'),
    ('"444": {"l": 28, "f": "libwebviewchromium.so[+aaaaaaa]"},')
]


class _RunSimpleperfTest(unittest.TestCase):
  """Unit tests for the run_simpleperf module. """

  def _AssertFileLines(self, mock_open, expected_lines):
    handle = mock_open()
    # Get 'str1', 'str2', ... from the call to f.write(str_i + '\n') which is
    # saved as [(str1 + '\n'), (str2 + '\n'), ...].
    actual_lines = [args[0][:-1] for (args, _) in
                    handle.write.call_args_list]
    self.assertEqual(expected_lines, actual_lines)

  def setUp(self):
    self.tmp_dir = '/tmp' # the actual directory won't be used in this test.
    self.args = mock.Mock(
        report_path=os.path.join(self.tmp_dir, 'report.html'),
        show_file_line=False)
    self.device = mock.Mock()

    self.stack_address_interpreter = StackAddressInterpreter(self.args,
                                                             self.tmp_dir)
    self.simple_perf_runner = SimplePerfRunner(
        self.device, self.args, self.tmp_dir, self.stack_address_interpreter)

  @mock.patch('run_simpleperf.open', new_callable=mock.mock_open)
  def testStackAddressInterpreter(self, mock_open):
    StackAddressInterpreter.RunStackScript = mock.Mock(
        return_value=_EXAMPLE_STACK_SCRIPT_OUTPUT)
    self.assertEqual(
        _EXAMPLE_INTERPRETER_OUTPUT,
        self.stack_address_interpreter.Interpret(_ADDRESSES, _WEBVIEW_LIB_PATH))
    self._AssertFileLines(mock_open, _EXAMPLE_STACK_SCRIPT_INPUT)

  @mock.patch('run_simpleperf.open', new_callable=mock.mock_open)
  def testStackAddressInterpreterWithFileNameLine(self, mock_open):
    self.args.show_file_line = True
    StackAddressInterpreter.RunStackScript = mock.Mock(
        return_value=_EXAMPLE_STACK_SCRIPT_OUTPUT)
    self.assertEqual(
        _EXAMPLE_INTERPRETER_OUTPUT_WITH_FILE_NAME_LINE,
        self.stack_address_interpreter.Interpret(_ADDRESSES, _WEBVIEW_LIB_PATH))
    self._AssertFileLines(mock_open, _EXAMPLE_STACK_SCRIPT_INPUT)

  def testSimplePerfRunner_CollectAddresses(self):
    addresses = self.simple_perf_runner.CollectAddresses(
        _MOCK_ORIGINAL_REPORT, 'libwebviewchromium.so')
    self.assertEqual(set(_MOCK_ADDRESSES), addresses)

  def testSimplePerfRunner_ReplaceAddresses(self):
    postprocessed_report = (
        self.simple_perf_runner.ReplaceAddressesWithFunctionInfos(
            _MOCK_ORIGINAL_REPORT, _MOCK_ADDRESS_FUNCTION_NAME_PAIRS,
            'libwebviewchromium.so'))
    self.assertEqual(_MOCK_FINAL_REPORT, postprocessed_report)

  @mock.patch('run_simpleperf.open', new_callable=mock.mock_open)
  def testSimplePerfRunner_Run(self, mock_open):
    self.stack_address_interpreter.Interpret = mock.Mock(
        return_value=_MOCK_ADDRESS_FUNCTION_NAME_PAIRS)

    SimplePerfRunner.RunSimplePerf = mock.Mock()
    SimplePerfRunner.RunPackageCompile = mock.Mock()
    SimplePerfRunner.GetOriginalReportHtml = mock.Mock(
        return_value=_MOCK_ORIGINAL_REPORT)
    self.simple_perf_runner.GetCurrentWebViewProvider = mock.Mock(
        return_value=_EXAMPLE_WEBVIEW_PACKAGE_NAME)

    self.simple_perf_runner.GetWebViewLibraryNameAndPath = mock.Mock(
        return_value=(_WEBVIEW_LIB_NAME, _WEBVIEW_LIB_PATH))

    self.simple_perf_runner.Run()

    self._AssertFileLines(mock_open, _MOCK_FINAL_REPORT)


if __name__ == '__main__':
  unittest.main()