1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import unittest
6
7from telemetry.internal.platform.power_monitor import cros_power_monitor
8
9
10class CrosPowerMonitorMonitorTest(unittest.TestCase):
11  initial_power = ('''line_power_connected 0
12battery_present 1
13battery_percent 70.20
14battery_charge 2.83
15battery_charge_full 4.03
16battery_charge_full_design 4.03
17battery_current 1.08
18battery_energy 31.83
19battery_energy_rate 12.78
20battery_voltage 11.82
21battery_discharging 1''')
22  final_power = ('''line_power_connected 0
23battery_present 1
24battery_percent 70.20
25battery_charge 2.83
26battery_charge_full 4.03
27battery_charge_full_design 4.03
28battery_current 1.08
29battery_energy 31.83
30battery_energy_rate 12.80
31battery_voltage 12.24
32battery_discharging 1''')
33  incomplete_final_power = ('''line_power_connected 0
34battery_present 1
35battery_percent 70.20
36battery_charge 2.83
37battery_charge_full 4.03
38battery_charge_full_design 4.03
39battery_energy_rate 12.80
40battery_discharging 1''')
41  expected_power = {
42    'energy_consumption_mwh': 2558.0,
43    'power_samples_mw': [12780.0, 12800.0],
44    'component_utilization': {
45      'battery': {
46        'charge_full': 4.03,
47        'charge_full_design': 4.03,
48        'charge_now': 2.83,
49        'current_now': 1.08,
50        'energy': 31.83,
51        'energy_rate': 12.80,
52        'voltage_now': 12.24
53      }
54    }
55  }
56  expected_incomplete_power = {
57    'energy_consumption_mwh': 2558.0,
58    'power_samples_mw': [12780.0, 12800.0],
59    'component_utilization': {
60      'battery': {
61        'charge_full': 4.03,
62        'charge_full_design': 4.03,
63        'charge_now': 2.83,
64        'energy_rate': 12.80,
65      }
66    }
67  }
68  expected_cpu = {
69    'whole_package': {
70      'frequency_percent': {
71        1700000000: 3.29254111574526,
72        1600000000: 0.0,
73        1500000000: 0.0,
74        1400000000: 0.15926805099535601,
75        1300000000: 0.47124116307273645,
76        1200000000: 0.818756100807525,
77        1100000000: 1.099381692400982,
78        1000000000: 2.5942528544384302,
79        900000000: 5.68661122326737,
80        800000000: 3.850545467654628,
81        700000000: 2.409691872245393,
82        600000000: 1.4693702487650486,
83        500000000: 2.4623575553879373,
84        400000000: 2.672038150383057,
85        300000000: 3.415770495015825,
86        200000000: 69.59817400982045
87      },
88      'cstate_residency_percent': {
89        'C0': 83.67623835616438535,
90        'C1': 0.2698609589041096,
91        'C2': 0.2780191780821918,
92        'C3': 15.77588150684931505
93      }
94    },
95    'cpu0': {
96      'frequency_percent': {
97        1700000000: 4.113700564971752,
98        1600000000: 0.0,
99        1500000000: 0.0,
100        1400000000: 0.1765536723163842,
101        1300000000: 0.4943502824858757,
102        1200000000: 0.7944915254237288,
103        1100000000: 1.2226341807909604,
104        1000000000: 3.0632062146892656,
105        900000000: 5.680614406779661,
106        800000000: 3.6679025423728815,
107        700000000: 2.379060734463277,
108        600000000: 1.4124293785310735,
109        500000000: 2.599752824858757,
110        400000000: 3.0102401129943503,
111        300000000: 3.650247175141243,
112        200000000: 67.73481638418079
113      },
114      'cstate_residency_percent': {
115        'C0': 76.76226164383562,
116        'C1': 0.3189164383561644,
117        'C2': 0.4544301369863014,
118        'C3': 22.4643917808219178
119      }
120    },
121    'cpu1': {
122      'frequency_percent': {
123        1700000000: 2.4713816665187682,
124        1600000000: 0.0,
125        1500000000: 0.0,
126        1400000000: 0.1419824296743278,
127        1300000000: 0.44813204365959713,
128        1200000000: 0.8430206761913214,
129        1100000000: 0.9761292040110037,
130        1000000000: 2.1252994941875945,
131        900000000: 5.69260803975508,
132        800000000: 4.033188392936374,
133        700000000: 2.4403230100275093,
134        600000000: 1.526311118999024,
135        500000000: 2.3249622859171177,
136        400000000: 2.3338361877717633,
137        300000000: 3.1812938148904073,
138        200000000: 71.46153163546012
139      },
140      'cstate_residency_percent': {
141        'C0': 90.5902150684931507,
142        'C1': 0.2208054794520548,
143        'C2': 0.1016082191780822,
144        'C3': 9.0873712328767123
145      }
146    }
147  }
148
149  def _assertPowerEqual(self, results, expected):
150    battery = results['component_utilization']['battery']
151    expected_battery = expected['component_utilization']['battery']
152    self.assertItemsEqual(battery.keys(), expected_battery.keys())
153    for value in battery:
154      self.assertAlmostEqual(battery[value], expected_battery[value])
155
156    self.assertAlmostEqual(results['energy_consumption_mwh'],
157                           expected['energy_consumption_mwh'])
158    self.assertAlmostEqual(results['power_samples_mw'][0],
159                           expected['power_samples_mw'][0])
160    self.assertAlmostEqual(results['power_samples_mw'][1],
161                           expected['power_samples_mw'][1])
162
163  def testParsePower(self):
164    results = cros_power_monitor.CrosPowerMonitor.ParsePower(
165        self.initial_power, self.final_power, 0.2)
166    self._assertPowerEqual(results, self.expected_power)
167
168  def testParseIncompletePowerState(self):
169    """Test the case where dump_power_status only outputs partial fields.
170
171    CrosPowerMonitor hard-coded expected fields from dump_power_status,
172    this test ensures it parses successfully when expected fields does not
173    exist. It's mainly for backward compatibility.
174    """
175    results = cros_power_monitor.CrosPowerMonitor.ParsePower(
176        self.initial_power, self.incomplete_final_power, 0.2)
177    self._assertPowerEqual(results, self.expected_incomplete_power)
178
179
180  def testSplitSample(self):
181    sample = self.initial_power + '\n1408739546\n'
182    power, time = cros_power_monitor.CrosPowerMonitor.SplitSample(sample)
183    self.assertEqual(power, self.initial_power)
184    self.assertEqual(time, 1408739546)
185
186  def testCombineResults(self):
187    result = cros_power_monitor.CrosPowerMonitor.CombineResults(
188        self.expected_cpu, self.expected_power)
189    comp_util = result['component_utilization']
190    # Test power values.
191    self.assertEqual(result['energy_consumption_mwh'],
192                     self.expected_power['energy_consumption_mwh'])
193    self.assertEqual(result['power_samples_mw'],
194                     self.expected_power['power_samples_mw'])
195    self.assertEqual(comp_util['battery'],
196                     self.expected_power['component_utilization']['battery'])
197    # Test frequency values.
198    self.assertDictEqual(
199        comp_util['whole_package']['frequency_percent'],
200        self.expected_cpu['whole_package']['frequency_percent'])
201    self.assertDictEqual(
202        comp_util['cpu0']['frequency_percent'],
203        self.expected_cpu['cpu0']['frequency_percent'])
204    self.assertDictEqual(
205        comp_util['cpu1']['frequency_percent'],
206        self.expected_cpu['cpu1']['frequency_percent'])
207    # Test c-state residency values.
208    self.assertDictEqual(
209        comp_util['whole_package']['cstate_residency_percent'],
210        self.expected_cpu['whole_package']['cstate_residency_percent'])
211    self.assertDictEqual(
212        comp_util['cpu0']['cstate_residency_percent'],
213        self.expected_cpu['cpu0']['cstate_residency_percent'])
214    self.assertDictEqual(
215        comp_util['cpu1']['cstate_residency_percent'],
216        self.expected_cpu['cpu1']['cstate_residency_percent'])
217
218  def testCanMonitorPower(self):
219    # TODO(tmandel): Add a test here where the device cannot monitor power.
220    initial_status = cros_power_monitor.CrosPowerMonitor.ParsePowerStatus(
221        self.initial_power)
222    final_status = cros_power_monitor.CrosPowerMonitor.ParsePowerStatus(
223        self.final_power)
224    self.assertTrue(cros_power_monitor.CrosPowerMonitor.IsOnBatteryPower(
225        initial_status, 'peppy'))
226    self.assertTrue(cros_power_monitor.CrosPowerMonitor.IsOnBatteryPower(
227        final_status, 'butterfly'))
228