# 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.
import os
import shutil
import tempfile
import unittest
from unittest import mock
from contrib.power import profiling_util
class MockPopen(object):
"""Helper class for unit tests that mock subprocess.Popen."""
def __init__(self, return_code, stdout=None, stderr=None):
self.return_code = return_code
self._stdout = stdout
self._stderr = stderr
def communicate(self):
return self._stdout, self._stderr
class ProfilingUtilTests(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(ProfilingUtilTests, self).__init__(*args, **kwargs)
# Working directory for testing.
self._temp_directory = None
# Placeholder paths used for testing.
self._trace_path = None
self._symbols_path = None
self._profile_file_name = None
# The directory where the profile is generated.
self._profile_source_dir = None
# The directory where the profile should eventually be copied to.
self._profile_path = None
def setUp(self):
self._temp_directory = tempfile.mkdtemp()
self.addCleanup(self.cleanup)
self._trace_path = os.path.join(self._temp_directory, 'trace')
with open(self._trace_path, 'w') as trace_file:
trace_file.write('placeholder_trace')
self._symbols_path = os.path.join(self._temp_directory, 'symbols')
self._profile_file_name = 'pprof'
# This needs to include 'perf_profile-', since this is what `traceconv`
# outputs, too.
self._profile_source_dir = os.path.join(self._temp_directory,
'perf_profile-dir')
self._profile_path = os.path.join(self._temp_directory,
self._profile_file_name)
os.environ['PERFETTO_BINARY_PATH'] = 'placeholder_binary_path'
def cleanup(self):
shutil.rmtree(self._temp_directory)
os.environ.pop('PERFETTO_BINARY_PATH', None)
# *args and **kwargs are needed for the mock to be able to accept extra
# arguments.
# pylint: disable=unused-argument
def writePlaceholderProfile(self, *args, **kwargs):
os.makedirs(self._profile_source_dir)
with open(os.path.join(self._profile_source_dir, self._profile_file_name),
'w') as profile_file:
profile_file.write('placeholder_pprof')
# Return output needs to include self._profile_source_dir, since this is
# what `traceconv` does, too.
return MockPopen(return_code=0,
stdout='Outputting to {}'.format(
self._profile_source_dir).encode('utf-8'))
@mock.patch('contrib.power.profiling_util.logging.warning')
def testSymbolizeTraceWithoutBinaryPath(self, mock_warning):
os.environ.pop('PERFETTO_BINARY_PATH', None)
profiling_util.SymbolizeTrace(self._trace_path)
mock_warning.assert_called()
@mock.patch('contrib.power.profiling_util.logging.error')
@mock.patch('contrib.power.profiling_util.subprocess.Popen')
def testSymbolizeTraceWithTraceconvError(self, mock_popen, mock_error):
mock_popen.return_value = MockPopen(
return_code=-1, stderr='placeholder_error'.encode('utf-8'))
profiling_util.SymbolizeTrace(self._trace_path)
mock_error.assert_called()
@mock.patch('contrib.power.profiling_util.subprocess.Popen')
def testSymbolizeTraceWithTraceconvSuccess(self, mock_popen):
mock_popen.return_value = MockPopen(
return_code=0, stdout='placeholder_symbols'.encode('utf-8'))
profiling_util.SymbolizeTrace(self._trace_path)
with open(self._trace_path, 'r') as trace_file:
self.assertEqual(trace_file.read(),
'placeholder_traceplaceholder_symbols')
@mock.patch('contrib.power.profiling_util.logging.error')
@mock.patch('contrib.power.profiling_util.subprocess.Popen')
def testGenerateProfilesWithTraceconvError(self, mock_popen, mock_error):
mock_popen.return_value = MockPopen(
return_code=-1, stderr='placeholder_error'.encode('utf-8'))
profiling_util.GenerateProfiles(self._trace_path)
mock_error.assert_called()
@mock.patch('contrib.power.profiling_util.logging.error')
@mock.patch('contrib.power.profiling_util.subprocess.Popen')
def testGenerateProfilesWithInvalidTraceconvOutput(self, mock_popen,
mock_error):
mock_popen.return_value = MockPopen(
return_code=0, stdout='placeholder_output'.encode('utf-8'))
profiling_util.GenerateProfiles(self._trace_path)
mock_error.assert_called()
@mock.patch('contrib.power.profiling_util.subprocess.Popen')
def testGenerateProfilesWithTraceconvSuccess(self, mock_popen):
mock_popen.side_effect = self.writePlaceholderProfile
profiling_util.GenerateProfiles(self._trace_path)
with open(self._profile_path, 'r') as profile_file:
self.assertEqual(profile_file.read(), 'placeholder_pprof')