[autotest][cfm] Adds perf monitoring actions and enables them for the
idle test

This enables us to record perf metrics in configurable cfm tests.
Note that the data is not uploaded to the perf dashaboards yet.

TEST=local device
BUG=b:119806655

Change-Id: I74b55534726d0ade2cd068a31182bff3ddccb401
Reviewed-on: https://chromium-review.googlesource.com/1361042
Commit-Ready: Denis Tosic <dtosic@google.com>
Tested-by: Denis Tosic <dtosic@google.com>
Reviewed-by: Kristoffer Erlandsson <kerl@google.com>
diff --git a/server/cros/cfm/configurable_test/action_context.py b/server/cros/cfm/configurable_test/action_context.py
index b024d15..ddbd638 100644
--- a/server/cros/cfm/configurable_test/action_context.py
+++ b/server/cros/cfm/configurable_test/action_context.py
@@ -8,7 +8,8 @@
                  host=None,
                  usb_device_collector=None,
                  usb_port_manager=None,
-                 crash_detector=None):
+                 crash_detector=None,
+                 perf_metrics_collector=None):
         """
         Initializes.
 
@@ -24,6 +25,7 @@
         @param usb_device_collecor a UsbDeviceCollector instance.
         @param usb_port_manager a UsbPortManager instance.
         @param crash_detector a CrashDetector instance.
+        @param perf_metrics_collector a PerfMetricsCollector instance.
         """
         self.cfm_facade = cfm_facade
         self.file_contents_collector = file_contents_collector
@@ -33,4 +35,4 @@
         self.usb_device_collector = usb_device_collector
         self.usb_port_manager = usb_port_manager
         self.crash_detector = crash_detector
-
+        self.perf_metrics_collector = perf_metrics_collector
diff --git a/server/cros/cfm/configurable_test/actions.py b/server/cros/cfm/configurable_test/actions.py
index f9773be..b840690 100644
--- a/server/cros/cfm/configurable_test/actions.py
+++ b/server/cros/cfm/configurable_test/actions.py
@@ -413,3 +413,36 @@
             return
     raise TimeoutError('Timeout after %s seconds waiting for condition %s'
                        % (timeout_seconds, condition))
+
+
+class StartPerfMetricsCollection(Action):
+    """
+    Starts collecting performance data.
+
+    Collection is performed in a background thread so this operation returns
+    immediately.
+
+    This action only collects the data, it does not upload it.
+    Use UploadPerfMetrics to upload the data to the perf dashboard.
+    """
+    def do_execute(self, context):
+        context.perf_metrics_collector.start()
+
+
+class StopPerfMetricsCollection(Action):
+    """
+    Stops collecting performance data.
+
+    This action only stops collecting the data, it does not upload it.
+    Use UploadPerfMetrics to upload the data to the perf dashboard.
+    """
+    def do_execute(self, context):
+        context.perf_metrics_collector.stop()
+
+
+class UploadPerfMetrics(Action):
+    """
+    Uploads the collected perf metrics to the perf dashboard.
+    """
+    def do_execute(self, context):
+        context.perf_metrics_collector.upload_metrics()
diff --git a/server/cros/cfm/configurable_test/actions_unittest.py b/server/cros/cfm/configurable_test/actions_unittest.py
index 18c25f7..9d093e0 100644
--- a/server/cros/cfm/configurable_test/actions_unittest.py
+++ b/server/cros/cfm/configurable_test/actions_unittest.py
@@ -27,12 +27,14 @@
         self.usb_device_collector_mock = mock.MagicMock()
         self.usb_port_manager_mock = mock.MagicMock()
         self.crash_detector_mock = mock.MagicMock()
+        self.metrics_collector_mock = mock.MagicMock()
         self.context_with_mocks = action_context.ActionContext(
                 host=self.host_mock,
                 cfm_facade=self.cfm_facade_mock,
                 usb_device_collector=self.usb_device_collector_mock,
                 usb_port_manager=self.usb_port_manager_mock,
-                crash_detector=self.crash_detector_mock)
+                crash_detector=self.crash_detector_mock,
+                perf_metrics_collector=self.metrics_collector_mock)
 
 
     def test_assert_file_does_not_contain_no_match(self):
