xref: /aosp_15_r20/external/autotest/server/cros/crosperf/device_setup_utils_unittest.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright 2020 The Chromium OS Authors. All rights reserved.
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7"""
8Unittest for device_setup_utils.
9
10"""
11
12from __future__ import print_function
13
14import logging
15import time
16import unittest
17from unittest import mock
18
19import common
20from autotest_lib.server import hosts
21from autotest_lib.server.cros.crosperf import device_setup_utils
22
23BIG_LITTLE_CPUINFO = """processor       : 0
24model name      : ARMv8 Processor rev 4 (v8l)
25BogoMIPS        : 48.00
26Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
27CPU implementer : 0x41
28CPU architecture: 8
29CPU variant     : 0x0
30CPU part        : 0xd03
31CPU revision    : 4
32
33processor       : 1
34model name      : ARMv8 Processor rev 4 (v8l)
35BogoMIPS        : 48.00
36Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
37CPU implementer : 0x41
38CPU architecture: 8
39CPU variant     : 0x0
40CPU part        : 0xd03
41CPU revision    : 4
42
43processor       : 2
44model name      : ARMv8 Processor rev 2 (v8l)
45BogoMIPS        : 48.00
46Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
47CPU implementer : 0x41
48CPU architecture: 8
49CPU variant     : 0x0
50CPU part        : 0xd08
51CPU revision    : 2
52"""
53LITTLE_ONLY_CPUINFO = """processor       : 0
54model name      : ARMv8 Processor rev 4 (v8l)
55BogoMIPS        : 48.00
56Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
57CPU implementer : 0x41
58CPU architecture: 8
59CPU variant     : 0x0
60CPU part        : 0xd03
61CPU revision    : 4
62
63processor       : 1
64model name      : ARMv8 Processor rev 4 (v8l)
65BogoMIPS        : 48.00
66Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
67CPU implementer : 0x41
68CPU architecture: 8
69CPU variant     : 0x0
70CPU part        : 0xd03
71CPU revision    : 4
72"""
73
74NOT_BIG_LITTLE_CPUINFO = """processor       : 0
75model name      : ARMv7 Processor rev 1 (v7l)
76Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4
77CPU implementer : 0x41
78CPU architecture: 7
79CPU variant     : 0x0
80CPU part        : 0xc0d
81CPU revision    : 1
82
83processor       : 1
84model name      : ARMv7 Processor rev 1 (v7l)
85Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4
86CPU implementer : 0x41
87CPU architecture: 7
88CPU variant     : 0x0
89CPU part        : 0xc0d
90CPU revision    : 1
91
92Hardware        : Rockchip (Device Tree)
93Revision        : 0000
94Serial          : 0000000000000000
95"""
96
97
98class device_setup_utilsTest(unittest.TestCase):
99    """
100    Class of device setup utils test.
101    """
102
103    def __init__(self, *args, **kwargs):
104        super(device_setup_utilsTest, self).__init__(*args, **kwargs)
105
106        self.dut = mock.Mock(spec=hosts.create_host)
107        self.dut.hostname = 'lumpy.cros2'
108        self.dut.run = mock.Mock()
109
110        logging.error = mock.Mock()
111        logging.warning = mock.Mock()
112
113
114    def test_run_command_on_dut(self):
115        self.dut.run.return_value.exit_status = 0
116        self.dut.run.return_value.stdout = ''
117        self.dut.run.return_value.stderr = ''
118
119        self.dut.run.assert_not_called()
120        device_setup_utils.run_command_on_dut(self.dut, 'run command;')
121        self.dut.run.assert_called_once_with(
122                'run command;', ignore_status=False)
123
124
125    def test_run_command_on_dut_fatal_error(self):
126        # Command returns error 1.
127        self.dut.run.return_value.exit_status = 1
128        self.dut.run.return_value.stdout = ''
129        self.dut.run.return_value.stderr = 'Error!'
130
131        self.dut.run.assert_not_called()
132        device_setup_utils.run_command_on_dut(self.dut, 'run command;')
133        self.dut.run.assert_called_once_with(
134                'run command;', ignore_status=False)
135        # Error status causes log fatal.
136        logging.error.assert_called_once_with(
137                'Command execution on DUT lumpy.cros2 failed.\n'
138                'Failing command: run command;\nreturned 1\n'
139                'Error message: Error!')
140
141
142    def test_run_command_on_dut_ignore_error(self):
143        # Command returns error 1.
144        self.dut.run.return_value.exit_status = 1
145        self.dut.run.return_value.stdout = ''
146        self.dut.run.return_value.stderr = 'Error!'
147
148        self.dut.run.assert_not_called()
149        device_setup_utils.run_command_on_dut(
150                self.dut, 'run command;', ignore_status=True)
151        self.dut.run.assert_called_once_with(
152                'run command;', ignore_status=True)
153        # Error status causes log fatal.
154        calls = [
155                mock.call('Command execution on DUT lumpy.cros2 failed.\n'
156                          'Failing command: run command;\nreturned 1\n'
157                          'Error message: Error!'),
158                mock.call('Failure is considered non-fatal. Continue.'),
159        ]
160        logging.warning.assert_has_calls(calls)
161
162
163    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
164    def test_disable_aslr(self, mock_run_command):
165        mock_run_command.return_value = (0, '', '')
166        device_setup_utils.disable_aslr(self.dut)
167        # pyformat: disable
168        set_cpu_cmd = ('set -e; '
169                       'if [[ -e /proc/sys/kernel/randomize_va_space ]]; then '
170                       '  echo 0 > /proc/sys/kernel/randomize_va_space; '
171                       'fi')
172        mock_run_command.assert_called_once_with(self.dut, set_cpu_cmd)
173
174
175    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
176    def test_set_cpu_governor(self, mock_run_command):
177        mock_run_command.return_value = (0, '', '')
178        device_setup_utils.set_cpu_governor(
179                self.dut, 'new_governor', ignore_status=False)
180        set_cpu_cmd = (
181                'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do'
182                # Skip writing scaling_governor if cpu is offline.
183                '  [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
184                '   && continue; '
185                ' cd $f; '
186                ' if [[ -e scaling_governor ]]; then '
187                '  echo %s > scaling_governor; fi; '
188                'done; ')
189        mock_run_command.assert_called_once_with(
190                self.dut, set_cpu_cmd % 'new_governor', ignore_status=False)
191
192
193    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
194    def test_set_cpu_governor_propagate_error(self, mock_run_command):
195        mock_run_command.return_value = (1, '', 'Error.')
196        device_setup_utils.set_cpu_governor(self.dut, 'non-exist_governor')
197        set_cpu_cmd = (
198                'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do'
199                # Skip writing scaling_governor if cpu is not online.
200                '  [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
201                '   && continue; '
202                ' cd $f; '
203                ' if [[ -e scaling_governor ]]; then '
204                '  echo %s > scaling_governor; fi; '
205                'done; ')
206        # By default error status is fatal.
207        mock_run_command.assert_called_once_with(
208                self.dut,
209                set_cpu_cmd % 'non-exist_governor',
210                ignore_status=False)
211
212
213    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
214    def test_set_cpu_governor_ignore_status(self, mock_run_command):
215        mock_run_command.return_value = (1, '', 'Error.')
216        ret_code = device_setup_utils.set_cpu_governor(
217                self.dut, 'non-exist_governor', ignore_status=True)
218        set_cpu_cmd = (
219                'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do'
220                # Skip writing scaling_governor if cpu is not online.
221                '  [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
222                '   && continue; '
223                ' cd $f; '
224                ' if [[ -e scaling_governor ]]; then '
225                '  echo %s > scaling_governor; fi; '
226                'done; ')
227        mock_run_command.assert_called_once_with(
228                self.dut,
229                set_cpu_cmd % 'non-exist_governor',
230                ignore_status=True)
231        self.assertEqual(ret_code, 1)
232
233
234    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
235    def test_disable_turbo(self, mock_run_command):
236        mock_run_command.return_value = (0, '', '')
237        device_setup_utils.disable_turbo(self.dut)
238        set_cpu_cmd = (
239                # Disable Turbo the in Intel pstate driver.
240                'if [[ -e /sys/devices/system/cpu/intel_pstate/no_turbo ]]; then '
241                '  if grep -q 0 /sys/devices/system/cpu/intel_pstate/no_turbo;  then '
242                '    echo -n 1 > /sys/devices/system/cpu/intel_pstate/no_turbo; '
243                '  fi; '
244                'fi; '
245                # Disable Boost on AMD.
246                'if [[ -e /sys/devices/system/cpu/cpufreq/boost ]]; then '
247                '  if grep -q 1 /sys/devices/system/cpu/cpufreq/boost;  then '
248                '    echo -n 0 > /sys/devices/system/cpu/cpufreq/boost; '
249                '  fi; '
250                'fi; '
251        )
252        mock_run_command.assert_called_once_with(self.dut, set_cpu_cmd)
253
254
255    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
256    def test_get_cpu_online_two(self, mock_run_command):
257        """Test one digit CPU #."""
258        mock_run_command.return_value = (
259                0, '/sys/devices/system/cpu/cpu0/online 0\n'
260                '/sys/devices/system/cpu/cpu1/online 1\n', '')
261        cpu_online = device_setup_utils.get_cpu_online(self.dut)
262        self.assertEqual(cpu_online, {0: 0, 1: 1})
263
264
265    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
266    def test_get_cpu_online_twelve(self, mock_run_command):
267        """Test two digit CPU #."""
268        mock_run_command.return_value = (
269                0, '/sys/devices/system/cpu/cpu0/online 1\n'
270                '/sys/devices/system/cpu/cpu1/online 0\n'
271                '/sys/devices/system/cpu/cpu10/online 1\n'
272                '/sys/devices/system/cpu/cpu11/online 1\n'
273                '/sys/devices/system/cpu/cpu2/online 1\n'
274                '/sys/devices/system/cpu/cpu3/online 0\n'
275                '/sys/devices/system/cpu/cpu4/online 1\n'
276                '/sys/devices/system/cpu/cpu5/online 0\n'
277                '/sys/devices/system/cpu/cpu6/online 1\n'
278                '/sys/devices/system/cpu/cpu7/online 0\n'
279                '/sys/devices/system/cpu/cpu8/online 1\n'
280                '/sys/devices/system/cpu/cpu9/online 0\n', '')
281        cpu_online = device_setup_utils.get_cpu_online(self.dut)
282        self.assertEqual(
283                cpu_online, {
284                        0: 1,
285                        1: 0,
286                        2: 1,
287                        3: 0,
288                        4: 1,
289                        5: 0,
290                        6: 1,
291                        7: 0,
292                        8: 1,
293                        9: 0,
294                        10: 1,
295                        11: 1
296                })
297
298
299    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
300    def test_get_cpu_online_core0_not_exposed(self, mock_run_command):
301        """Test that not exposed cpu0/online will still be in the list."""
302
303        def run_command(dut, cmd):
304            """Helper function."""
305            if '/sys/devices/system/cpu/cpu' in cmd:
306                # Cpu0 online is not exposed.
307                return (0, '/sys/devices/system/cpu/cpu1/online 1\n', '')
308            elif '/sys/devices/system/cpu/online' in cmd:
309                # All online cores shows cpu0.
310                return (0, '0-1', '')
311            else:
312                return (1, '', '')
313
314        mock_run_command.side_effect = run_command
315        cpu_online = device_setup_utils.get_cpu_online(self.dut)
316        # Make sure core0 in the online list.
317        self.assertEqual(cpu_online, {0: 1, 1: 1})
318
319
320    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
321    def test_get_cpu_online_no_output(self, mock_run_command):
322        """Test error case, no output."""
323        mock_run_command.return_value = (0, '', '')
324        with self.assertRaises(AssertionError):
325            device_setup_utils.get_cpu_online(self.dut)
326
327
328    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
329    def test_get_cpu_online_command_error(self, mock_run_command):
330        """Test error case, command error."""
331        mock_run_command.side_effect = AssertionError
332        with self.assertRaises(AssertionError):
333            device_setup_utils.get_cpu_online(self.dut)
334
335
336    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
337    @mock.patch.object(device_setup_utils, 'setup_arm_cores')
338    def test_setup_cpu_usage_little_on_arm(self, mock_setup_arm,
339                                           mock_run_command):
340        device_setup_utils.setup_arm_cores = mock_setup_arm
341        mock_run_command.return_value = (0, 'armv7l', '')
342        cpu_usage = 'little_only'
343        device_setup_utils.setup_cpu_usage(self.dut, cpu_usage)
344        mock_setup_arm.assert_called_once()
345
346
347    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
348    @mock.patch.object(device_setup_utils, 'setup_arm_cores')
349    def test_setup_cpu_usage_big_on_aarch64(self, mock_setup_arm,
350                                            mock_run_command):
351        device_setup_utils.setup_arm_cores = mock_setup_arm
352        mock_run_command.return_value = (0, 'aarch64', '')
353        cpu_usage = 'big_only'
354        device_setup_utils.setup_cpu_usage(self.dut, cpu_usage)
355        mock_setup_arm.assert_called_once()
356
357
358    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
359    @mock.patch.object(device_setup_utils, 'setup_arm_cores')
360    def test_setup_cpu_usage_big_on_intel(self, mock_setup_arm,
361                                          mock_run_command):
362        device_setup_utils.setup_arm_cores = mock_setup_arm
363        mock_run_command.return_value = (0, 'x86_64', '')
364        cpu_usage = 'big_only'
365        device_setup_utils.setup_cpu_usage(self.dut, cpu_usage)
366        # Check that setup_arm_cores not called with invalid setup.
367        mock_setup_arm.assert_not_called()
368
369
370    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
371    @mock.patch.object(device_setup_utils, 'setup_arm_cores')
372    def test_setup_cpu_usage_all_on_intel(self, mock_setup_arm,
373                                          mock_run_command):
374        device_setup_utils.setup_arm_cores = mock_setup_arm
375        mock_run_command.return_value = (0, 'x86_64', '')
376        cpu_usage = 'all'
377        device_setup_utils.setup_cpu_usage(self.dut, cpu_usage)
378        # Check that setup_arm_cores not called in general case.
379        mock_setup_arm.assert_not_called()
380
381
382    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
383    def test_setup_arm_cores_big_on_big_little(self, mock_run_command):
384        mock_run_command.side_effect = [
385                (0, BIG_LITTLE_CPUINFO, ''),
386                (0, '', ''),
387        ]
388        cpu_usage = 'big_only'
389        device_setup_utils.setup_arm_cores(self.dut, cpu_usage)
390        mock_run_command.assert_called_with(
391                self.dut,
392                'echo 1 | tee /sys/devices/system/cpu/cpu{2}/online; '
393                'echo 0 | tee /sys/devices/system/cpu/cpu{0,1}/online')
394
395
396    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
397    def test_setup_arm_cores_little_on_big_little(self, mock_run_command):
398        mock_run_command.side_effect = [
399                (0, BIG_LITTLE_CPUINFO, ''),
400                (0, '', ''),
401        ]
402        cpu_usage = 'little_only'
403        device_setup_utils.setup_arm_cores(self.dut, cpu_usage)
404        mock_run_command.assert_called_with(
405                self.dut,
406                'echo 1 | tee /sys/devices/system/cpu/cpu{0,1}/online; '
407                'echo 0 | tee /sys/devices/system/cpu/cpu{2}/online')
408
409
410    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
411    def test_setup_arm_cores_invalid_config(self, mock_run_command):
412        mock_run_command.side_effect = [
413                (0, LITTLE_ONLY_CPUINFO, ''),
414                (0, '', ''),
415        ]
416        cpu_usage = 'big_only'
417        device_setup_utils.setup_arm_cores(self.dut, cpu_usage)
418        # Check that setup command is not sent when trying
419        # to use 'big_only' on a platform with all little cores.
420        mock_run_command.assert_called_once_with(self.dut, 'cat /proc/cpuinfo')
421
422
423    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
424    def test_setup_arm_cores_not_big_little(self, mock_run_command):
425        mock_run_command.side_effect = [
426                (0, NOT_BIG_LITTLE_CPUINFO, ''),
427                (0, '', ''),
428        ]
429        cpu_usage = 'big_only'
430        device_setup_utils.setup_arm_cores(self.dut, cpu_usage)
431        # Check that setup command is not sent when trying
432        # to use 'big_only' on a platform w/o support of big/little.
433        mock_run_command.assert_called_once_with(self.dut, 'cat /proc/cpuinfo')
434
435
436    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
437    def test_setup_arm_cores_unsupported_cpu_usage(self, mock_run_command):
438        mock_run_command.side_effect = [
439                (0, BIG_LITTLE_CPUINFO, ''),
440                (0, '', ''),
441        ]
442        cpu_usage = 'exclusive_cores'
443        device_setup_utils.setup_arm_cores(self.dut, cpu_usage)
444        # Check that setup command is not sent when trying to use
445        # 'exclusive_cores' on ARM CPU setup.
446        mock_run_command.assert_called_once_with(self.dut, 'cat /proc/cpuinfo')
447
448
449    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
450    def test_setup_cpu_freq_single_full(self, mock_run_command):
451        online = [0]
452        mock_run_command.side_effect = [
453                (0,
454                 '/sys/devices/system/cpu/cpu0/cpufreq/'
455                 'scaling_available_frequencies\n',
456                 ''),
457                (0, '1 2 3 4 5 6 7 8 9 10', ''),
458                (0, '', ''),
459        ]
460        cpu_frq_pct = 100
461        device_setup_utils.setup_cpu_freq(self.dut, cpu_frq_pct, online)
462        self.assertGreaterEqual(mock_run_command.call_count, 3)
463        self.assertIn(
464                mock.call(
465                        self.dut, 'echo 10 | tee '
466                        '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
467                        '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'
468                ),
469                mock_run_command.call_args_list)
470        # Check the fix of single core online.
471        # We expect to see cpu0 instead of cpu{0}.
472        self.assertIn(
473                mock.call(self.dut,
474                          'ls /sys/devices/system/cpu/cpu0/cpufreq/'
475                          'scaling_available_frequencies',
476                          ignore_status=True),
477                mock_run_command.call_args_list)
478
479
480    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
481    def test_setup_cpu_freq_middle(self, mock_run_command):
482        online = [0]
483        mock_run_command.side_effect = [
484                (0,
485                 '/sys/devices/system/cpu/cpu0/cpufreq/'
486                 'scaling_available_frequencies\n',
487                 ''),
488                (0, '1 2 3 4 5 6 7 8 9 10', ''),
489                (0, '', ''),
490        ]
491        cpu_frq_pct = 60
492        device_setup_utils.setup_cpu_freq(self.dut, cpu_frq_pct, online)
493        self.assertGreaterEqual(mock_run_command.call_count, 2)
494        self.assertEqual(
495                mock_run_command.call_args,
496                mock.call(
497                        self.dut, 'echo 6 | tee '
498                        '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
499                        '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'
500                ))
501
502
503    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
504    def test_setup_cpu_freq_lowest(self, mock_run_command):
505        online = [0]
506        mock_run_command.side_effect = [
507                (0,
508                 '/sys/devices/system/cpu/cpu0/cpufreq/'
509                 'scaling_available_frequencies\n',
510                 ''),
511                (0, '1 2 3 4 5 6 7 8 9 10', ''),
512                (0, '', ''),
513        ]
514        cpu_frq_pct = 0
515        device_setup_utils.setup_cpu_freq(self.dut, cpu_frq_pct, online)
516        self.assertGreaterEqual(mock_run_command.call_count, 2)
517        self.assertEqual(
518                mock_run_command.call_args,
519                mock.call(
520                        self.dut, 'echo 1 | tee '
521                        '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
522                        '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'
523                ))
524
525
526    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
527    def test_setup_cpu_freq_multiple_middle(self, mock_run_command):
528        online = [0, 1]
529        mock_run_command.side_effect = [
530                (0,
531                 '/sys/devices/system/cpu/cpu0/cpufreq/'
532                 'scaling_available_frequencies\n'
533                 '/sys/devices/system/cpu/cpu1/cpufreq/'
534                 'scaling_available_frequencies\n',
535                 ''),
536                (0, '1 2 3 4 5 6 7 8 9 10', ''),
537                (0, '', ''),
538                (0, '1 4 6 8 10 12 14 16 18 20', ''),
539                (0, '', ''),
540        ]
541        cpu_frq_pct = 70
542        device_setup_utils.setup_cpu_freq(self.dut, cpu_frq_pct, online)
543        self.assertEqual(mock_run_command.call_count, 5)
544        self.assertIn(
545                mock.call(
546                        self.dut, 'echo 7 | tee '
547                        '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
548                        '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'
549                ),
550                mock_run_command.call_args_list)
551        self.assertIn(
552                mock.call(
553                        self.dut, 'echo 14 | tee '
554                        '/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq '
555                        '/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq'
556                ),
557                mock_run_command.call_args_list)
558        # Check that setup_cpu_freq queries all online cores.
559        self.assertIn(
560                mock.call(self.dut,
561                          'ls /sys/devices/system/cpu/cpu{0,1}/cpufreq/'
562                          'scaling_available_frequencies',
563                          ignore_status=True),
564                mock_run_command.call_args_list)
565
566
567    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
568    def test_setup_cpu_freq_no_scaling_available(self, mock_run_command):
569        online = [0, 1]
570        mock_run_command.return_value = (2, '', 'No such file or directory')
571        cpu_frq_pct = 50
572        device_setup_utils.setup_cpu_freq(self.dut, cpu_frq_pct, online)
573        mock_run_command.assert_called_once()
574        self.assertNotRegexpMatches(mock_run_command.call_args_list[0][0][1],
575                                    '^echo.*scaling_max_freq$')
576
577
578    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
579    def test_setup_cpu_freq_multiple_no_access(self, mock_run_command):
580        online = [0, 1]
581        mock_run_command.side_effect = [
582                (0,
583                 '/sys/devices/system/cpu/cpu0/cpufreq/'
584                 'scaling_available_frequencies\n'
585                 '/sys/devices/system/cpu/cpu1/cpufreq/'
586                 'scaling_available_frequencies\n',
587                 ''),
588                (0, '1 4 6 8 10 12 14 16 18 20', ''),
589                AssertionError(),
590        ]
591        cpu_frq_pct = 30
592        # Error status causes log fatal.
593        with self.assertRaises(AssertionError):
594            device_setup_utils.setup_cpu_freq(self.dut, cpu_frq_pct, online)
595
596
597    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
598    @mock.patch.object(time, 'sleep')
599    def test_wait_cooldown_nowait(self, mock_sleep, mock_run_command):
600        """
601        No cooldown wait.
602
603        Don't wait when the temperature in uC does not exceed 40C.
604        """
605        mock_sleep.return_value = 0
606        mock_run_command.side_effect = [
607                (0, '/sys/class/thermal/thermal_zone0/temp', ''),
608                (0, 'cpu', ''),
609                (0, '39000', ''),
610        ]
611        cooldown_time = 10
612        cooldown_temp = 40
613        wait_time = device_setup_utils.wait_cooldown(self.dut, cooldown_time,
614                                                     cooldown_temp)
615        mock_run_command.assert_called()
616        # Expect no wait time.
617        mock_sleep.assert_not_called()
618        self.assertEqual(wait_time, 0)
619
620
621    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
622    @mock.patch.object(time, 'sleep')
623    def test_wait_cooldown_needwait_once(self, mock_sleep, mock_run_command):
624        """
625        Wait one iteration for cooldown.
626
627        Set large enough timeout and changing temperature output.
628        Make sure it exits when expected value received.
629        """
630        mock_sleep.return_value = 0
631        mock_run_command.side_effect = [
632                (0, '/sys/class/thermal/thermal_zone0/temp', ''),
633                (0, 'cpu', ''),
634                (0, '41000', ''),
635                (0, '39000', ''),
636        ]
637        cooldown_time = 100
638        cooldown_temp = 40
639        wait_time = device_setup_utils.wait_cooldown(self.dut, cooldown_time,
640                                                     cooldown_temp)
641        mock_run_command.assert_called()
642        # Wait time is non-zero.
643        mock_sleep.assert_called()
644        self.assertGreater(wait_time, 0)
645
646
647    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
648    @mock.patch.object(time, 'sleep')
649    def test_wait_cooldown_space_in_thermal_name(self, mock_sleep,
650                                                 mock_run_command):
651        """
652        Wait one iteration for cooldown.
653
654        Make sure the cooldown is working properly when there is a space
655        in the sensor type name.
656        """
657        mock_sleep.return_value = 0
658        mock_run_command.side_effect = [
659                (0, '/sys/class/thermal/thermal_zone0/temp\n'
660                 '/sys/class/thermal/thermal_zone1/temp', ''),
661                (0, 'cpu\ngpu thermal', ''),
662                (0, '39000', ''),
663                (0, '41000', ''),
664                (0, '38000', ''),
665        ]
666        cooldown_time = 10
667        cooldown_temp = 40
668        wait_time = device_setup_utils.wait_cooldown(self.dut, cooldown_time,
669                                                     cooldown_temp)
670        mock_run_command.assert_called()
671        # Expect no wait time.
672        mock_sleep.assert_called_once()
673        self.assertGreater(wait_time, 0)
674
675    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
676    @mock.patch.object(time, 'sleep')
677    def test_wait_cooldown_wait_timeout(self, mock_sleep, mock_run_command):
678        """
679        Test exit by timeout.
680
681        Send command to DUT checking the temperature and
682        check repeatedly until timeout goes off.
683        Output from temperature sensor never changes.
684
685        """
686
687        def constant_temp(temp):
688            """Helper function returns gradually decreasing temperature."""
689            yield (0, '/sys/class/thermal/thermal_zone0/temp', '')
690            yield (0, 'cpu', '')
691            while True:
692                yield (0, str(temp), '')
693
694        mock_sleep.return_value = 0
695        # Set the temperature higher than a default threshold 40k.
696        mock_run_command.side_effect = constant_temp(41000)
697        # Cooldown time - 5 minutes.
698        cooldown_time = 5
699        cooldown_temp = 40
700        wait_time = device_setup_utils.wait_cooldown(self.dut, cooldown_time,
701                                                     cooldown_temp)
702        mock_run_command.assert_called()
703        mock_sleep.assert_called()
704        # Convert cooldown_time to seconds.
705        self.assertEqual(wait_time, cooldown_time * 60)
706
707
708    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
709    @mock.patch.object(time, 'sleep')
710    def test_wait_cooldown_needwait_multtemp(self, mock_sleep,
711                                             mock_run_command):
712        """
713        Wait until all temps go down.
714
715        Set large enough timeout and changing temperature
716        output. Make sure it exits when expected value
717        for all temperatures received.
718        """
719        mock_sleep.return_value = 0
720        mock_run_command.side_effect = [
721                (0, '/sys/class/thermal/thermal_zone0/temp\n'
722                 '/sys/class/thermal/thermal_zone1/temp\n'
723                 '/sys/class/thermal/thermal_zone2/temp', ''),
724                (0, 'cpu0\ncpu1\ngpu', ''),
725                # Iteration 1 of monitoring.
726                (0, '45000', ''),
727                (0, '41000', ''),
728                (0, '20000', ''),
729                # Iteration 2 of monitoring.
730                (0, '42000', ''),
731                (0, '39000', ''),
732                # Iteration 3 of monitoring.
733                (0, '38000', ''),
734                # Monitoring ends.
735        ]
736        cooldown_time = 100
737        cooldown_temp = 40
738        wait_time = device_setup_utils.wait_cooldown(self.dut, cooldown_time,
739                                                     cooldown_temp)
740        mock_run_command.assert_called()
741        # Wait time is non-zero.
742        mock_sleep.assert_called()
743        self.assertGreater(wait_time, 0)
744
745
746    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
747    @mock.patch.object(time, 'sleep')
748    def test_wait_cooldown_ignore_irrelevant_sensor(self, mock_sleep,
749                                                    mock_run_command):
750        """
751        Ignore non cpu/gpu sensors.
752
753        Set large temperature of a non-cpu sensor.
754        Make sure we don't wait if cpu temperature is low
755        regardless of other reports.
756        """
757        mock_sleep.return_value = 0
758        mock_run_command.side_effect = [
759                (0, '/sys/class/thermal/thermal_zone0/temp\n'
760                 '/sys/class/thermal/thermal_zone1/temp', ''),
761                (0, 'cpu0\ncharger-sensor', ''),
762                # Iteration 1 of monitoring, check only cpu0.
763                # cpu0
764                (0, '39000', ''),
765                # Monitoring should stop at this point since the other
766                # sensor is irrelevant.
767                # If it doesn't, the test will fail since the function
768                # will continue monitoring until 50C drops but there is
769                # no more input.
770                (0, '50000', ''),
771        ]
772        cooldown_time = 100
773        cooldown_temp = 40
774        wait_time = device_setup_utils.wait_cooldown(self.dut, cooldown_time,
775                                                     cooldown_temp)
776        mock_run_command.assert_called()
777        # Wait time is zero.
778        mock_sleep.assert_not_called()
779        self.assertEqual(wait_time, 0)
780
781
782    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
783    @mock.patch.object(time, 'sleep')
784    def test_wait_cooldown_thermal_error(self, mock_sleep, mock_run_command):
785        """
786        Handle error status gracefully.
787
788        Sensor with an error is excluded from temperature monitoring.
789        But wait_cooldown still waits.
790        """
791        mock_sleep.return_value = 0
792        # Error status and output with a high temperature.
793        mock_run_command.side_effect = [
794                (0, '/sys/class/thermal/thermal_zone0/temp\n'
795                 '/sys/class/thermal/thermal_zone1/temp', ''),
796                (0, 'cpu0\ncpu1', ''),
797                # Iteration 1 of monitoring.
798                # cpu0
799                (1, '', 'Thernal error'),
800                # Iteration 2 of monitoring.
801                # cpu1
802                (0, '45000', ''),
803                (0, '39000', ''),
804        ]
805        cooldown_time = 10
806        cooldown_temp = 40
807        wait_time = device_setup_utils.wait_cooldown(self.dut, cooldown_time,
808                                                     cooldown_temp)
809        # Wait time is greater than 0.
810        mock_sleep.assert_called()
811        self.assertGreater(wait_time, 0)
812
813
814    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
815    def test_stop_ui(self, mock_run_command):
816        mock_run_command.return_value = (0, '', '')
817        device_setup_utils.stop_ui(self.dut)
818        mock_run_command.assert_called_once_with(self.dut, 'stop ui')
819
820
821    @mock.patch.object(device_setup_utils, 'run_command_on_dut')
822    def test_start_ui(self, mock_run_command):
823        mock_run_command.return_value = (0, '', '')
824        device_setup_utils.start_ui(self.dut)
825        mock_run_command.assert_called_once_with(self.dut, 'start ui')
826
827
828    @mock.patch.object(device_setup_utils, 'kern_cmd_update_needed')
829    @mock.patch.object(device_setup_utils, 'update_kern_cmd_intel_pstate')
830    @mock.patch.object(device_setup_utils, 'disable_aslr')
831    @mock.patch.object(device_setup_utils, 'setup_cpu_usage')
832    @mock.patch.object(device_setup_utils, 'setup_cpu_freq')
833    @mock.patch.object(device_setup_utils, 'get_cpu_online')
834    @mock.patch.object(device_setup_utils, 'set_cpu_governor')
835    @mock.patch.object(device_setup_utils, 'disable_turbo')
836    @mock.patch.object(device_setup_utils, 'stop_ui')
837    @mock.patch.object(device_setup_utils, 'start_ui')
838    @mock.patch.object(device_setup_utils, 'wait_cooldown')
839    @mock.patch.object(device_setup_utils, 'decrease_wait_time')
840    def test_setup_device(self, mock_decrease_wait_time, mock_wait_cooldown,
841                          mock_start_ui, mock_stop_ui, mock_disable_turbo,
842                          mock_set_cpu_governor, mock_get_cpu_online,
843                          mock_setup_cpu_freq, mock_setup_cpu_usage,
844                          mock_disable_aslr, mock_update_kern_cmd_intel_pstate,
845                          mock_kern_cmd_update_needed):
846
847        def setup_mock_functions():
848            """
849            Reset mock functions.
850            """
851            mock_kern_cmd_update_needed.reset_mock()
852            mock_update_kern_cmd_intel_pstate.reset_mock()
853            mock_disable_aslr.reset_mock()
854            mock_setup_cpu_usage.reset_mock()
855            mock_setup_cpu_freq.reset_mock()
856            mock_get_cpu_online.reset_mock()
857            mock_set_cpu_governor.reset_mock()
858            mock_disable_turbo.reset_mock()
859            mock_stop_ui.reset_mock()
860            mock_start_ui.reset_mock()
861            mock_wait_cooldown.reset_mock()
862            mock_decrease_wait_time.reset_mock()
863
864            mock_kern_cmd_update_needed.return_value = True
865            mock_update_kern_cmd_intel_pstate.return_value = 0
866            mock_disable_aslr.return_value = 0
867            mock_setup_cpu_usage.return_value = 0
868            mock_setup_cpu_freq.return_value = 0
869            mock_get_cpu_online.return_value = {0: 1, 1: 1, 2: 0}
870            mock_set_cpu_governor.return_value = 0
871            mock_disable_turbo.return_value = 0
872            mock_stop_ui.return_value = 0
873            mock_start_ui.return_value = 0
874            mock_wait_cooldown.return_value = 0
875            mock_decrease_wait_time.return_value = 0
876
877        dut_config = {
878          'enable_aslr': False,
879          'cooldown_time': 0,
880          'cooldown_temp': 40,
881          'governor': 'fake_governor',
882          'cpu_freq_pct': 65,
883          'intel_pstate': 'no_hwp',
884        }
885
886        setup_mock_functions()
887        device_setup_utils.setup_device(self.dut, dut_config)
888
889        mock_kern_cmd_update_needed.assert_called_once()
890        mock_update_kern_cmd_intel_pstate.assert_called_once()
891        mock_disable_aslr.assert_called_once()
892        mock_setup_cpu_usage.assert_called_once()
893        mock_setup_cpu_freq.assert_called_once_with(
894                self.dut, dut_config['cpu_freq_pct'], [0, 1])
895        mock_get_cpu_online.assert_called_once()
896        mock_set_cpu_governor.assert_called_once_with(self.dut,
897                                                      'fake_governor')
898        mock_disable_turbo.assert_called_once()
899        mock_decrease_wait_time.assert_called_once()
900        mock_stop_ui.assert_called_once()
901        mock_start_ui.assert_called_once()
902        mock_wait_cooldown.assert_not_called()
903
904        # Test SetupDevice with cooldown
905        dut_config['cooldown_time'] = 10
906
907        setup_mock_functions()
908        mock_get_cpu_online.return_value = {0: 0, 1: 1}
909
910        device_setup_utils.setup_device(self.dut, dut_config)
911
912        mock_wait_cooldown.assert_called_once()
913        mock_disable_aslr.assert_called_once()
914        mock_disable_turbo.assert_called_once()
915        mock_setup_cpu_usage.assert_called_once()
916        mock_setup_cpu_freq.assert_called_once_with(
917                self.dut, dut_config['cpu_freq_pct'], [1])
918        mock_set_cpu_governor.assert_called()
919        mock_get_cpu_online.assert_called_once()
920        mock_stop_ui.assert_called_once()
921        mock_start_ui.assert_called_once()
922        self.assertGreater(mock_set_cpu_governor.call_count, 1)
923        self.assertEqual(mock_set_cpu_governor.call_args,
924                         mock.call(self.dut, 'fake_governor'))
925
926        # Test SetupDevice with cooldown
927        setup_mock_functions()
928        mock_setup_cpu_usage.side_effect = RuntimeError()
929
930        with self.assertRaises(RuntimeError):
931            device_setup_utils.setup_device(self.dut, dut_config)
932
933        # This call injected an exception.
934        mock_setup_cpu_usage.assert_called_once()
935        # Calls following the exception are skipped.
936        mock_wait_cooldown.assert_not_called()
937        mock_disable_turbo.assert_not_called()
938        mock_setup_cpu_freq.assert_not_called()
939        mock_set_cpu_governor.assert_not_called()
940        mock_get_cpu_online.assert_not_called()
941        # Check that Stop/Start UI are always called.
942        mock_stop_ui.assert_called_once()
943        mock_start_ui.assert_called_once()
944
945
946if __name__ == '__main__':
947    unittest.main()
948