summaryrefslogtreecommitdiff
path: root/buildscripts
diff options
context:
space:
mode:
authorJonathan Abrahams <jonathan@mongodb.com>2019-04-12 14:16:14 -0400
committerJonathan Abrahams <jonathan@mongodb.com>2019-04-15 11:50:35 -0400
commiteb05fd56aadd4acd4197fdee209dc3ad2b77d495 (patch)
treea0146439e57a7d664b52e0b2521a9a62d9910892 /buildscripts
parent688948927bedd25c4c5c50bc7e6a253feeec4e25 (diff)
downloadmongo-eb05fd56aadd4acd4197fdee209dc3ad2b77d495.tar.gz
SERVER-40418 Refactor test_adb_monitor; add python2 for systrace.py for adb_monitor
Diffstat (limited to 'buildscripts')
-rw-r--r--buildscripts/mobile/adb_monitor.py290
-rw-r--r--buildscripts/tests/mobile/test_adb_monitor.py945
2 files changed, 990 insertions, 245 deletions
diff --git a/buildscripts/mobile/adb_monitor.py b/buildscripts/mobile/adb_monitor.py
index ac6b1042e22..7f535c7c768 100644
--- a/buildscripts/mobile/adb_monitor.py
+++ b/buildscripts/mobile/adb_monitor.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
"""ADB utilities to collect adb samples from a locally connected Android device."""
+import argparse
import distutils.spawn # pylint: disable=no-name-in-module
import logging
-import optparse
import os
import pipes
import re
@@ -22,34 +22,138 @@ if __name__ == "__main__" and __package__ is None:
from buildscripts.util import fileops
from buildscripts.util import runcommand
+# Default program options.
+DEFAULT_ADB_BINARY = "adb"
+DEFAULT_BATTERY_FILE = "battery.csv"
+DEFAULT_CPU_FILE = "cpu.json"
+DEFAULT_NUM_SAMPLES = 0
+DEFAULT_LOG_LEVEL = "info"
+DEFAULT_MEMORY_FILE = "memory.csv"
+DEFAULT_PYTHON27 = "python2"
+DEFAULT_SAMPLE_INTERVAL_MS = 500
+
+LOG_LEVELS = ["debug", "error", "info", "warning"]
+
# Initialize the global logger.
LOGGER = logging.getLogger(__name__)
+def parse_command_line():
+ """Parse command line options.
+
+ :return: Argparser object.
+ """
+ parser = argparse.ArgumentParser()
+
+ program_options = parser.add_argument_group("Program Options")
+ battery_options = parser.add_argument_group("Battery Options")
+ memory_options = parser.add_argument_group("Memory Options")
+ systrace_options = parser.add_argument_group("Systrace Options")
+
+ program_options.add_argument(
+ "--adbBinary", dest="adb_binary",
+ help="The path for adb. Defaults to '%(default)s', which is in $PATH.",
+ default=DEFAULT_ADB_BINARY)
+
+ program_options.add_argument(
+ "--python27", dest="python27",
+ help="The path for python2.7, required by systrace. Defaults to '%(default)s', which is in"
+ " $PATH.", default=DEFAULT_PYTHON27)
+
+ program_options.add_argument(
+ "--samples", dest="num_samples",
+ help="Number of samples to collect, 0 indicates infinite. [Default: %(default)d]", type=int,
+ default=DEFAULT_NUM_SAMPLES)
+
+ program_options.add_argument(
+ "--collectionTime", dest="collection_time_secs",
+ help="Time in seconds to collect samples, if specifed overrides '--samples'.", type=int,
+ default=None)
+
+ program_options.add_argument(
+ "--sampleIntervalMs", dest="sample_interval_ms",
+ help="Time in milliseconds between collecting a sample. [Default: %(default)d]", type=int,
+ default=DEFAULT_SAMPLE_INTERVAL_MS)
+
+ program_options.add_argument(
+ "--logLevel", dest="log_level", choices=LOG_LEVELS,
+ help=f"The log level. Accepted values are: {LOG_LEVELS}. [default: '%(default)s'].",
+ default=DEFAULT_LOG_LEVEL)
+
+ battery_options.add_argument(
+ "--batteryFile", dest="battery_file",
+ help="The destination file for battery stats (CSV format). [Default: %(default)s].",
+ default=DEFAULT_BATTERY_FILE)
+
+ battery_options.add_argument("--noBattery", dest="battery_file",
+ help="Disable collection of battery samples.",
+ action="store_const", const=None)
+
+ memory_options.add_argument(
+ "--memoryFile", dest="memory_file",
+ help="The destination file for memory stats (CSV format). [Default: %(default)s].",
+ default=DEFAULT_MEMORY_FILE)
+
+ memory_options.add_argument("--noMemory", dest="memory_file",
+ help="Disable collection of memory samples.", action="store_const",
+ const=None)
+
+ systrace_options.add_argument(
+ "--cpuFile", dest="cpu_file",
+ help="The destination file for CPU stats (JSON format). [Default: %(default)s].",
+ default=DEFAULT_CPU_FILE)
+
+ systrace_options.add_argument("--noCpu", dest="cpu_file",
+ help="Disable collection of CPU samples.", action="store_const",
+ const=None)
+
+ return parser
+
+
+def create_files_mtime(files):
+ """Create dict of file names and it's modified time.
+
+ param files: List file names.
+ return: Dict of file names with value of the file's modified time.
+ """
+ return {file_name: fileops.getmtime(file_name) for file_name in files if file_name}
+
+
+def find_executable(binary_file):
+ """Find if binary_file exists in $PATH. Raise exception if it cannot be found.
+
+ param binary_file: Name of binary to find.
+ return: Full path of binary_file.
+ """
+ binary_path = distutils.spawn.find_executable(binary_file)
+ if not binary_path:
+ raise EnvironmentError(f"Executable '{binary_file}' does not exist or is not in the PATH.")
+ return binary_path
+
+
class Adb(object):
"""Class to abstract calls to adb."""
- def __init__(self, adb_binary="adb", logger=LOGGER):
+ def __init__(self, adb_binary=DEFAULT_ADB_BINARY, logger=LOGGER, python27=DEFAULT_PYTHON27):
"""Initialize the Adb object."""
self._cmd = None
+ self._tempfile = None
self.logger = logger
- self.adb_path = distutils.spawn.find_executable(adb_binary)
- if not self.adb_path:
- raise EnvironmentError(
- "Executable '{}' does not exist or is not in the PATH.".format(adb_binary))
+ self.python27 = find_executable(python27)
+ adb_path = find_executable(adb_binary)
# We support specifying a path the adb binary to use; however, systrace.py only
# knows how to find it using the PATH environment variable. It is possible that
# 'adb_binary' is an absolute path specified by the user, so we add its parent
# directory to the PATH manually.
- adb_dir = os.path.dirname(self.adb_path)
- os.environ["PATH"] = "{}{}{}".format(os.environ["PATH"], os.path.pathsep, adb_dir)
+ adb_dir = os.path.dirname(adb_path)
+ if adb_dir:
+ os.environ["PATH"] = "{}{}{}".format(os.environ["PATH"], os.path.pathsep, adb_dir)
# systrace.py should be in <adb_dir>/systrace/systrace.py
self.systrace_script = os.path.join(adb_dir, "systrace", "systrace.py")
if not os.path.isfile(self.systrace_script):
raise EnvironmentError("Script '{}' cannot be found.".format(self.systrace_script))
- self._tempfile = None
@staticmethod
def adb_cmd(adb_command, output_file=None, append_file=False, output_string=False):
@@ -108,13 +212,13 @@ class Adb(object):
def _battery_cmd(self, option, output_file=None, append_file=False):
self.adb_cmd("shell dumpsys batterystats {}".format(option), output_file, append_file)
- def battery(self, reset=False, output_file=None, append_file=False):
+ def battery(self, output_file, append_file=False, reset=False):
"""Collect the battery stats and save to the output_file."""
if reset:
self._battery_cmd("--reset")
self._battery_cmd("--checkin", output_file, append_file)
- def memory(self, output_file=None, append_file=False):
+ def memory(self, output_file, append_file=False):
"""Collect the memory stats and save to the output_file."""
self.adb_cmd("shell dumpsys meminfo -c -d", output_file, append_file)
@@ -122,7 +226,8 @@ class Adb(object):
"""Start the systrace.py script to collect CPU usage."""
self._tempfile = tempfile.NamedTemporaryFile(delete=False).name
self._cmd = runcommand.RunCommand(output_file=self._tempfile, propagate_signals=False)
- self._cmd.add_file(sys.executable)
+ # systrace.py currently only supports python 2.7.
+ self._cmd.add_file(self.python27)
self._cmd.add_file(self.systrace_script)
self._cmd.add("--json")
self._cmd.add("-o")
@@ -150,15 +255,16 @@ class AdbControl(object): # pylint: disable=too-many-instance-attributes
def __init__( # pylint: disable=too-many-arguments
self, adb, logger=LOGGER, battery_file=None, memory_file=None, cpu_file=None,
- append_file=False, num_samples=0, collection_time_secs=0, sample_interval_ms=0):
+ append_file=False, num_samples=DEFAULT_NUM_SAMPLES, collection_time_secs=None,
+ sample_interval_ms=DEFAULT_SAMPLE_INTERVAL_MS):
"""Initialize AdbControl object."""
self.adb = adb
self.logger = logger
- output_files = [battery_file, memory_file, cpu_file]
- if not any(output_files):
+ output_files = [fn for fn in [battery_file, memory_file, cpu_file] if fn]
+ if not output_files:
raise ValueError("There are no collection sample files selected.")
self.battery_file = battery_file
self.memory_file = memory_file
@@ -171,44 +277,43 @@ class AdbControl(object): # pylint: disable=too-many-instance-attributes
if not append_file:
fileops.create_empty(output_file)
- self.append_file = append_file
# collection_time_secs overrides num_samples
- self.num_samples = num_samples if collection_time_secs == 0 else 0
+ self.num_samples = num_samples if not collection_time_secs else 0
self.collection_time_secs = collection_time_secs
self.sample_interval_ms = sample_interval_ms
- self.should_stop = threading.Event()
- self.should_stop.clear()
- self.sample_based_threads = []
- self.all_threads = []
+ self._should_stop = threading.Event()
+ self._should_stop.clear()
+ self._sample_based_threads = []
+ self._all_threads = []
def start(self):
"""Start adb sample collection."""
if self.cpu_file:
- monitor = AdbContinuousResourceMonitor(self.cpu_file, self.should_stop,
+ monitor = AdbContinuousResourceMonitor(self.cpu_file, self._should_stop,
self.adb.systrace_start, self.adb.systrace_stop)
- self.all_threads.append(monitor)
+ self._all_threads.append(monitor)
monitor.start()
if self.battery_file:
- monitor = AdbSampleBasedResourceMonitor(self.battery_file, self.should_stop,
+ monitor = AdbSampleBasedResourceMonitor(self.battery_file, self._should_stop,
self.adb.battery, self.num_samples,
self.sample_interval_ms)
- self.sample_based_threads.append(monitor)
- self.all_threads.append(monitor)
+ self._sample_based_threads.append(monitor)
+ self._all_threads.append(monitor)
monitor.start()
if self.memory_file:
- monitor = AdbSampleBasedResourceMonitor(self.memory_file, self.should_stop,
+ monitor = AdbSampleBasedResourceMonitor(self.memory_file, self._should_stop,
self.adb.memory, self.num_samples,
self.sample_interval_ms)
- self.sample_based_threads.append(monitor)
- self.all_threads.append(monitor)
+ self._sample_based_threads.append(monitor)
+ self._all_threads.append(monitor)
monitor.start()
def stop(self):
"""Stop adb sample collection."""
- self.should_stop.set()
+ self._should_stop.set()
self.wait()
def wait(self):
@@ -216,10 +321,10 @@ class AdbControl(object): # pylint: disable=too-many-instance-attributes
try:
# We either wait for the specified amount of time or for the sample-based monitors
# to have collected the specified number of samples.
- if self.collection_time_secs > 0:
- self.should_stop.wait(self.collection_time_secs)
+ if self.collection_time_secs:
+ self._should_stop.wait(self.collection_time_secs)
else:
- for thread in self.sample_based_threads:
+ for thread in self._sample_based_threads:
# We must specify a timeout to threading.Thread.join() to ensure that the
# wait is interruptible. The main thread would otherwise never be able to
# receive a KeyboardInterrupt.
@@ -229,17 +334,17 @@ class AdbControl(object): # pylint: disable=too-many-instance-attributes
# that they should exit as quickly as they can.
pass
finally:
- self.should_stop.set()
+ self._should_stop.set()
# Wait for all of the monitor threads to exit, by specifying a timeout to
# threading.Thread.join() in case the user tries to interrupt the script again.
- for thread in self.all_threads:
+ for thread in self._all_threads:
thread.join(self._JOIN_TIMEOUT)
self.logger.info("Collections stopped.")
# If any of the monitor threads encountered an error, then reraise the exception in the
# main thread.
- for thread in self.all_threads:
+ for thread in self._all_threads:
if thread.exception is not None:
raise thread.exception
@@ -322,99 +427,42 @@ class AdbContinuousResourceMonitor(AdbResourceMonitor):
self._adb_stop_cmd(output_file=self._output_file)
-def main(): #pylint: disable=too-many-statements
+def monitor_device(adb_control, files_mtime):
+ """Run monitoring on device and collect results.
+ param adb_control: AdbControl object.
+ param files_mtime: Dict of files with their modified time.
+ """
+ adb_control.start()
+ try:
+ adb_control.wait()
+ finally:
+ files_saved = [
+ path for path in files_mtime
+ if fileops.getmtime(path) > files_mtime[path] and not fileops.is_empty(path)
+ ]
+ LOGGER.info("Files saved: %s", files_saved)
+
+
+def main():
"""Execute Main program."""
logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s", level=logging.INFO)
logging.Formatter.converter = time.gmtime
- parser = optparse.OptionParser()
-
- program_options = optparse.OptionGroup(parser, "Program Options")
- battery_options = optparse.OptionGroup(parser, "Battery Options")
- memory_options = optparse.OptionGroup(parser, "Memory Options")
- systrace_options = optparse.OptionGroup(parser, "Systrace Options")
-
- program_options.add_option("--adbBinary", dest="adb_binary",
- help="The path for adb. Defaults to '%default', which is in $PATH.",
- default="adb")
-
- program_options.add_option(
- "--samples", dest="num_samples",
- help="Number of samples to collect, 0 indicates infinite. [Default: %default]", type=int,
- default=0)
-
- program_options.add_option(
- "--collectionTime", dest="collection_time_secs",
- help="Time in seconds to collect samples, if specifed overrides '--samples'.", type=int,
- default=0)
-
- program_options.add_option(
- "--sampleIntervalMs", dest="sample_interval_ms",
- help="Time in milliseconds between collecting a sample. [Default: %default]", type=int,
- default=500)
-
- log_levels = ["debug", "error", "info", "warning"]
- program_options.add_option(
- "--logLevel", dest="log_level", choices=log_levels,
- help="The log level. Accepted values are: {}. [default: '%default'].".format(log_levels),
- default="info")
+ parser = parse_command_line()
+ options = parser.parse_args()
- battery_options.add_option(
- "--batteryFile", dest="battery_file",
- help="The destination file for battery stats (CSV format). [Default: %default].",
- default="battery.csv")
-
- battery_options.add_option("--noBattery", dest="no_battery",
- help="Disable collection of battery samples.", action="store_true",
- default=False)
-
- memory_options.add_option(
- "--memoryFile", dest="memory_file",
- help="The destination file for memory stats (CSV format). [Default: %default].",
- default="memory.csv")
-
- memory_options.add_option("--noMemory", dest="no_memory",
- help="Disable collection of memory samples.", action="store_true",
- default=False)
-
- systrace_options.add_option(
- "--cpuFile", dest="cpu_file",
- help="The destination file for CPU stats (JSON format). [Default: %default].",
- default="cpu.json")
-
- systrace_options.add_option("--noCpu", dest="no_cpu", help="Disable collection of CPU samples.",
- action="store_true", default=False)
-
- parser.add_option_group(program_options)
- parser.add_option_group(battery_options)
- parser.add_option_group(memory_options)
- parser.add_option_group(systrace_options)
+ files_mtime = create_files_mtime([options.battery_file, options.memory_file, options.cpu_file])
- options, _ = parser.parse_args()
-
- output_files = {}
- if options.no_battery:
- options.battery_file = None
- else:
- output_files[options.battery_file] = fileops.getmtime(options.battery_file)
-
- if options.no_memory:
- options.memory_file = None
- else:
- output_files[options.memory_file] = fileops.getmtime(options.memory_file)
-
- if options.no_cpu:
- options.cpu_file = None
- else:
- output_files[options.cpu_file] = fileops.getmtime(options.cpu_file)
+ if not files_mtime:
+ parser.error("Must specify one ouptut file")
LOGGER.setLevel(options.log_level.upper())
LOGGER.info(
"This program can be cleanly terminated by issuing the following command:"
"\n\t\t'kill -INT %d'", os.getpid())
- adb = Adb(options.adb_binary)
+ adb = Adb(adb_binary=options.adb_binary, python27=options.python27)
LOGGER.info("Detected devices by adb:\n%s%s", adb.devices(), adb.device_available())
adb_control = AdbControl(adb=adb, battery_file=options.battery_file,
@@ -423,15 +471,7 @@ def main(): #pylint: disable=too-many-statements
collection_time_secs=options.collection_time_secs,
sample_interval_ms=options.sample_interval_ms)
- adb_control.start()
- try:
- adb_control.wait()
- finally:
- files_saved = []
- for path in output_files:
- if fileops.getmtime(path) > output_files[path] and not fileops.is_empty(path):
- files_saved.append(path)
- LOGGER.info("Files saved: %s", files_saved)
+ monitor_device(adb_control, files_mtime)
if __name__ == "__main__":
diff --git a/buildscripts/tests/mobile/test_adb_monitor.py b/buildscripts/tests/mobile/test_adb_monitor.py
index 610e3e10aae..286593fbc89 100644
--- a/buildscripts/tests/mobile/test_adb_monitor.py
+++ b/buildscripts/tests/mobile/test_adb_monitor.py
@@ -1,134 +1,839 @@
""" Unit tests for adb_monitor. """
-import distutils.spawn # pylint: disable=no-name-in-module
import os
-import shutil
-import sys
-import tempfile
import unittest
+from unittest.mock import MagicMock, mock_open, patch
+
import buildscripts.mobile.adb_monitor as adb_monitor
-_IS_WINDOWS = sys.platform == "win32" or sys.platform == "cygwin"
-
-if _IS_WINDOWS:
- import win32file
-
-# pylint: disable=missing-docstring,protected-access
-
-
-def mock_adb_and_systrace(directory):
- """Mock adb and systrace.py."""
- # Create mock 'adb', which is really 'echo'.
- adb_binary = os.path.join(directory, "adb")
- echo_binary = distutils.spawn.find_executable("echo")
- if _IS_WINDOWS:
- adb_binary = "{}.exe".format(adb_binary)
- shutil.copyfile(echo_binary, adb_binary)
- else:
- os.symlink(echo_binary, adb_binary)
- os.environ["PATH"] = "{}{}{}".format(directory, os.path.pathsep, os.environ["PATH"])
-
- # Create mock 'systrace.py'.
- systrace_dir = os.path.join(directory, "systrace")
- os.mkdir(systrace_dir)
- systrace = os.path.join(systrace_dir, "systrace.py")
- with open(systrace, "w") as fh:
- fh.write("import optparse\n")
- fh.write("input('waiting...')\n")
- fh.write("print('Wrote trace')\n")
- fh.write("parser = optparse.OptionParser()\n")
- fh.write("parser.add_option('-o', dest='output_file')\n")
- fh.write("parser.add_option('--json', dest='json_opts', action='store_true')\n")
- fh.write("options, args = parser.parse_args()\n")
- fh.write("with open(options.output_file, 'w') as fh:\n")
- fh.write("\tfh.write('{hello:1}')\n")
-
-
-def file_contents(path):
- with open(path, "r") as fh:
- return fh.read()
-
-
-class AdbTestCase(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- cls.temp_dir = tempfile.mkdtemp()
- mock_adb_and_systrace(cls.temp_dir)
- cls.adb = adb_monitor.Adb()
-
- @classmethod
- def tearDownClass(cls):
- shutil.rmtree(cls.temp_dir)
-
-
-class AdbTest(AdbTestCase):
- def test_bad_adb(self):
- self.assertRaises(EnvironmentError, lambda: adb_monitor.Adb("bad_adb"))
-
- def test_devices(self):
- self.adb.devices()
-
- def test_battery(self):
- temp_file = os.path.join(self.temp_dir, "battery_output")
- self.adb.battery(output_file=temp_file)
- self.assertTrue(os.path.isfile(temp_file))
-
- def test_memory(self):
- temp_file = os.path.join(self.temp_dir, "memory_output")
- self.adb.memory(output_file=temp_file)
- self.assertTrue(os.path.isfile(temp_file))
-
- def test_systrace(self):
- temp_file = os.path.join(self.temp_dir, "systrace_output")
- self.adb.systrace_start(output_file=temp_file)
- self.adb.systrace_stop()
- self.assertTrue(os.path.isfile(temp_file))
-
-
-class AdbControlTestCase(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- cls.temp_dir = tempfile.mkdtemp()
- mock_adb_and_systrace(cls.temp_dir)
- cls.adb = adb_monitor.Adb()
-
- @classmethod
- def tearDownClass(cls):
- shutil.rmtree(cls.temp_dir)
-
-
-class AdbControlTest(AdbControlTestCase):
- def _test_files(self, num_samples=1, collection_time_secs=0, sample_interval_ms=500,
- arg_list=None):
- args = {}
- arg_file_list = []
- for arg_name in arg_list:
- arg_test_file = tempfile.NamedTemporaryFile(dir=self.temp_dir, delete=False).name
- args[arg_name] = arg_test_file
- arg_file_list.append(arg_test_file)
- adb_control = adb_monitor.AdbControl(self.adb, collection_time_secs=collection_time_secs,
- num_samples=num_samples,
- sample_interval_ms=sample_interval_ms, **args)
+ADB_MONITOR = "buildscripts.mobile.adb_monitor"
+
+
+def ns(module):
+ return f"{ADB_MONITOR}.{module}"
+
+
+class TestParseCommandLine(unittest.TestCase):
+ def test_parse_command_line(self):
+ options = adb_monitor.parse_command_line().parse_args([])
+ self.assertEqual(options.adb_binary, adb_monitor.DEFAULT_ADB_BINARY)
+ self.assertEqual(options.python27, adb_monitor.DEFAULT_PYTHON27)
+ self.assertEqual(options.sample_interval_ms, adb_monitor.DEFAULT_SAMPLE_INTERVAL_MS)
+ self.assertEqual(options.log_level, adb_monitor.DEFAULT_LOG_LEVEL)
+ self.assertEqual(options.battery_file, adb_monitor.DEFAULT_BATTERY_FILE)
+ self.assertEqual(options.memory_file, adb_monitor.DEFAULT_MEMORY_FILE)
+ self.assertEqual(options.cpu_file, adb_monitor.DEFAULT_CPU_FILE)
+ self.assertEqual(options.num_samples, adb_monitor.DEFAULT_NUM_SAMPLES)
+ self.assertEqual(options.sample_interval_ms, adb_monitor.DEFAULT_SAMPLE_INTERVAL_MS)
+ self.assertIsNone(options.collection_time_secs)
+
+ def test_parse_command_line_partial_args(self):
+ adb_binary = "myadb"
+ python27 = "mypython2"
+ collection_time_secs = 35
+ battery_file = "mybattery"
+ arg_list = [
+ "--adbBinary", adb_binary, "--python27", python27, "--collectionTime",
+ str(collection_time_secs), "--batteryFile", battery_file, "--noMemory"
+ ]
+ options = adb_monitor.parse_command_line().parse_args(arg_list)
+ self.assertEqual(options.adb_binary, adb_binary)
+ self.assertEqual(options.python27, python27)
+ self.assertEqual(options.collection_time_secs, collection_time_secs)
+ self.assertEqual(options.battery_file, battery_file)
+
+ self.assertEqual(options.sample_interval_ms, adb_monitor.DEFAULT_SAMPLE_INTERVAL_MS)
+ self.assertEqual(options.log_level, adb_monitor.DEFAULT_LOG_LEVEL)
+ self.assertIsNone(options.memory_file)
+ self.assertEqual(options.cpu_file, adb_monitor.DEFAULT_CPU_FILE)
+ self.assertEqual(options.num_samples, adb_monitor.DEFAULT_NUM_SAMPLES)
+ self.assertEqual(options.sample_interval_ms, adb_monitor.DEFAULT_SAMPLE_INTERVAL_MS)
+
+ def test_parse_command_line_no_files(self):
+ arg_list = ["--noBattery", "--noCpu", "--noMemory"]
+ options = adb_monitor.parse_command_line().parse_args(arg_list)
+ self.assertIsNone(options.battery_file)
+ self.assertIsNone(options.cpu_file)
+ self.assertIsNone(options.memory_file)
+
+ def test_parse_command_line_no_battery_first(self):
+ battery_file = "mybattery"
+ arg_list = ["--noBattery", "--batteryFile", battery_file]
+ options = adb_monitor.parse_command_line().parse_args(arg_list)
+ self.assertEqual(options.battery_file, battery_file)
+
+ def test_parse_command_line_no_battery_second(self):
+ battery_file = "mybattery"
+ arg_list = ["--batteryFile", battery_file, "--noBattery"]
+ options = adb_monitor.parse_command_line().parse_args(arg_list)
+ self.assertIsNone(options.battery_file)
+
+
+class TestMonitorDevice(unittest.TestCase):
+ @patch(ns("fileops"), return_value=False)
+ def test_monitor_device(self, mock_fileops):
+ files_mtime = {"file1": 0, "file2": 0}
+ mock_fileops.getmtime.return_value = 10
+ mock_adb_control = MagicMock()
+ adb_monitor.monitor_device(mock_adb_control, files_mtime)
+ mock_adb_control.start.assert_called_once()
+ mock_adb_control.wait.assert_called_once()
+ self.assertEqual(mock_fileops.getmtime.call_count, len(files_mtime))
+ self.assertEqual(mock_fileops.is_empty.call_count, len(files_mtime))
+
+ @patch(ns("fileops"), return_value=True)
+ def test_monitor_device_empty_file(self, mock_fileops):
+ files_mtime = {"file1": 0, "file2": 0}
+ mock_fileops.getmtime.return_value = 10
+ mock_adb_control = MagicMock()
+ adb_monitor.monitor_device(mock_adb_control, files_mtime)
+ mock_adb_control.start.assert_called_once()
+ mock_adb_control.wait.assert_called_once()
+ self.assertEqual(mock_fileops.getmtime.call_count, len(files_mtime))
+ self.assertEqual(mock_fileops.is_empty.call_count, len(files_mtime))
+
+ @patch(ns("fileops"), return_value=False)
+ def test_monitor_device_earlier_mtime(self, mock_fileops):
+ files_mtime = {"file1": 10, "file2": 10}
+ mock_fileops.getmtime.return_value = 6
+ mock_adb_control = MagicMock()
+ adb_monitor.monitor_device(mock_adb_control, files_mtime)
+ mock_adb_control.start.assert_called_once()
+ mock_adb_control.wait.assert_called_once()
+ self.assertEqual(mock_fileops.getmtime.call_count, len(files_mtime))
+ mock_fileops.is_empty.assert_not_called()
+
+ @patch(ns("fileops"), return_value=False)
+ def test_monitor_device_no_files(self, mock_fileops):
+ files_mtime = {}
+ mock_fileops.getmtime.return_value = 10
+ mock_adb_control = MagicMock()
+ adb_monitor.monitor_device(mock_adb_control, files_mtime)
+ mock_adb_control.start.assert_called_once()
+ mock_adb_control.wait.assert_called_once()
+ mock_fileops.getmtime.assert_not_called()
+ mock_fileops.is_empty.assert_not_called()
+
+
+class TestOutputFilesMtime(unittest.TestCase):
+ @patch(ns("fileops.getmtime"))
+ def test_output_files_mtime(self, mock_getmtime):
+ mtime = 11
+ mock_getmtime.return_value = mtime
+ files = ["file1", "file2"]
+ m_files = adb_monitor.create_files_mtime(files)
+ self.assertEqual(len(m_files), len(files))
+ for fn in files:
+ self.assertEqual(m_files[fn], mtime)
+
+ @patch(ns("fileops.getmtime"))
+ def test_output_files_mtime_no_files(self, mock_getmtime):
+ m_files = adb_monitor.create_files_mtime([])
+ self.assertEqual(len(m_files), 0)
+
+
+class TestFindExecutable(unittest.TestCase):
+ def test_find_executable(self):
+ my_binary = "mybinary"
+ with patch(ns("distutils.spawn.find_executable"), side_effect=lambda x: x):
+ adb_binary = adb_monitor.find_executable(my_binary)
+ self.assertEqual(adb_binary, my_binary)
+
+ def test_find_executable_not_found(self):
+ with patch(ns("distutils.spawn.find_executable"), side_effect=lambda x: x),\
+ self.assertRaises(EnvironmentError):
+ adb_monitor.find_executable(None)
+
+
+class TestAdb(unittest.TestCase):
+ @patch(ns("os.path.isfile"), return_value=True)
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ def test___init__(self, mock_find_executable, mock_isfile):
+ os_path = os.environ["PATH"]
+ adb = adb_monitor.Adb()
+ self.assertTrue(adb.systrace_script.startswith(os.path.join("systrace", "systrace.py")))
+ self.assertEqual(adb.logger, adb_monitor.LOGGER)
+ self.assertEqual(adb.python27, adb_monitor.DEFAULT_PYTHON27)
+ self.assertEqual(adb.logger, adb_monitor.LOGGER)
+ self.assertEqual(os.environ["PATH"], os_path)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ def test___init__adb_binary(self, mock_find_executable, mock_os):
+ adb_dir = os.path.join("/root", "adb_dir")
+ adb_path = os.path.join(adb_dir, "adb")
+ mock_os.environ = {"PATH": os.environ["PATH"]}
+ mock_os.path.pathsep = os.path.pathsep
+ mock_os.path.dirname = lambda x: x
+ adb = adb_monitor.Adb(adb_binary=adb_path)
+ self.assertTrue(adb.systrace_script.startswith(adb_dir))
+ self.assertIn(os.path.pathsep + adb_dir, mock_os.environ["PATH"])
+
+ @patch(ns("os.environ"))
+ @patch(ns("os.path.isfile"), return_value=True)
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ def test___init__python27_binary(self, mock_find_executable, mock_isfile, mock_os_environ):
+ python_dir = os.path.join("/root", "python27_dir")
+ python27_path = os.path.join(python_dir, "python2")
+ adb = adb_monitor.Adb(python27=python27_path)
+ self.assertEqual(adb.python27, python27_path)
+
+ @patch(ns("os.environ"))
+ @patch(ns("os.path.isfile"), return_value=False)
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ def test___init__bad_systrace(self, mock_find_executable, mock_isfile, mock_os_environ):
+ adb_dir = os.path.join("/root", "adb_dir")
+ adb_path = os.path.join(adb_dir, "adb")
+ with self.assertRaises(EnvironmentError):
+ adb_monitor.Adb()
+
+ @patch(ns("os"))
+ @patch(ns("runcommand"))
+ def test_adb_cmd_output(self, mock_runcmd, mock_os):
+ adb_result = adb_monitor.Adb.adb_cmd("mycmd")
+ self.assertEqual(adb_result, mock_runcmd.RunCommand().execute_with_output())
+ self.assertNotEqual(adb_result, mock_runcmd.RunCommand().execute_save_output())
+
+ @patch(ns("os"))
+ @patch(ns("runcommand"))
+ def test_adb_cmd_output_string(self, mock_runcmd, mock_os):
+ adb_result = adb_monitor.Adb.adb_cmd("mycmd", output_string=True)
+ self.assertEqual(adb_result, mock_runcmd.RunCommand().execute_with_output())
+ self.assertNotEqual(adb_result, mock_runcmd.RunCommand().execute_save_output())
+
+ @patch(ns("os"))
+ @patch(ns("runcommand"))
+ def test_adb_cmd_save_output(self, mock_runcmd, mock_os):
+ adb_result = adb_monitor.Adb.adb_cmd("mycmd", output_file="myfile")
+ self.assertEqual(adb_result, mock_runcmd.RunCommand().execute_save_output())
+ self.assertNotEqual(adb_result, mock_runcmd.RunCommand().execute_with_output())
+
+ @patch(ns("os"))
+ @patch(ns("runcommand"))
+ def test_adb_cmd_all_params(self, mock_runcmd, mock_os):
+ adb_result = adb_monitor.Adb.adb_cmd("mycmd", output_file="myfile", append_file=True,
+ output_string=True)
+ self.assertNotEqual(adb_result, mock_runcmd.RunCommand().execute_save_output())
+ self.assertEqual(adb_result, mock_runcmd.RunCommand().execute_with_output())
+
+ @patch(ns("os"))
+ @patch(ns("runcommand"))
+ def test_shell(self, mock_runcmd, mock_os):
+ cmd_output = adb_monitor.Adb.shell("mycmd")
+ self.assertEqual(cmd_output, mock_runcmd.RunCommand().execute_with_output())
+
+ @patch(ns("os"))
+ @patch(ns("runcommand"))
+ def test_shell_stripped(self, mock_runcmd, mock_os):
+ output = "output from shell"
+ mock_runcmd.RunCommand().execute_with_output.return_value = output + "__EXIT__:0\n"
+ cmd_output = adb_monitor.Adb.shell("mycmd")
+ self.assertEqual(cmd_output, output)
+
+ @patch(ns("os"))
+ @patch(ns("runcommand"))
+ def test_shell_error(self, mock_runcmd, mock_os):
+ output = "output from shell"
+ mock_runcmd.RunCommand().execute_with_output.return_value = output + "__EXIT__:1\n"
+ with self.assertRaises(RuntimeError):
+ adb_monitor.Adb.shell("mycmd")
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_devices(self, mock_runcmd, mock_find_executable, mock_os):
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.devices()
+ mock_runcmd.RunCommand.assert_called_once_with("adb devices -l", unittest.mock.ANY,
+ unittest.mock.ANY)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_device_available(self, mock_runcmd, mock_find_executable, mock_os):
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.device_available()
+ mock_runcmd.RunCommand.assert_called_once_with("adb shell uptime", unittest.mock.ANY,
+ unittest.mock.ANY)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_push(self, mock_runcmd, mock_find_executable, mock_os):
+ files = "myfile"
+ remote_dir = "/remotedir"
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.push(files, remote_dir)
+ push_cmd = mock_runcmd.RunCommand.call_args_list[0][0][0]
+ self.assertIn(files, push_cmd)
+ self.assertIn(remote_dir, push_cmd)
+ self.assertNotIn("--sync", push_cmd)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_push_list(self, mock_runcmd, mock_find_executable, mock_os):
+ files = ["myfile", "file2"]
+ remote_dir = "/remotedir"
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.push(files, remote_dir)
+ push_cmd = mock_runcmd.RunCommand.call_args_list[0][0][0]
+ self.assertIn(" ".join(files), push_cmd)
+ self.assertIn(remote_dir, push_cmd)
+ self.assertNotIn("--sync", push_cmd)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_push_sync(self, mock_runcmd, mock_find_executable, mock_os):
+ files = ["myfile", "file2"]
+ remote_dir = "/remotedir"
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.push(files, remote_dir, sync=True)
+ push_cmd = mock_runcmd.RunCommand.call_args_list[0][0][0]
+ for file_name in files:
+ self.assertIn(file_name, push_cmd)
+ self.assertIn(remote_dir, push_cmd)
+ self.assertIn("--sync", push_cmd)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_pull(self, mock_runcmd, mock_find_executable, mock_os):
+ files = "myfile"
+ local_dir = "/localdir"
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.pull(files, local_dir)
+ pull_cmd = mock_runcmd.RunCommand.call_args_list[0][0][0]
+ self.assertIn(files, pull_cmd)
+ self.assertIn(local_dir, pull_cmd)
+ self.assertNotIn("--sync", pull_cmd)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_pull_files(self, mock_runcmd, mock_find_executable, mock_os):
+ files = ["myfile", "file2"]
+ local_dir = "/localdir"
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.pull(files, local_dir)
+ pull_cmd = mock_runcmd.RunCommand.call_args_list[0][0][0]
+ for file_name in files:
+ self.assertIn(file_name, pull_cmd)
+ self.assertIn(local_dir, pull_cmd)
+ self.assertNotIn("--sync", pull_cmd)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test__battery_cmd(self, mock_runcmd, mock_find_executable, mock_os):
+ option = "myopt"
+ battery_cmd = "shell dumpsys batterystats " + option
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb._battery_cmd(option)
+ mock_runcmd.RunCommand.assert_called_once_with("adb " + battery_cmd, unittest.mock.ANY,
+ unittest.mock.ANY)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test__battery_cmd_save_output(self, mock_runcmd, mock_find_executable, mock_os):
+ option = "myopt"
+ battery_cmd = "shell dumpsys batterystats " + option
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb._battery_cmd(option, "myfile")
+ mock_runcmd.RunCommand.assert_called_once_with("adb " + battery_cmd, unittest.mock.ANY,
+ unittest.mock.ANY)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_battery(self, mock_runcmd, mock_find_executable, mock_os):
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.battery("myfile")
+ battery_cmd = mock_runcmd.RunCommand.call_args_list[0][0][0]
+ self.assertIn("--checkin", battery_cmd)
+ self.assertNotIn("--reset", battery_cmd)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_battery_reset(self, mock_runcmd, mock_find_executable, mock_os):
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.battery("myfile", reset=True, append_file=True)
+ battery_cmd1 = mock_runcmd.RunCommand.call_args_list[0][0][0]
+ battery_cmd2 = mock_runcmd.RunCommand.call_args_list[1][0][0]
+ self.assertIn("--reset", battery_cmd1)
+ self.assertIn("--checkin", battery_cmd2)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_memory(self, mock_runcmd, mock_find_executable, mock_os):
+ memory_cmd = "shell dumpsys meminfo -c -d"
+ mock_os.path.dirname.return_value = "adb_dir"
+ adb = adb_monitor.Adb()
+ adb.memory("myfile")
+ mock_runcmd.RunCommand.assert_called_once_with("adb " + memory_cmd, unittest.mock.ANY,
+ unittest.mock.ANY)
+
+ @patch(ns("tempfile.NamedTemporaryFile"))
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_systrace_start(self, mock_runcmd, mock_find_executable, mock_os, mock_tempfile):
+ mock_os.path.dirname.return_value = "adb_dir"
+ systrace_script = "systrace.py"
+ mock_os.path.join.return_value = systrace_script
+ adb = adb_monitor.Adb()
+ adb.systrace_start()
+ call_args = mock_runcmd.RunCommand.call_args_list[0]
+ mock_runcmd.RunCommand.assert_called_once_with(output_file=mock_tempfile().name,
+ propagate_signals=False)
+ self.assertEqual(adb.systrace_script, systrace_script)
+ self.assertEqual(mock_runcmd.RunCommand().add_file.call_count, 3)
+ mock_runcmd.RunCommand().add_file.assert_any_call(adb_monitor.DEFAULT_PYTHON27)
+ mock_runcmd.RunCommand().add_file.assert_any_call(systrace_script)
+ self.assertEqual(mock_runcmd.RunCommand().add.call_count, 3)
+ mock_runcmd.RunCommand().add.assert_any_call("--json")
+ mock_runcmd.RunCommand().add.assert_any_call("-o")
+ mock_runcmd.RunCommand().add.assert_any_call("dalvik sched freq idle load")
+ mock_runcmd.RunCommand().start_process.assert_called_once()
+
+ @patch(ns("tempfile.NamedTemporaryFile"))
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ @patch(ns("runcommand"))
+ def test_systrace_start_output_file(self, mock_runcmd, mock_find_executable, mock_os,
+ mock_tempfile):
+ output_file = "myfile"
+ mock_os.path.dirname.return_value = "adb_dir"
+ systrace_script = "systrace.py"
+ mock_os.path.join.return_value = systrace_script
+ adb = adb_monitor.Adb()
+ adb.systrace_start(output_file)
+ call_args = mock_runcmd.RunCommand.call_args_list[0]
+ mock_runcmd.RunCommand.assert_called_once_with(output_file=mock_tempfile().name,
+ propagate_signals=False)
+ self.assertEqual(adb.systrace_script, systrace_script)
+ self.assertEqual(mock_runcmd.RunCommand().add_file.call_count, 3)
+ mock_runcmd.RunCommand().add_file.assert_any_call(adb_monitor.DEFAULT_PYTHON27)
+ mock_runcmd.RunCommand().add_file.assert_any_call(systrace_script)
+ mock_runcmd.RunCommand().add_file.assert_any_call(output_file)
+ self.assertEqual(mock_runcmd.RunCommand().add.call_count, 3)
+ mock_runcmd.RunCommand().add.assert_any_call("--json")
+ mock_runcmd.RunCommand().add.assert_any_call("-o")
+ mock_runcmd.RunCommand().add.assert_any_call("dalvik sched freq idle load")
+ mock_runcmd.RunCommand().start_process.assert_called_once()
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ def test_systrace_stop(self, mock_find_executable, mock_os):
+ mock_os.path.dirname.return_value = "adb_dir"
+ systrace_script = "systrace.py"
+ systrace_output = "Systrace: Wrote trace"
+ mock_os.path.join.return_value = systrace_script
+ adb = adb_monitor.Adb()
+ adb._cmd = MagicMock()
+ with patch(ADB_MONITOR + ".open", mock_open(read_data=systrace_output)):
+ adb.systrace_stop()
+ adb._cmd.send_to_process.assert_called_once_with(b"bye")
+ mock_os.remove.assert_called_once_with(adb._tempfile)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ def test_systrace_stop_output_file(self, mock_find_executable, mock_os):
+ mock_os.path.dirname.return_value = "adb_dir"
+ systrace_script = "systrace.py"
+ systrace_output = "Systrace: Wrote trace"
+ output_file = "myfile"
+ mock_os.path.join.return_value = systrace_script
+ adb = adb_monitor.Adb()
+ adb._cmd = MagicMock()
+ with patch(ADB_MONITOR + ".open", mock_open(read_data=systrace_output)):
+ adb.systrace_stop(output_file=output_file)
+ adb._cmd.send_to_process.assert_called_once_with(b"bye")
+ mock_os.remove.assert_called_once_with(adb._tempfile)
+
+ @patch(ns("os"))
+ @patch(ns("find_executable"), side_effect=lambda x: x)
+ def test_systrace_stop_no_trace(self, mock_find_executable, mock_os):
+ mock_os.path.dirname.return_value = "adb_dir"
+ systrace_script = "systrace.py"
+ systrace_output = "Systrace: did not Write trace"
+ output_file = "myfile"
+ mock_os.path.join.return_value = systrace_script
+ adb = adb_monitor.Adb()
+ adb._cmd = MagicMock()
+ with patch(ADB_MONITOR + ".open", mock_open(read_data=systrace_output)):
+ adb.systrace_stop(output_file=output_file)
+ adb._cmd.send_to_process.assert_called_once_with(b"bye")
+ self.assertEqual(mock_os.remove.call_count, 2)
+ mock_os.remove.assert_any_call(adb._tempfile)
+ mock_os.remove.assert_any_call(output_file)
+
+
+class TestAdbControl(unittest.TestCase):
+ def test___init___no_files(self):
+ mock_adb = MagicMock()
+ with self.assertRaises(ValueError):
+ adb_monitor.AdbControl(mock_adb)
+
+ @patch(ns("fileops.create_empty"))
+ def test___init___all_files(self, mock_create_empty):
+ mock_adb = MagicMock()
+ battery_file = "mybattery"
+ memory_file = "mymemory"
+ cpu_file = "mycpu"
+ adb_control = adb_monitor.AdbControl(mock_adb, battery_file=battery_file,
+ memory_file=memory_file, cpu_file=cpu_file)
+ self.assertEqual(adb_control.adb, mock_adb)
+ self.assertEqual(adb_control.battery_file, battery_file)
+ self.assertEqual(adb_control.memory_file, memory_file)
+ self.assertEqual(adb_control.cpu_file, cpu_file)
+ self.assertEqual(adb_control.num_samples, 0)
+ self.assertIsNone(adb_control.collection_time_secs)
+ self.assertEqual(adb_control.sample_interval_ms, adb_monitor.DEFAULT_SAMPLE_INTERVAL_MS)
+ self.assertEqual(mock_create_empty.call_count, 3)
+ mock_create_empty.assert_any_call(battery_file)
+ mock_create_empty.assert_any_call(memory_file)
+ mock_create_empty.assert_any_call(cpu_file)
+
+ @patch(ns("fileops.create_empty"))
+ def test___init___battery_file(self, mock_create_empty):
+ mock_adb = MagicMock()
+ battery_file = "mybattery"
+ adb_control = adb_monitor.AdbControl(mock_adb, battery_file=battery_file)
+ self.assertEqual(adb_control.adb, mock_adb)
+ self.assertEqual(adb_control.battery_file, battery_file)
+ self.assertIsNone(adb_control.memory_file)
+ self.assertIsNone(adb_control.cpu_file)
+ self.assertEqual(adb_control.num_samples, 0)
+ self.assertIsNone(adb_control.collection_time_secs)
+ self.assertEqual(adb_control.sample_interval_ms, adb_monitor.DEFAULT_SAMPLE_INTERVAL_MS)
+ mock_create_empty.assert_called_once_with(battery_file)
+
+ @patch(ns("fileops.create_empty"))
+ def test___init___all_params(self, mock_create_empty):
+ mock_adb = MagicMock()
+ mock_logger = MagicMock()
+ battery_file = "mybattery"
+ memory_file = "mymemory"
+ cpu_file = "mycpu"
+ num_samples = 5
+ collection_time_secs = 10
+ sample_interval_ms = 25
+ adb_control = adb_monitor.AdbControl(
+ mock_adb, logger=mock_logger, battery_file=battery_file, memory_file=memory_file,
+ cpu_file=cpu_file, append_file=True, num_samples=num_samples, collection_time_secs=10,
+ sample_interval_ms=sample_interval_ms)
+ self.assertEqual(adb_control.adb, mock_adb)
+ self.assertEqual(adb_control.battery_file, battery_file)
+ self.assertEqual(adb_control.memory_file, memory_file)
+ self.assertEqual(adb_control.cpu_file, cpu_file)
+ self.assertEqual(adb_control.num_samples, 0)
+ self.assertEqual(adb_control.collection_time_secs, collection_time_secs)
+ self.assertEqual(adb_control.sample_interval_ms, sample_interval_ms)
+ mock_create_empty.assert_not_called()
+
+ @patch(ns("fileops.create_empty"))
+ def test___init___num_samples(self, mock_create_empty):
+ mock_adb = MagicMock()
+ battery_file = "mybattery"
+ num_samples = 5
+ adb_control = adb_monitor.AdbControl(mock_adb, battery_file=battery_file,
+ num_samples=num_samples)
+ self.assertEqual(adb_control.adb, mock_adb)
+ self.assertEqual(adb_control.battery_file, battery_file)
+ self.assertIsNone(adb_control.memory_file)
+ self.assertIsNone(adb_control.cpu_file)
+ self.assertEqual(adb_control.num_samples, num_samples)
+ self.assertIsNone(adb_control.collection_time_secs)
+ self.assertEqual(adb_control.sample_interval_ms, adb_monitor.DEFAULT_SAMPLE_INTERVAL_MS)
+
+ @patch(ns("AdbContinuousResourceMonitor"))
+ @patch(ns("AdbSampleBasedResourceMonitor"))
+ def test_start_all(self, mock_sample_monitor, mock_continuous_monitor):
+ mock_adb = MagicMock()
+ mock_logger = MagicMock()
+ battery_file = "mybattery"
+ memory_file = "mymemory"
+ cpu_file = "mycpu"
+ num_samples = 5
+ collection_time_secs = 10
+ sample_interval_ms = 25
+ adb_control = adb_monitor.AdbControl(
+ mock_adb, logger=mock_logger, battery_file=battery_file, memory_file=memory_file,
+ cpu_file=cpu_file, append_file=True, num_samples=num_samples, collection_time_secs=10,
+ sample_interval_ms=sample_interval_ms)
+ adb_control.start()
+ self.assertEqual(len(adb_control._all_threads), 3)
+ self.assertEqual(len(adb_control._sample_based_threads), 2)
+ mock_continuous_monitor.assert_called_once_with(
+ cpu_file, adb_control._should_stop, mock_adb.systrace_start, mock_adb.systrace_stop)
+ mock_continuous_monitor().start.assert_called_once()
+ self.assertEqual(mock_sample_monitor.call_count, 2)
+ mock_sample_monitor.assert_any_call(battery_file, adb_control._should_stop,
+ mock_adb.battery, 0, sample_interval_ms)
+ mock_sample_monitor.assert_any_call(memory_file, adb_control._should_stop, mock_adb.memory,
+ 0, sample_interval_ms)
+ self.assertEqual(mock_sample_monitor().start.call_count, 2)
+
+ @patch(ns("AdbContinuousResourceMonitor"))
+ @patch(ns("AdbSampleBasedResourceMonitor"))
+ def test_start_cpu(self, mock_sample_monitor, mock_continuous_monitor):
+ mock_adb = MagicMock()
+ mock_logger = MagicMock()
+ cpu_file = "mycpu"
+ collection_time_secs = 10
+ adb_control = adb_monitor.AdbControl(mock_adb, logger=mock_logger, cpu_file=cpu_file,
+ append_file=True, collection_time_secs=10)
+ adb_control.start()
+ self.assertEqual(len(adb_control._all_threads), 1)
+ self.assertEqual(len(adb_control._sample_based_threads), 0)
+ mock_continuous_monitor.assert_called_once_with(
+ cpu_file, adb_control._should_stop, mock_adb.systrace_start, mock_adb.systrace_stop)
+ mock_continuous_monitor().start.assert_called_once()
+ mock_sample_monitor.assert_not_called()
+
+ @patch(ns("AdbContinuousResourceMonitor"))
+ @patch(ns("AdbSampleBasedResourceMonitor"))
+ def test_start_battery(self, mock_sample_monitor, mock_continuous_monitor):
+ mock_adb = MagicMock()
+ mock_logger = MagicMock()
+ battery_file = "mybattery"
+ num_samples = 5
+ sample_interval_ms = 25
+ adb_control = adb_monitor.AdbControl(
+ mock_adb, logger=mock_logger, battery_file=battery_file, append_file=True,
+ num_samples=num_samples, sample_interval_ms=sample_interval_ms)
+ adb_control.start()
+ self.assertEqual(len(adb_control._all_threads), 1)
+ self.assertEqual(len(adb_control._sample_based_threads), 1)
+ mock_continuous_monitor.assert_not_called()
+ mock_sample_monitor.assert_called_once_with(battery_file, adb_control._should_stop,
+ mock_adb.battery, num_samples,
+ sample_interval_ms)
+ mock_sample_monitor().start.assert_called_once()
+
+ def test_stop(self):
+ mock_adb = MagicMock()
+ mock_logger = MagicMock()
+ battery_file = "mybattery"
+ num_samples = 5
+ sample_interval_ms = 25
+ adb_control = adb_monitor.AdbControl(
+ mock_adb, logger=mock_logger, battery_file=battery_file, append_file=True,
+ num_samples=num_samples, sample_interval_ms=sample_interval_ms)
+ adb_control._should_stop = MagicMock()
+ adb_control.wait = MagicMock()
+ adb_control.stop()
+ adb_control._should_stop.set.assert_called_once()
+ adb_control.wait.assert_called_once()
+
+ @patch(ns("AdbContinuousResourceMonitor"))
+ @patch(ns("AdbSampleBasedResourceMonitor"))
+ def test_wait(self, mock_sample_monitor, mock_continuous_monitor):
+ mock_adb = MagicMock()
+ mock_logger = MagicMock()
+ battery_file = "mybattery"
+ memory_file = "mymemory"
+ cpu_file = "mycpu"
+ num_samples = 5
+ collection_time_secs = 10
+ sample_interval_ms = 25
+ adb_control = adb_monitor.AdbControl(
+ mock_adb, logger=mock_logger, battery_file=battery_file, memory_file=memory_file,
+ cpu_file=cpu_file, append_file=True, num_samples=num_samples, collection_time_secs=10,
+ sample_interval_ms=sample_interval_ms)
+ mock_sample_monitor().exception = None
+ mock_continuous_monitor().exception = None
+ adb_control._should_stop = MagicMock()
adb_control.start()
adb_control.wait()
- for arg_file in arg_file_list:
- self.assertGreater(os.stat(arg_file).st_size, 0)
- os.remove(arg_file)
+ adb_control._should_stop.wait.assert_called_once_with(collection_time_secs)
+ adb_control._should_stop.set.assert_called_once()
+ mock_continuous_monitor().join.assert_called_once_with(adb_control._JOIN_TIMEOUT)
+ self.assertEqual(mock_sample_monitor().join.call_count, 2)
+ mock_sample_monitor().join.assert_called_with(adb_control._JOIN_TIMEOUT)
+
+ @patch(ns("AdbContinuousResourceMonitor"))
+ @patch(ns("AdbSampleBasedResourceMonitor"))
+ def test_wait_keyboard_interrupt(self, mock_sample_monitor, mock_continuous_monitor):
+ mock_adb = MagicMock()
+ mock_logger = MagicMock()
+ battery_file = "mybattery"
+ memory_file = "mymemory"
+ cpu_file = "mycpu"
+ num_samples = 5
+ collection_time_secs = 10
+ sample_interval_ms = 25
+ adb_control = adb_monitor.AdbControl(
+ mock_adb, logger=mock_logger, battery_file=battery_file, memory_file=memory_file,
+ cpu_file=cpu_file, append_file=True, num_samples=num_samples, collection_time_secs=10,
+ sample_interval_ms=sample_interval_ms)
+ mock_sample_monitor().exception = None
+ mock_continuous_monitor().exception = None
+ adb_control._should_stop = MagicMock()
+ adb_control._should_stop.wait.side_effect = KeyboardInterrupt()
+ adb_control.start()
+ adb_control.wait()
+ adb_control._should_stop.wait.assert_called_once_with(collection_time_secs)
+ adb_control._should_stop.set.assert_called_once()
+ mock_continuous_monitor().join.assert_called_once_with(adb_control._JOIN_TIMEOUT)
+ self.assertEqual(mock_sample_monitor().join.call_count, 2)
+ mock_sample_monitor().join.assert_called_with(adb_control._JOIN_TIMEOUT)
+
+ @patch(ns("AdbSampleBasedResourceMonitor"))
+ def test_wait_no_collection_time(self, mock_sample_monitor):
+ mock_adb = MagicMock()
+ mock_logger = MagicMock()
+ battery_file = "mybattery"
+ memory_file = "mymemory"
+ num_samples = 5
+ sample_interval_ms = 25
+ adb_control = adb_monitor.AdbControl(
+ mock_adb, logger=mock_logger, battery_file=battery_file, memory_file=memory_file,
+ append_file=True, num_samples=num_samples, sample_interval_ms=sample_interval_ms)
+ mock_sample_monitor().exception = None
+ adb_control._should_stop = MagicMock()
+ adb_control.start()
+ adb_control.wait()
+ adb_control._should_stop.wait.assert_not_called()
+ adb_control._should_stop.set.assert_called_once()
+ self.assertEqual(mock_sample_monitor().join.call_count, 4)
+ mock_sample_monitor().join.assert_called_with(adb_control._JOIN_TIMEOUT)
+
+ @patch(ns("AdbContinuousResourceMonitor"))
+ @patch(ns("AdbSampleBasedResourceMonitor"))
+ def test_wait_thread_exception(self, mock_sample_monitor, mock_continuous_monitor):
+ mock_adb = MagicMock()
+ mock_logger = MagicMock()
+ battery_file = "mybattery"
+ memory_file = "mymemory"
+ cpu_file = "mycpu"
+ num_samples = 5
+ collection_time_secs = 10
+ sample_interval_ms = 25
+ adb_control = adb_monitor.AdbControl(
+ mock_adb, logger=mock_logger, battery_file=battery_file, memory_file=memory_file,
+ cpu_file=cpu_file, append_file=True, num_samples=num_samples, collection_time_secs=10,
+ sample_interval_ms=sample_interval_ms)
+ mock_sample_monitor().exception = RuntimeError("my run time exception")
+ mock_continuous_monitor().exception = None
+ adb_control._should_stop = MagicMock()
+ adb_control.start()
+ with self.assertRaises(RuntimeError):
+ adb_control.wait()
+ adb_control._should_stop.wait.assert_called_once_with(collection_time_secs)
+ adb_control._should_stop.set.assert_called_once()
+ mock_continuous_monitor().join.assert_called_once_with(adb_control._JOIN_TIMEOUT)
+ self.assertEqual(mock_sample_monitor().join.call_count, 2)
+ mock_sample_monitor().join.assert_called_with(adb_control._JOIN_TIMEOUT)
+
+
+class TestAdbResourceMonitor(unittest.TestCase):
+ @patch(ns("threading"))
+ def test_run(self, mock_threading):
+ output_file = "myfile"
+ should_stop = MagicMock()
+ arm = adb_monitor.AdbResourceMonitor(output_file, should_stop)
+ arm._do_monitoring = MagicMock()
+ arm.run()
+ self.assertEqual(arm._output_file, output_file)
+ arm._do_monitoring.assert_called_once()
+ self.assertIsNone(arm.exception)
+ should_stop.set.assert_not_called()
+
+ @patch(ns("threading"))
+ def test_run_exception(self, mock_threading):
+ output_file = "myfile"
+ should_stop = MagicMock()
+ exception = RuntimeError()
+ arm = adb_monitor.AdbResourceMonitor(output_file, should_stop)
+ arm._do_monitoring = MagicMock(side_effect=exception)
+ arm.run()
+ self.assertEqual(arm._output_file, output_file)
+ arm._do_monitoring.assert_called_once()
+ self.assertEqual(arm.exception, exception)
+ should_stop.set.assert_called_once()
+
+
+class TestAdbSampleBasedResourceMonitor(unittest.TestCase):
+ @patch(ns("threading"))
+ def test__do_monitoring(self, mock_threading):
+ output_file = "myfile"
+ mock_should_stop = MagicMock()
+ mock_should_stop.is_set.return_value = False
+ mock_adb_cmd = MagicMock()
+ num_samples = 5
+ sample_interval_ms = 50
+ arm = adb_monitor.AdbSampleBasedResourceMonitor(output_file, mock_should_stop, mock_adb_cmd,
+ num_samples, sample_interval_ms)
+ arm._take_sample = MagicMock()
+ arm._do_monitoring()
+ self.assertEqual(mock_should_stop.is_set.call_count, num_samples + 1)
+ self.assertEqual(arm._take_sample.call_count, num_samples)
+ self.assertEqual(mock_should_stop.wait.call_count, num_samples - 1)
+ mock_should_stop.wait.assert_called_with(sample_interval_ms / 1000.0)
- def test_all_files_num_samples(self):
- self._test_files(num_samples=5, arg_list=["battery_file", "cpu_file", "memory_file"])
+ def _is_set(self):
+ self.num_collected += 1
+ return self.num_collected == self.num_samples
- def test_all_files_collection_time_secs(self):
- self._test_files(collection_time_secs=3,
- arg_list=["battery_file", "cpu_file", "memory_file"])
+ @patch(ns("threading"))
+ def test__do_monitoring_no_samples(self, mock_threading):
+ output_file = "myfile"
+ mock_should_stop = MagicMock()
+ self.num_collected = 0
+ self.num_samples = 5
+ mock_should_stop.is_set = self._is_set
+ mock_adb_cmd = MagicMock()
+ num_samples = 0
+ sample_interval_ms = 50
+ arm = adb_monitor.AdbSampleBasedResourceMonitor(output_file, mock_should_stop, mock_adb_cmd,
+ num_samples, sample_interval_ms)
+ arm._take_sample = MagicMock()
+ arm._do_monitoring()
+ self.assertEqual(arm._take_sample.call_count, self.num_samples - 1)
+ self.assertEqual(mock_should_stop.wait.call_count, self.num_samples - 2)
- def test_all_files_collection_and_samples(self):
- self._test_files(collection_time_secs=3, num_samples=5,
- arg_list=["battery_file", "cpu_file", "memory_file"])
+ @patch(ns("threading"))
+ def test__take_sample(self, mock_threading):
+ output_file = "myfile"
+ mock_should_stop = MagicMock()
+ self.num_collected = 0
+ self.num_samples = 5
+ mock_should_stop.is_set = self._is_set
+ mock_adb_cmd = MagicMock()
+ arm = adb_monitor.AdbSampleBasedResourceMonitor(output_file, mock_should_stop, mock_adb_cmd,
+ 10, 20)
+ arm._take_sample(2)
+ mock_adb_cmd.assert_called_once_with(output_file=output_file, append_file=True)
- def test_no_file_arg(self):
- self.assertRaises(ValueError, lambda: adb_monitor.AdbControl(self.adb))
- def test_bad_file_arg(self):
- self.assertRaises(TypeError, lambda: self._test_files(arg_list=["bad_file_arg"]))
+class TestAdbContinuousResourceMonitor(unittest.TestCase):
+ @patch(ns("threading"))
+ def test__do_monitoring(self, mock_threading):
+ output_file = "myfile"
+ mock_should_stop = MagicMock()
+ mock_adb_start_cmd = MagicMock()
+ mock_adb_stop_cmd = MagicMock()
+ acrm = adb_monitor.AdbContinuousResourceMonitor(output_file, mock_should_stop,
+ mock_adb_start_cmd, mock_adb_stop_cmd)
+ acrm._do_monitoring()
+ mock_should_stop.wait.assert_called_once()
+ mock_adb_start_cmd.assert_called_with(output_file=output_file)
+ mock_adb_stop_cmd.assert_called_with(output_file=output_file)