@@ -188,6 +190,22 @@
                 AssertionError,
                 lambda: action.do_execute(self.context_with_mocks))
 
+    def test_start_metrics_colllection(self):
+        action = actions.StartPerfMetricsCollection()
+        action.execute(self.context_with_mocks)
+        self.metrics_collector_mock.start.assert_called_once_with()
+
+    def test_stop_metrics_colllection(self):
+        action = actions.StopPerfMetricsCollection()
+        action.execute(self.context_with_mocks)
+        self.metrics_collector_mock.stop.assert_called_once_with()
+
+    def test_upload_metrics(self):
+        action = actions.UploadPerfMetrics()
+        action.execute(self.context_with_mocks)
+        self.metrics_collector_mock.upload_metrics.assert_called_once_with()
+
+
 
 class FakeCollector(object):
     def __init__(self, contents):
diff --git a/server/cros/cfm/configurable_test/configurable_cfm_test.py b/server/cros/cfm/configurable_test/configurable_cfm_test.py
index 4c01dc4..c410cbf 100644
--- a/server/cros/cfm/configurable_test/configurable_cfm_test.py
+++ b/server/cros/cfm/configurable_test/configurable_cfm_test.py
@@ -5,6 +5,8 @@
 from autotest_lib.client.common_lib.cros.cfm.usb import usb_port_manager
 from autotest_lib.server.cros.cfm import cfm_base_test
 from autotest_lib.server.cros.cfm.configurable_test import action_context
+from autotest_lib.server.cros.cfm.utils import perf_metrics_collector
+
 
 class TestRunner(object):
     """
@@ -27,6 +29,7 @@
         logging.info('RUNNING:\n%s', str(cfm_test))
         cfm_test.scenario.execute(self.context)
 
+
 class HostFileContentsCollector(object):
     """
     File contents collector that executes commands against the host.
@@ -48,6 +51,7 @@
         """
         return self.host.run_output('cat "%s"' % path)
 
+
 class ConfigurableCfmTest(cfm_base_test.CfmBaseTest):
     """
     Base class for the actual Autotests that execute configurable CFM tests.
@@ -80,12 +84,17 @@
                 host=host,
                 usb_device_collector=device_collector,
                 usb_port_manager=port_manager,
-                crash_detector=crash_file_detector)
+                crash_detector=crash_file_detector,
+                perf_metrics_collector=self._create_perf_metrics_collector())
         self.test_runner = TestRunner(context)
 
+    def _create_perf_metrics_collector(self):
+        system_facade = self._facade_factory.create_system_facade()
+        return perf_metrics_collector.PerfMetricsCollector(system_facade,
+            self.cfm_facade, self.output_perf_value)
+
     def run_once(self):
         """
         Runs the test.
         """
         self.test_runner.run_test(self.cfm_test)
-
diff --git a/server/cros/cfm/utils/perf_metrics_collector.py b/server/cros/cfm/utils/perf_metrics_collector.py
index ed477bb..45224e1 100644
--- a/server/cros/cfm/utils/perf_metrics_collector.py
+++ b/server/cros/cfm/utils/perf_metrics_collector.py
@@ -5,44 +5,24 @@
         media_metrics_collector)
 
 
-class ParticipantCountMetric(system_metrics_collector.Metric):
-    """
-    Metric for getting the current participant count in a call.
-    """
-    def __init__(self, cfm_facade):
-        """
-        Initializes with a cfm_facade.
-
-        @param cfm_facade object having a get_participant_count() method.
-        """
-        super(ParticipantCountMetric, self).__init__(
-                'participant_count',
-                'participants',
-                higher_is_better=True)
-        self.cfm_facade = cfm_facade
-
-    def collect_metric(self):
-        """
-        Collects one metric value.
-        """
-        self.values.append(self.cfm_facade.get_participant_count())
-
-
 class PerfMetricsCollector(object):
     """
     Metrics collector that runs in seprate thread than the caller.
     """
-    def __init__(self, system_facade, cfm_facade, writer_function):
+    def __init__(self, system_facade, cfm_facade, writer_function,
+                 additional_system_metrics=[]):
         """
         Constructor.
 
         @param system_facade facade object to access system utils.
         @param cfm_facade facade object to access cfm utils.
         @param writer_function function called to collected metrics.
+        @param additional_system_metrics Additional metrics to collect.
         """
         metrics = system_metrics_collector.create_default_metric_set(
             system_facade)
-        metrics.append(ParticipantCountMetric(cfm_facade))
+        for system_metric in additional_system_metrics:
+            metrics.append(system_facade)
         self._system_metrics_collector = (
             system_metrics_collector.SystemMetricsCollector(system_facade,
                                                             metrics))
diff --git a/server/site_tests/enterprise_CFM_MeetingRoomScenario/control.idle_on_landing_page b/server/site_tests/enterprise_CFM_MeetingRoomScenario/control.idle_on_landing_page
index 777bb30..ec6032e 100644
--- a/server/site_tests/enterprise_CFM_MeetingRoomScenario/control.idle_on_landing_page
+++ b/server/site_tests/enterprise_CFM_MeetingRoomScenario/control.idle_on_landing_page
@@ -22,7 +22,10 @@
     scenario=Scenario(
         RebootDut(restart_chrome_for_cfm=True),
         WaitForMeetingsLandingPage(),
+        StartPerfMetricsCollection(),
         Sleep(15 * 60),
+        StopPerfMetricsCollection(),
+        UploadPerfMetrics(),
     ),
 )
 
diff --git a/server/site_tests/enterprise_CFM_Perf/enterprise_CFM_Perf.py b/server/site_tests/enterprise_CFM_Perf/enterprise_CFM_Perf.py
index bac728f..2ceeb42 100644
--- a/server/site_tests/enterprise_CFM_Perf/enterprise_CFM_Perf.py
+++ b/server/site_tests/enterprise_CFM_Perf/enterprise_CFM_Perf.py
@@ -8,6 +8,7 @@
 
 from autotest_lib.client.common_lib import error
 from autotest_lib.client.common_lib import file_utils
+from autotest_lib.client.common_lib.cros import system_metrics_collector
 from autotest_lib.server.cros.cfm import cfm_base_test
 from autotest_lib.server.cros.cfm.utils import bond_http_api
 from autotest_lib.server.cros.cfm.utils import perf_metrics_collector
@@ -21,6 +22,29 @@
 _VIDEO_NAME = 'crowd720_25frames.y4m'
 
 
+class ParticipantCountMetric(system_metrics_collector.Metric):
+    """
+    Metric for getting the current participant count in a call.
+    """
+    def __init__(self, cfm_facade):
+        """
+        Initializes with a cfm_facade.
+
+        @param cfm_facade object having a get_participant_count() method.
+        """
+        super(ParticipantCountMetric, self).__init__(
+                'participant_count',
+                'participants',
+                higher_is_better=True)
+        self.cfm_facade = cfm_facade
+
+    def collect_metric(self):
+        """
+        Collects one metric value.
+        """
+        self.values.append(self.cfm_facade.get_participant_count())
+
+
 class enterprise_CFM_Perf(cfm_base_test.CfmBaseTest):
     """This is a server test which clears device TPM and runs
     enterprise_RemoraRequisition client test to enroll the device in to hotrod
@@ -63,7 +87,12 @@
         system_facade = self._facade_factory.create_system_facade()
         self._perf_metrics_collector = (
             perf_metrics_collector.PerfMetricsCollector(
-                system_facade, self.cfm_facade, self.output_perf_value))
+                system_facade,
+                self.cfm_facade,
+                self.output_perf_value,
+                additional_system_metrics=[
+                    ParticipantCountMetric(self.cfm_facade),
+                ]))
 
     def setup(self):
         """