crosperf: migrate all device setup code to a separate utils file

This patch extracts all the device setup code which interacts with DUT
to a single utils file, and be put into a wrapper class. This will help
migrating all related code to telemetry_Crosperf for skylab runs.

BUG=chromium:1020655
TEST=Passed all unittests; tested with simple experiment on kevin.

Change-Id: I2edcd7bb2d8cd0255d3ae6d380a5983c24427d98
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/1895500
Tested-by: Zhizhou Yang <zhizhouy@google.com>
Reviewed-by: Zhizhou Yang <zhizhouy@google.com>
Reviewed-by: Denis Nikitin <denik@chromium.org>
Commit-Queue: Zhizhou Yang <zhizhouy@google.com>
diff --git a/cros_utils/device_setup_utils.py b/cros_utils/device_setup_utils.py
new file mode 100755
index 0000000..3b2d132
--- /dev/null
+++ b/cros_utils/device_setup_utils.py
@@ -0,0 +1,455 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+#
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Utils for setting devices
+
+This script provides utils to set device specs.
+"""
+
+from __future__ import division
+from __future__ import print_function
+
+__author__ = 'zhizhouy@google.com (Zhizhou Yang)'
+
+import re
+import time
+
+from cros_utils import command_executer
+
+
+class DutWrapper(object):
+  """Wrap DUT parameters inside.
+
+  Eventially CommandExecuter will reqiure only one
+  argument - command.
+  """
+
+  def __init__(self,
+               chromeos_root,
+               remote,
+               log_level='verbose',
+               logger=None,
+               ce=None,
+               dut_config=None):
+    self.chromeos_root = chromeos_root
+    self.remote = remote
+    self.log_level = log_level
+    self.logger = logger
+    self.ce = ce or command_executer.GetCommandExecuter(log_level=log_level)
+    self.dut_config = dut_config
+
+  def RunCommandOnDut(self, command, ignore_status=False):
+    ret, msg, err_msg = self.ce.CrosRunCommandWOutput(
+        command, machine=self.remote, chromeos_root=self.chromeos_root)
+
+    if ret:
+      err_msg = ('Command execution on DUT %s failed.\n'
+                 'Failing command: %s\n'
+                 'returned %d\n'
+                 'Error message: %s' % (self.remote, command, ret, err_msg))
+      if ignore_status:
+        self.logger.LogError(err_msg +
+                             '\n(Failure is considered non-fatal. Continue.)')
+      else:
+        self.logger.LogFatal(err_msg)
+
+    return ret, msg, err_msg
+
+  def DisableASLR(self):
+    disable_aslr = ('set -e; '
+                    'if [[ -e /proc/sys/kernel/randomize_va_space ]]; then '
+                    '  echo 0 > /proc/sys/kernel/randomize_va_space; '
+                    'fi')
+    if self.log_level == 'average':
+      self.logger.LogOutput('Disable ASLR.')
+    self.RunCommandOnDut(disable_aslr, ignore_status=False)
+
+  def SetCpuGovernor(self, governor, ignore_status=False):
+    set_gov_cmd = (
+        'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do '
+        # Skip writing scaling_governor if cpu is offline.
+        ' [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
+        '   && continue; '
+        ' cd $f; '
+        ' if [[ -e scaling_governor ]]; then '
+        '  echo %s > scaling_governor; fi; '
+        'done; ')
+    if self.log_level == 'average':
+      self.logger.LogOutput('Setup CPU Governor: %s.' % governor)
+    ret, _, _ = self.RunCommandOnDut(
+        set_gov_cmd % governor, ignore_status=ignore_status)
+    return ret
+
+  def DisableTurbo(self):
+    dis_turbo_cmd = (
+        'if [[ -e /sys/devices/system/cpu/intel_pstate/no_turbo ]]; then '
+        '  if grep -q 0 /sys/devices/system/cpu/intel_pstate/no_turbo;  then '
+        '    echo -n 1 > /sys/devices/system/cpu/intel_pstate/no_turbo; '
+        '  fi; '
+        'fi; ')
+    if self.log_level == 'average':
+      self.logger.LogOutput('Disable Turbo.')
+    self.RunCommandOnDut(dis_turbo_cmd)
+
+  def SetupCpuUsage(self):
+    """Setup CPU usage.
+
+    Based on self.dut_config['cpu_usage'] configure CPU cores
+    utilization.
+    """
+
+    if (self.dut_config['cpu_usage'] == 'big_only' or
+        self.dut_config['cpu_usage'] == 'little_only'):
+      _, arch, _ = self.RunCommandOnDut('uname -m')
+
+      if arch.lower().startswith('arm') or arch.lower().startswith('aarch64'):
+        self.SetupArmCores()
+
+  def SetupArmCores(self):
+    """Setup ARM big/little cores."""
+
+    # CPU implemeters/part numbers of big/LITTLE CPU.
+    # Format: dict(CPU implementer: set(CPU part numbers))
+    LITTLE_CORES = {
+        '0x41': {
+            '0xd01',  # Cortex A32
+            '0xd03',  # Cortex A53
+            '0xd04',  # Cortex A35
+            '0xd05',  # Cortex A55
+        },
+    }
+    BIG_CORES = {
+        '0x41': {
+            '0xd07',  # Cortex A57
+            '0xd08',  # Cortex A72
+            '0xd09',  # Cortex A73
+            '0xd0a',  # Cortex A75
+            '0xd0b',  # Cortex A76
+        },
+    }
+
+    # Values of CPU Implementer and CPU part number are exposed by cpuinfo.
+    # Format:
+    # =================
+    # processor       : 0
+    # model name      : ARMv8 Processor rev 4 (v8l)
+    # BogoMIPS        : 48.00
+    # Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
+    # CPU implementer : 0x41
+    # CPU architecture: 8
+    # CPU variant     : 0x0
+    # CPU part        : 0xd03
+    # CPU revision    : 4
+
+    _, cpuinfo, _ = self.RunCommandOnDut('cat /proc/cpuinfo')
+
+    # List of all CPU cores: 0, 1, ..
+    proc_matches = re.findall(r'^processor\s*: (\d+)$', cpuinfo, re.MULTILINE)
+    # List of all corresponding CPU implementers
+    impl_matches = re.findall(r'^CPU implementer\s*: (0x[\da-f]+)$', cpuinfo,
+                              re.MULTILINE)
+    # List of all corresponding CPU part numbers
+    part_matches = re.findall(r'^CPU part\s*: (0x[\da-f]+)$', cpuinfo,
+                              re.MULTILINE)
+    assert len(proc_matches) == len(impl_matches)
+    assert len(part_matches) == len(impl_matches)
+
+    all_cores = set(proc_matches)
+    dut_big_cores = {
+        core
+        for core, impl, part in zip(proc_matches, impl_matches, part_matches)
+        if impl in BIG_CORES and part in BIG_CORES[impl]
+    }
+    dut_lit_cores = {
+        core
+        for core, impl, part in zip(proc_matches, impl_matches, part_matches)
+        if impl in LITTLE_CORES and part in LITTLE_CORES[impl]
+    }
+
+    if self.dut_config['cpu_usage'] == 'big_only':
+      cores_to_enable = dut_big_cores
+      cores_to_disable = all_cores - dut_big_cores
+    elif self.dut_config['cpu_usage'] == 'little_only':
+      cores_to_enable = dut_lit_cores
+      cores_to_disable = all_cores - dut_lit_cores
+    else:
+      self.logger.LogError(
+          'cpu_usage=%s is not supported on ARM.\n'
+          'Ignore ARM CPU setup and continue.' % self.dut_config['cpu_usage'])
+      return
+
+    if cores_to_enable:
+      cmd_enable_cores = ('echo 1 | tee /sys/devices/system/cpu/cpu{%s}/online'
+                          % ','.join(sorted(cores_to_enable)))
+
+      cmd_disable_cores = ''
+      if cores_to_disable:
+        cmd_disable_cores = (
+            'echo 0 | tee /sys/devices/system/cpu/cpu{%s}/online' % ','.join(
+                sorted(cores_to_disable)))
+
+      self.RunCommandOnDut('; '.join([cmd_enable_cores, cmd_disable_cores]))
+    else:
+      # If there are no cores enabled by dut_config then configuration
+      # is invalid for current platform and should be ignored.
+      self.logger.LogError(
+          '"cpu_usage" is invalid for targeted platform.\n'
+          'dut_config[cpu_usage]=%s\n'
+          'dut big cores: %s\n'
+          'dut little cores: %s\n'
+          'Ignore ARM CPU setup and continue.' % (self.dut_config['cpu_usage'],
+                                                  dut_big_cores, dut_lit_cores))
+
+  def GetCpuOnline(self):
+    """Get online status of CPU cores.
+
+    Return dict of {int(cpu_num): <0|1>}.
+    """
+    get_cpu_online_cmd = ('paste -d" "'
+                          ' <(ls /sys/devices/system/cpu/cpu*/online)'
+                          ' <(cat /sys/devices/system/cpu/cpu*/online)')
+    _, online_output_str, _ = self.RunCommandOnDut(get_cpu_online_cmd)
+
+    # Here is the output we expect to see:
+    # -----------------
+    # /sys/devices/system/cpu/cpu0/online 0
+    # /sys/devices/system/cpu/cpu1/online 1
+
+    cpu_online = {}
+    cpu_online_match = re.compile(r'^[/\S]+/cpu(\d+)/[/\S]+\s+(\d+)$')
+    for line in online_output_str.splitlines():
+      match = cpu_online_match.match(line)
+      if match:
+        cpu = int(match.group(1))
+        status = int(match.group(2))
+        cpu_online[cpu] = status
+    # At least one CPU has to be online.
+    assert cpu_online
+
+    return cpu_online
+
+  def SetupCpuFreq(self, online_cores):
+    """Setup CPU frequency.
+
+    Based on self.dut_config['cpu_freq_pct'] setup frequency of online CPU cores
+    to a supported value which is less or equal to (freq_pct * max_freq / 100)
+    limited by min_freq.
+
+    NOTE: scaling_available_frequencies support is required.
+    Otherwise the function has no effect.
+    """
+    freq_percent = self.dut_config['cpu_freq_pct']
+    list_all_avail_freq_cmd = ('ls /sys/devices/system/cpu/cpu{%s}/cpufreq/'
+                               'scaling_available_frequencies')
+    # Ignore error to support general usage of frequency setup.
+    # Not all platforms support scaling_available_frequencies.
+    ret, all_avail_freq_str, _ = self.RunCommandOnDut(
+        list_all_avail_freq_cmd % ','.join(str(core) for core in online_cores),
+        ignore_status=True)
+    if ret or not all_avail_freq_str:
+      # No scalable frequencies available for the core.
+      return ret
+    for avail_freq_path in all_avail_freq_str.split():
+      # Get available freq from every scaling_available_frequency path.
+      # Error is considered fatal in self.RunCommandOnDut().
+      _, avail_freq_str, _ = self.RunCommandOnDut('cat ' + avail_freq_path)
+      assert avail_freq_str
+
+      all_avail_freq = sorted(
+          int(freq_str) for freq_str in avail_freq_str.split())
+      min_freq = all_avail_freq[0]
+      max_freq = all_avail_freq[-1]
+      # Calculate the frequency we are targeting.
+      target_freq = round(max_freq * freq_percent / 100)
+      # More likely it's not in the list of supported frequencies
+      # and our goal is to find the one which is less or equal.
+      # Default is min and we will try to maximize it.
+      avail_ngt_target = min_freq
+      # Find the largest not greater than the target.
+      for next_largest in reversed(all_avail_freq):
+        if next_largest <= target_freq:
+          avail_ngt_target = next_largest
+          break
+
+      max_freq_path = avail_freq_path.replace('scaling_available_frequencies',
+                                              'scaling_max_freq')
+      min_freq_path = avail_freq_path.replace('scaling_available_frequencies',
+                                              'scaling_min_freq')
+      # With default ignore_status=False we expect 0 status or Fatal error.
+      self.RunCommandOnDut('echo %s | tee %s %s' %
+                           (avail_ngt_target, max_freq_path, min_freq_path))
+
+  def WaitCooldown(self):
+    waittime = 0
+    timeout_in_sec = int(self.dut_config['cooldown_time']) * 60
+    # Temperature from sensors come in uCelsius units.
+    temp_in_ucels = int(self.dut_config['cooldown_temp']) * 1000
+    sleep_interval = 30
+
+    # Wait until any of two events occurs:
+    # 1. CPU cools down to a specified temperature.
+    # 2. Timeout cooldown_time expires.
+    # For the case when targeted temperature is not reached within specified
+    # timeout the benchmark is going to start with higher initial CPU temp.
+    # In the worst case it may affect test results but at the same time we
+    # guarantee the upper bound of waiting time.
+    # TODO(denik): Report (or highlight) "high" CPU temperature in test results.
+    # "high" should be calculated based on empirical data per platform.
+    # Based on such reports we can adjust CPU configuration or
+    # cooldown limits accordingly.
+    while waittime < timeout_in_sec:
+      _, temp_output, _ = self.RunCommandOnDut(
+          'cat /sys/class/thermal/thermal_zone*/temp', ignore_status=True)
+      if any(int(temp) > temp_in_ucels for temp in temp_output.split()):
+        time.sleep(sleep_interval)
+        waittime += sleep_interval
+      else:
+        # Exit the loop when:
+        # 1. Reported temp numbers from all thermal sensors do not exceed
+        # 'cooldown_temp' or
+        # 2. No data from the sensors.
+        break
+
+    self.logger.LogOutput('Cooldown wait time: %.1f min' % (waittime / 60))
+    return waittime
+
+  def DecreaseWaitTime(self):
+    """Change the ten seconds wait time for pagecycler to two seconds."""
+    FILE = '/usr/local/telemetry/src/tools/perf/page_sets/page_cycler_story.py'
+    ret = self.RunCommandOnDut('ls ' + FILE)
+
+    if not ret:
+      sed_command = 'sed -i "s/_TTI_WAIT_TIME = 10/_TTI_WAIT_TIME = 2/g" '
+      self.RunCommandOnDut(sed_command + FILE)
+
+  def StopUI(self):
+    self.RunCommandOnDut('stop ui')
+
+  def StartUI(self):
+    self.RunCommandOnDut('start ui')
+
+  def KerncmdUpdateNeeded(self, intel_pstate):
+    """Check whether kernel cmdline update is needed.
+
+    Args:
+      intel_pstate: kernel command line argument (active, passive, no_hwp)
+
+    Returns:
+      True if update is needed.
+    """
+
+    good = 0
+
+    # Check that dut platform supports hwp
+    cmd = "grep -q '^flags.*hwp' /proc/cpuinfo"
+    ret_code, _, _ = self.RunCommandOnDut(cmd, ignore_status=True)
+    if ret_code != good:
+      # Intel hwp is not supported, update is not needed.
+      return False
+
+    kern_cmdline_cmd = 'grep -q "intel_pstate=%s" /proc/cmdline' % intel_pstate
+    ret_code, _, _ = self.RunCommandOnDut(kern_cmdline_cmd, ignore_status=True)
+    self.logger.LogOutput('grep /proc/cmdline returned %d' % ret_code)
+    if (intel_pstate and ret_code == good or
+        not intel_pstate and ret_code != good):
+      # No need to updated cmdline if:
+      # 1. We are setting intel_pstate and we found it is already set.
+      # 2. Not using intel_pstate and it is not in cmdline.
+      return False
+
+    # Otherwise we need to update intel_pstate.
+    return True
+
+  def UpdateKerncmdIntelPstate(self, intel_pstate):
+    """Update kernel command line.
+
+    Args:
+      intel_pstate: kernel command line argument (active, passive, no_hwp)
+    """
+
+    good = 0
+
+    # First phase is to remove rootfs verification to allow cmdline change.
+    remove_verif_cmd = ' '.join([
+        '/usr/share/vboot/bin/make_dev_ssd.sh',
+        '--remove_rootfs_verification',
+        '--partition %d',
+    ])
+    # Command for partition 2.
+    verif_part2_failed, _, _ = self.RunCommandOnDut(
+        remove_verif_cmd % 2, ignore_status=True)
+    # Command for partition 4
+    # Some machines in the lab use partition 4 to boot from,
+    # so cmdline should be update for both partitions.
+    verif_part4_failed, _, _ = self.RunCommandOnDut(
+        remove_verif_cmd % 4, ignore_status=True)
+    if verif_part2_failed or verif_part4_failed:
+      self.logger.LogFatal(
+          'ERROR. Failed to update kernel cmdline on partition %d.\n'
+          'Remove verification failed with status %d' %
+          (2 if verif_part2_failed else 4, verif_part2_failed or
+           verif_part4_failed))
+
+    self.RunCommandOnDut('reboot && exit')
+    # Give enough time for dut to complete reboot
+    # TODO(denik): Replace with the function checking machine availability.
+    time.sleep(30)
+
+    # Second phase to update intel_pstate in kernel cmdline.
+    kern_cmdline = '\n'.join([
+        'tmpfile=$(mktemp)',
+        'partnumb=%d',
+        'pstate=%s',
+        # Store kernel cmdline in a temp file.
+        '/usr/share/vboot/bin/make_dev_ssd.sh --partition ${partnumb}'
+        ' --save_config ${tmpfile}',
+        # Remove intel_pstate argument if present.
+        "sed -i -r 's/ intel_pstate=[A-Za-z_]+//g' ${tmpfile}.${partnumb}",
+        # Insert intel_pstate with a new value if it is set.
+        '[[ -n ${pstate} ]] &&'
+        ' sed -i -e \"s/ *$/ intel_pstate=${pstate}/\" ${tmpfile}.${partnumb}',
+        # Save the change in kernel cmdline.
+        # After completion we have to reboot.
+        '/usr/share/vboot/bin/make_dev_ssd.sh --partition ${partnumb}'
+        ' --set_config ${tmpfile}'
+    ])
+    kern_part2_cmdline_cmd = kern_cmdline % (2, intel_pstate)
+    self.logger.LogOutput(
+        'Command to change kernel command line: %s' % kern_part2_cmdline_cmd)
+    upd_part2_failed, _, _ = self.RunCommandOnDut(
+        kern_part2_cmdline_cmd, ignore_status=True)
+    # Again here we are updating cmdline for partition 4
+    # in addition to partition 2. Without this some machines
+    # in the lab might fail.
+    kern_part4_cmdline_cmd = kern_cmdline % (4, intel_pstate)
+    self.logger.LogOutput(
+        'Command to change kernel command line: %s' % kern_part4_cmdline_cmd)
+    upd_part4_failed, _, _ = self.RunCommandOnDut(
+        kern_part4_cmdline_cmd, ignore_status=True)
+    if upd_part2_failed or upd_part4_failed:
+      self.logger.LogFatal(
+          'ERROR. Failed to update kernel cmdline on partition %d.\n'
+          'intel_pstate update failed with status %d' %
+          (2 if upd_part2_failed else 4, upd_part2_failed or upd_part4_failed))
+
+    self.RunCommandOnDut('reboot && exit')
+    # Wait 30s after reboot.
+    time.sleep(30)
+
+    # Verification phase.
+    # Check that cmdline was updated.
+    # Throw an exception if not.
+    kern_cmdline_cmd = 'grep -q "intel_pstate=%s" /proc/cmdline' % intel_pstate
+    ret_code, _, _ = self.RunCommandOnDut(kern_cmdline_cmd, ignore_status=True)
+    if (intel_pstate and ret_code != good or
+        not intel_pstate and ret_code == good):
+      # Kernel cmdline doesn't match input intel_pstate.
+      self.logger.LogFatal(
+          'ERROR. Failed to update kernel cmdline. '
+          'Final verification failed with status %d' % ret_code)
+
+    self.logger.LogOutput('Kernel cmdline updated successfully.')
diff --git a/cros_utils/device_setup_utils_unittest.py b/cros_utils/device_setup_utils_unittest.py
new file mode 100755
index 0000000..162851e
--- /dev/null
+++ b/cros_utils/device_setup_utils_unittest.py
@@ -0,0 +1,618 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+#
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unittest for device_setup_utils."""
+
+from __future__ import print_function
+
+import time
+
+import unittest
+import mock
+
+from device_setup_utils import DutWrapper
+
+from cros_utils import command_executer
+from cros_utils import logger
+
+BIG_LITTLE_CPUINFO = """processor       : 0
+model name      : ARMv8 Processor rev 4 (v8l)
+BogoMIPS        : 48.00
+Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
+CPU implementer : 0x41
+CPU architecture: 8
+CPU variant     : 0x0
+CPU part        : 0xd03
+CPU revision    : 4
+
+processor       : 1
+model name      : ARMv8 Processor rev 4 (v8l)
+BogoMIPS        : 48.00
+Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
+CPU implementer : 0x41
+CPU architecture: 8
+CPU variant     : 0x0
+CPU part        : 0xd03
+CPU revision    : 4
+
+processor       : 2
+model name      : ARMv8 Processor rev 2 (v8l)
+BogoMIPS        : 48.00
+Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
+CPU implementer : 0x41
+CPU architecture: 8
+CPU variant     : 0x0
+CPU part        : 0xd08
+CPU revision    : 2
+"""
+LITTLE_ONLY_CPUINFO = """processor       : 0
+model name      : ARMv8 Processor rev 4 (v8l)
+BogoMIPS        : 48.00
+Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
+CPU implementer : 0x41
+CPU architecture: 8
+CPU variant     : 0x0
+CPU part        : 0xd03
+CPU revision    : 4
+
+processor       : 1
+model name      : ARMv8 Processor rev 4 (v8l)
+BogoMIPS        : 48.00
+Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
+CPU implementer : 0x41
+CPU architecture: 8
+CPU variant     : 0x0
+CPU part        : 0xd03
+CPU revision    : 4
+"""
+
+NOT_BIG_LITTLE_CPUINFO = """processor       : 0
+model name      : ARMv7 Processor rev 1 (v7l)
+Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant     : 0x0
+CPU part        : 0xc0d
+CPU revision    : 1
+
+processor       : 1
+model name      : ARMv7 Processor rev 1 (v7l)
+Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant     : 0x0
+CPU part        : 0xc0d
+CPU revision    : 1
+
+Hardware        : Rockchip (Device Tree)
+Revision        : 0000
+Serial          : 0000000000000000
+"""
+
+
+class DutWrapperTest(unittest.TestCase):
+  """Class of DutWrapper test."""
+  real_logger = logger.GetLogger()
+
+  mock_cmd_exec = mock.Mock(spec=command_executer.CommandExecuter)
+  mock_logger = mock.Mock(spec=logger.Logger)
+
+  def __init__(self, *args, **kwargs):
+    super(DutWrapperTest, self).__init__(*args, **kwargs)
+
+  def setUp(self):
+    self.dw = DutWrapper(
+        '/tmp/chromeos',
+        'lumpy.cros2',
+        log_level='verbose',
+        logger=self.mock_logger,
+        ce=self.mock_cmd_exec,
+        dut_config={})
+
+  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommandWOutput')
+  def test_run_command_on_dut(self, mock_cros_runcmd):
+    self.mock_cmd_exec.CrosRunCommandWOutput = mock_cros_runcmd
+    mock_cros_runcmd.return_value = (0, '', '')
+    mock_cros_runcmd.assert_not_called()
+    self.dw.RunCommandOnDut('run command;')
+    mock_cros_runcmd.assert_called_once_with(
+        'run command;', chromeos_root='/tmp/chromeos', machine='lumpy.cros2')
+
+  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommandWOutput')
+  def test_dut_wrapper_fatal_error(self, mock_cros_runcmd):
+    self.mock_cmd_exec.CrosRunCommandWOutput = mock_cros_runcmd
+    # Command returns error 1.
+    mock_cros_runcmd.return_value = (1, '', 'Error!')
+    mock_cros_runcmd.assert_not_called()
+    self.dw.RunCommandOnDut('run command;')
+    mock_cros_runcmd.assert_called_once_with(
+        'run command;', chromeos_root='/tmp/chromeos', machine='lumpy.cros2')
+    # Error status causes log fatal.
+    self.assertEqual(
+        self.mock_logger.method_calls[-1],
+        mock.call.LogFatal('Command execution on DUT lumpy.cros2 failed.\n'
+                           'Failing command: run command;\nreturned 1\n'
+                           'Error message: Error!'))
+
+  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommandWOutput')
+  def test_dut_wrapper_ignore_error(self, mock_cros_runcmd):
+    self.mock_cmd_exec.CrosRunCommandWOutput = mock_cros_runcmd
+    # Command returns error 1.
+    mock_cros_runcmd.return_value = (1, '', 'Error!')
+    self.dw.RunCommandOnDut('run command;', ignore_status=True)
+    mock_cros_runcmd.assert_called_once_with(
+        'run command;', chromeos_root='/tmp/chromeos', machine='lumpy.cros2')
+    # Error status is not fatal. LogError records the error message.
+    self.assertEqual(
+        self.mock_logger.method_calls[-1],
+        mock.call.LogError('Command execution on DUT lumpy.cros2 failed.\n'
+                           'Failing command: run command;\nreturned 1\n'
+                           'Error message: Error!\n'
+                           '(Failure is considered non-fatal. Continue.)'))
+
+  def test_disable_aslr(self):
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, '', ''))
+    self.dw.DisableASLR()
+    # pyformat: disable
+    set_cpu_cmd = ('set -e; '
+                   'if [[ -e /proc/sys/kernel/randomize_va_space ]]; then '
+                   '  echo 0 > /proc/sys/kernel/randomize_va_space; '
+                   'fi')
+    self.dw.RunCommandOnDut.assert_called_once_with(
+        set_cpu_cmd, ignore_status=False)
+
+  def test_set_cpu_governor(self):
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, '', ''))
+    self.dw.SetCpuGovernor('new_governor', ignore_status=False)
+    set_cpu_cmd = (
+        'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do '
+        # Skip writing scaling_governor if cpu is offline.
+        ' [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
+        '   && continue; '
+        ' cd $f; '
+        ' if [[ -e scaling_governor ]]; then '
+        '  echo %s > scaling_governor; fi; '
+        'done; ')
+    self.dw.RunCommandOnDut.assert_called_once_with(
+        set_cpu_cmd % 'new_governor', ignore_status=False)
+
+  def test_set_cpu_governor_propagate_error(self):
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(1, '', 'Error.'))
+    self.dw.SetCpuGovernor('non-exist_governor')
+    set_cpu_cmd = (
+        'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do '
+        # Skip writing scaling_governor if cpu is not online.
+        ' [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
+        '   && continue; '
+        ' cd $f; '
+        ' if [[ -e scaling_governor ]]; then '
+        '  echo %s > scaling_governor; fi; '
+        'done; ')
+    # By default error status is fatal.
+    self.dw.RunCommandOnDut.assert_called_once_with(
+        set_cpu_cmd % 'non-exist_governor', ignore_status=False)
+
+  def test_set_cpu_governor_ignore_status(self):
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(1, '', 'Error.'))
+    ret_code = self.dw.SetCpuGovernor('non-exist_governor', ignore_status=True)
+    set_cpu_cmd = (
+        'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do '
+        # Skip writing scaling_governor if cpu is not online.
+        ' [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
+        '   && continue; '
+        ' cd $f; '
+        ' if [[ -e scaling_governor ]]; then '
+        '  echo %s > scaling_governor; fi; '
+        'done; ')
+    self.dw.RunCommandOnDut.assert_called_once_with(
+        set_cpu_cmd % 'non-exist_governor', ignore_status=True)
+    self.assertEqual(ret_code, 1)
+
+  def test_disable_turbo(self):
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, '', ''))
+    self.dw.DisableTurbo()
+    set_cpu_cmd = (
+        # Disable Turbo in Intel pstate driver
+        'if [[ -e /sys/devices/system/cpu/intel_pstate/no_turbo ]]; then '
+        '  if grep -q 0 /sys/devices/system/cpu/intel_pstate/no_turbo;  then '
+        '    echo -n 1 > /sys/devices/system/cpu/intel_pstate/no_turbo; '
+        '  fi; '
+        'fi; ')
+    self.dw.RunCommandOnDut.assert_called_once_with(set_cpu_cmd)
+
+  def test_get_cpu_online_two(self):
+    """Test one digit CPU #."""
+    self.dw.RunCommandOnDut = mock.Mock(
+        return_value=(0, '/sys/devices/system/cpu/cpu0/online 0\n'
+                      '/sys/devices/system/cpu/cpu1/online 1\n', ''))
+    cpu_online = self.dw.GetCpuOnline()
+    self.assertEqual(cpu_online, {0: 0, 1: 1})
+
+  def test_get_cpu_online_twelve(self):
+    """Test two digit CPU #."""
+    self.dw.RunCommandOnDut = mock.Mock(
+        return_value=(0, '/sys/devices/system/cpu/cpu0/online 1\n'
+                      '/sys/devices/system/cpu/cpu1/online 0\n'
+                      '/sys/devices/system/cpu/cpu10/online 1\n'
+                      '/sys/devices/system/cpu/cpu11/online 1\n'
+                      '/sys/devices/system/cpu/cpu2/online 1\n'
+                      '/sys/devices/system/cpu/cpu3/online 0\n'
+                      '/sys/devices/system/cpu/cpu4/online 1\n'
+                      '/sys/devices/system/cpu/cpu5/online 0\n'
+                      '/sys/devices/system/cpu/cpu6/online 1\n'
+                      '/sys/devices/system/cpu/cpu7/online 0\n'
+                      '/sys/devices/system/cpu/cpu8/online 1\n'
+                      '/sys/devices/system/cpu/cpu9/online 0\n', ''))
+    cpu_online = self.dw.GetCpuOnline()
+    self.assertEqual(cpu_online, {
+        0: 1,
+        1: 0,
+        2: 1,
+        3: 0,
+        4: 1,
+        5: 0,
+        6: 1,
+        7: 0,
+        8: 1,
+        9: 0,
+        10: 1,
+        11: 1
+    })
+
+  def test_get_cpu_online_no_output(self):
+    """Test error case, no output."""
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, '', ''))
+    with self.assertRaises(AssertionError):
+      self.dw.GetCpuOnline()
+
+  def test_get_cpu_online_command_error(self):
+    """Test error case, command error."""
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=AssertionError)
+    with self.assertRaises(AssertionError):
+      self.dw.GetCpuOnline()
+
+  @mock.patch.object(DutWrapper, 'SetupArmCores')
+  def test_setup_cpu_usage_little_on_arm(self, mock_setup_arm):
+    self.dw.SetupArmCores = mock_setup_arm
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, 'armv7l', ''))
+    self.dw.dut_config['cpu_usage'] = 'little_only'
+    self.dw.SetupCpuUsage()
+    self.dw.SetupArmCores.assert_called_once_with()
+
+  @mock.patch.object(DutWrapper, 'SetupArmCores')
+  def test_setup_cpu_usage_big_on_aarch64(self, mock_setup_arm):
+    self.dw.SetupArmCores = mock_setup_arm
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, 'aarch64', ''))
+    self.dw.dut_config['cpu_usage'] = 'big_only'
+    self.dw.SetupCpuUsage()
+    self.dw.SetupArmCores.assert_called_once_with()
+
+  @mock.patch.object(DutWrapper, 'SetupArmCores')
+  def test_setup_cpu_usage_big_on_intel(self, mock_setup_arm):
+    self.dw.SetupArmCores = mock_setup_arm
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, 'x86_64', ''))
+    self.dw.dut_config['cpu_usage'] = 'big_only'
+    self.dw.SetupCpuUsage()
+    # Check that SetupArmCores not called with invalid setup.
+    self.dw.SetupArmCores.assert_not_called()
+
+  @mock.patch.object(DutWrapper, 'SetupArmCores')
+  def test_setup_cpu_usage_all_on_intel(self, mock_setup_arm):
+    self.dw.SetupArmCores = mock_setup_arm
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, 'x86_64', ''))
+    self.dw.dut_config['cpu_usage'] = 'all'
+    self.dw.SetupCpuUsage()
+    # Check that SetupArmCores not called in general case.
+    self.dw.SetupArmCores.assert_not_called()
+
+  def test_setup_arm_cores_big_on_big_little(self):
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0, BIG_LITTLE_CPUINFO, ''),
+        (0, '', ''),
+    ])
+    self.dw.dut_config['cpu_usage'] = 'big_only'
+    self.dw.SetupArmCores()
+    self.dw.RunCommandOnDut.assert_called_with(
+        'echo 1 | tee /sys/devices/system/cpu/cpu{2}/online; '
+        'echo 0 | tee /sys/devices/system/cpu/cpu{0,1}/online')
+
+  def test_setup_arm_cores_little_on_big_little(self):
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0, BIG_LITTLE_CPUINFO, ''),
+        (0, '', ''),
+    ])
+    self.dw.dut_config['cpu_usage'] = 'little_only'
+    self.dw.SetupArmCores()
+    self.dw.RunCommandOnDut.assert_called_with(
+        'echo 1 | tee /sys/devices/system/cpu/cpu{0,1}/online; '
+        'echo 0 | tee /sys/devices/system/cpu/cpu{2}/online')
+
+  def test_setup_arm_cores_invalid_config(self):
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0, LITTLE_ONLY_CPUINFO, ''),
+        (0, '', ''),
+    ])
+    self.dw.dut_config['cpu_usage'] = 'big_only'
+    self.dw.SetupArmCores()
+    # Check that setup command is not sent when trying
+    # to use 'big_only' on a platform with all little cores.
+    self.dw.RunCommandOnDut.assert_called_once_with('cat /proc/cpuinfo')
+
+  def test_setup_arm_cores_not_big_little(self):
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0, NOT_BIG_LITTLE_CPUINFO, ''),
+        (0, '', ''),
+    ])
+    self.dw.dut_config['cpu_usage'] = 'big_only'
+    self.dw.SetupArmCores()
+    # Check that setup command is not sent when trying
+    # to use 'big_only' on a platform w/o support of big/little.
+    self.dw.RunCommandOnDut.assert_called_once_with('cat /proc/cpuinfo')
+
+  def test_setup_arm_cores_unsupported_cpu_usage(self):
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0, BIG_LITTLE_CPUINFO, ''),
+        (0, '', ''),
+    ])
+    self.dw.dut_config['cpu_usage'] = 'exclusive_cores'
+    self.dw.SetupArmCores()
+    # Check that setup command is not sent when trying to use
+    # 'exclusive_cores' on ARM CPU setup.
+    self.dw.RunCommandOnDut.assert_called_once_with('cat /proc/cpuinfo')
+
+  def test_setup_cpu_freq_single_full(self):
+    online = [0]
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0,
+         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n',
+         ''),
+        (0, '1 2 3 4 5 6 7 8 9 10', ''),
+        (0, '', ''),
+    ])
+    self.dw.dut_config['cpu_freq_pct'] = 100
+    self.dw.SetupCpuFreq(online)
+    self.assertGreaterEqual(self.dw.RunCommandOnDut.call_count, 3)
+    self.assertEqual(
+        self.dw.RunCommandOnDut.call_args,
+        mock.call('echo 10 | tee '
+                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
+                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'))
+
+  def test_setup_cpu_freq_middle(self):
+    online = [0]
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0,
+         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n',
+         ''),
+        (0, '1 2 3 4 5 6 7 8 9 10', ''),
+        (0, '', ''),
+    ])
+    self.dw.dut_config['cpu_freq_pct'] = 60
+    self.dw.SetupCpuFreq(online)
+    self.assertGreaterEqual(self.dw.RunCommandOnDut.call_count, 2)
+    self.assertEqual(
+        self.dw.RunCommandOnDut.call_args,
+        mock.call('echo 6 | tee '
+                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
+                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'))
+
+  def test_setup_cpu_freq_lowest(self):
+    online = [0]
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0,
+         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n',
+         ''),
+        (0, '1 2 3 4 5 6 7 8 9 10', ''),
+        (0, '', ''),
+    ])
+    self.dw.dut_config['cpu_freq_pct'] = 0
+    self.dw.SetupCpuFreq(online)
+    self.assertGreaterEqual(self.dw.RunCommandOnDut.call_count, 2)
+    self.assertEqual(
+        self.dw.RunCommandOnDut.call_args,
+        mock.call('echo 1 | tee '
+                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
+                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'))
+
+  def test_setup_cpu_freq_multiple_middle(self):
+    online = [0, 1]
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0,
+         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n'
+         '/sys/devices/system/cpu/cpu1/cpufreq/scaling_available_frequencies\n',
+         ''),
+        (0, '1 2 3 4 5 6 7 8 9 10', ''),
+        (0, '', ''),
+        (0, '1 4 6 8 10 12 14 16 18 20', ''),
+        (0, '', ''),
+    ])
+    self.dw.dut_config['cpu_freq_pct'] = 70
+    self.dw.SetupCpuFreq(online)
+    self.assertEqual(self.dw.RunCommandOnDut.call_count, 5)
+    self.assertEqual(
+        self.dw.RunCommandOnDut.call_args_list[2],
+        mock.call('echo 7 | tee '
+                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
+                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'))
+    self.assertEqual(
+        self.dw.RunCommandOnDut.call_args_list[4],
+        mock.call('echo 14 | tee '
+                  '/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq '
+                  '/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq'))
+
+  def test_setup_cpu_freq_no_scaling_available(self):
+    online = [0, 1]
+    self.dw.RunCommandOnDut = mock.Mock(
+        return_value=(2, '', 'No such file or directory'))
+    self.dw.dut_config['cpu_freq_pct'] = 50
+    self.dw.SetupCpuFreq(online)
+    self.dw.RunCommandOnDut.assert_called_once()
+    self.assertNotRegexpMatches(self.dw.RunCommandOnDut.call_args_list[0][0][0],
+                                '^echo.*scaling_max_freq$')
+
+  def test_setup_cpu_freq_multiple_no_access(self):
+    online = [0, 1]
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0,
+         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n'
+         '/sys/devices/system/cpu/cpu1/cpufreq/scaling_available_frequencies\n',
+         ''),
+        (0, '1 4 6 8 10 12 14 16 18 20', ''),
+        AssertionError(),
+    ])
+    self.dw.dut_config['cpu_freq_pct'] = 30
+    # Error status causes log fatal.
+    with self.assertRaises(AssertionError):
+      self.dw.SetupCpuFreq(online)
+
+  @mock.patch.object(time, 'sleep')
+  def test_wait_cooldown_nowait(self, mock_sleep):
+    mock_sleep.return_value = 0
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, '39000', ''))
+    self.dw.dut_config['cooldown_time'] = 10
+    self.dw.dut_config['cooldown_temp'] = 40
+    wait_time = self.dw.WaitCooldown()
+    # Send command to DUT only once to check temperature
+    # and make sure it does not exceed the threshold.
+    self.dw.RunCommandOnDut.assert_called_once()
+    mock_sleep.assert_not_called()
+    self.assertEqual(wait_time, 0)
+
+  @mock.patch.object(time, 'sleep')
+  def test_wait_cooldown_needwait_once(self, mock_sleep):
+    """Wait one iteration for cooldown.
+
+    Set large enough timeout and changing temperature
+    output. Make sure it exits when expected value
+    received.
+    Expect that WaitCooldown check temp twice.
+    """
+    mock_sleep.return_value = 0
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[(0, '41000',
+                                                      ''), (0, '39999', '')])
+    self.dw.dut_config['cooldown_time'] = 100
+    self.dw.dut_config['cooldown_temp'] = 40
+    wait_time = self.dw.WaitCooldown()
+    self.dw.RunCommandOnDut.assert_called()
+    self.assertEqual(self.dw.RunCommandOnDut.call_count, 2)
+    mock_sleep.assert_called()
+    self.assertGreater(wait_time, 0)
+
+  @mock.patch.object(time, 'sleep')
+  def test_wait_cooldown_needwait(self, mock_sleep):
+    """Test exit by timeout.
+
+    Send command to DUT checking the temperature and
+    check repeatedly until timeout goes off.
+    Output from temperature sensor never changes.
+    """
+    mock_sleep.return_value = 0
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, '41000', ''))
+    self.dw.dut_config['cooldown_time'] = 60
+    self.dw.dut_config['cooldown_temp'] = 40
+    wait_time = self.dw.WaitCooldown()
+    self.dw.RunCommandOnDut.assert_called()
+    self.assertGreater(self.dw.RunCommandOnDut.call_count, 2)
+    mock_sleep.assert_called()
+    self.assertGreater(wait_time, 0)
+
+  @mock.patch.object(time, 'sleep')
+  def test_wait_cooldown_needwait_multtemp(self, mock_sleep):
+    """Wait until all temps go down.
+
+    Set large enough timeout and changing temperature
+    output. Make sure it exits when expected value
+    for all temperatures received.
+    Expect 3 checks.
+    """
+    mock_sleep.return_value = 0
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (0, '41000\n20000\n30000\n45000', ''),
+        (0, '39000\n20000\n30000\n41000', ''),
+        (0, '39000\n20000\n30000\n31000', ''),
+    ])
+    self.dw.dut_config['cooldown_time'] = 100
+    self.dw.dut_config['cooldown_temp'] = 40
+    wait_time = self.dw.WaitCooldown()
+    self.dw.RunCommandOnDut.assert_called()
+    self.assertEqual(self.dw.RunCommandOnDut.call_count, 3)
+    mock_sleep.assert_called()
+    self.assertGreater(wait_time, 0)
+
+  @mock.patch.object(time, 'sleep')
+  def test_wait_cooldown_thermal_error(self, mock_sleep):
+    """Handle error status.
+
+    Any error should be considered non-fatal.
+    """
+    mock_sleep.return_value = 0
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[
+        (1, '39000\n20000\n30000\n41000', 'Thermal error'),
+        (1, '39000\n20000\n30000\n31000', 'Thermal error'),
+    ])
+    self.dw.dut_config['cooldown_time'] = 10
+    self.dw.dut_config['cooldown_temp'] = 40
+    wait_time = self.dw.WaitCooldown()
+    # Check that errors are ignored.
+    self.dw.RunCommandOnDut.assert_called_with(
+        'cat /sys/class/thermal/thermal_zone*/temp', ignore_status=True)
+    self.assertEqual(self.dw.RunCommandOnDut.call_count, 2)
+    # Check that we are waiting even when an error is returned
+    # as soon as data is coming.
+    mock_sleep.assert_called()
+    self.assertGreater(wait_time, 0)
+
+  @mock.patch.object(time, 'sleep')
+  def test_wait_cooldown_thermal_no_output(self, mock_sleep):
+    """Handle no output.
+
+    Check handling of empty stdout.
+    """
+    mock_sleep.return_value = 0
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[(1, '', 'Thermal error')])
+    self.dw.dut_config['cooldown_time'] = 10
+    self.dw.dut_config['cooldown_temp'] = 40
+    wait_time = self.dw.WaitCooldown()
+    # Check that errors are ignored.
+    self.dw.RunCommandOnDut.assert_called_once_with(
+        'cat /sys/class/thermal/thermal_zone*/temp', ignore_status=True)
+    # No wait.
+    mock_sleep.assert_not_called()
+    self.assertEqual(wait_time, 0)
+
+  @mock.patch.object(time, 'sleep')
+  def test_wait_cooldown_thermal_ws_output(self, mock_sleep):
+    """Handle whitespace output.
+
+    Check handling of whitespace only.
+    """
+    mock_sleep.return_value = 0
+    self.dw.RunCommandOnDut = mock.Mock(side_effect=[(1, '\n',
+                                                      'Thermal error')])
+    self.dw.dut_config['cooldown_time'] = 10
+    self.dw.dut_config['cooldown_temp'] = 40
+    wait_time = self.dw.WaitCooldown()
+    # Check that errors are ignored.
+    self.dw.RunCommandOnDut.assert_called_once_with(
+        'cat /sys/class/thermal/thermal_zone*/temp', ignore_status=True)
+    # No wait.
+    mock_sleep.assert_not_called()
+    self.assertEqual(wait_time, 0)
+
+  def test_stop_ui(self):
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, '', ''))
+    self.dw.StopUI()
+    self.dw.RunCommandOnDut.assert_called_once_with('stop ui')
+
+  def test_start_ui(self):
+    self.dw.RunCommandOnDut = mock.Mock(return_value=(0, '', ''))
+    self.dw.StartUI()
+    self.dw.RunCommandOnDut.assert_called_once_with('start ui')
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/crosperf/schedv2.py b/crosperf/schedv2.py
index dea3cdd..d15f18c 100644
--- a/crosperf/schedv2.py
+++ b/crosperf/schedv2.py
@@ -9,7 +9,6 @@
 from __future__ import print_function
 
 import sys
-import time
 import traceback
 
 from collections import defaultdict
@@ -21,6 +20,7 @@
 from machine_image_manager import MachineImageManager
 from cros_utils import command_executer
 from cros_utils import logger
+from cros_utils.device_setup_utils import DutWrapper
 
 
 class DutWorker(Thread):
@@ -49,153 +49,6 @@
         # suite_runner.Terminate and updates timeline.
         self._active_br.Terminate()
 
-  def _kerncmd_update_needed(self, intel_pstate):
-    """Check whether kernel cmdline update is needed.
-
-    Args:
-      intel_pstate: kernel command line argument (active, passive, no_hwp)
-
-    Returns:
-      True if update is needed.
-    """
-
-    ce = command_executer.GetCommandExecuter()
-    good = 0
-
-    # Check that dut platform supports hwp
-    cmd = "grep -q '^flags.*hwp' /proc/cpuinfo"
-    ret_code = ce.CrosRunCommand(
-        cmd,
-        chromeos_root=self._sched.get_labels(0).chromeos_root,
-        machine=self._dut.name)
-    if ret_code != good:
-      # Intel hwp is not supported, update is not needed.
-      return False
-
-    kern_cmdline_cmd = 'grep -q "intel_pstate=%s" /proc/cmdline' % intel_pstate
-    ret_code = ce.CrosRunCommand(
-        kern_cmdline_cmd,
-        chromeos_root=self._sched.get_labels(0).chromeos_root,
-        machine=self._dut.name)
-    self._logger.LogOutput('grep /proc/cmdline returned %d' % ret_code)
-    if (intel_pstate and ret_code == good or
-        not intel_pstate and ret_code != good):
-      # No need to updated cmdline if:
-      # 1. We are setting intel_pstate and we found it is already set.
-      # 2. Not using intel_pstate and it is not in cmdline.
-      return False
-
-    # Otherwise we need to update intel_pstate.
-    return True
-
-  def _update_kerncmd_intel_pstate(self, intel_pstate):
-    """Update kernel command line.
-
-    Args:
-      intel_pstate: kernel command line argument (active, passive, no_hwp)
-    """
-
-    ce = command_executer.GetCommandExecuter()
-    good = 0
-
-    # First phase is to remove rootfs verification to allow cmdline change.
-    remove_verif_cmd = ' '.join([
-        '/usr/share/vboot/bin/make_dev_ssd.sh',
-        '--remove_rootfs_verification',
-        '--partition %d',
-    ])
-    # Command for partition 2.
-    verif_part2_failed = ce.CrosRunCommand(
-        remove_verif_cmd % 2,
-        chromeos_root=self._sched.get_labels(0).chromeos_root,
-        machine=self._dut.name)
-    # Command for partition 4
-    # Some machines in the lab use partition 4 to boot from,
-    # so cmdline should be update for both partitions.
-    verif_part4_failed = ce.CrosRunCommand(
-        remove_verif_cmd % 4,
-        chromeos_root=self._sched.get_labels(0).chromeos_root,
-        machine=self._dut.name)
-    if verif_part2_failed or verif_part4_failed:
-      self._logger.LogFatal(
-          'ERROR. Failed to update kernel cmdline on partition %d.\n'
-          'Remove verification failed with status %d' %
-          (2 if verif_part2_failed else 4, verif_part2_failed or
-           verif_part4_failed))
-
-    ce.CrosRunCommand(
-        'reboot && exit',
-        chromeos_root=self._sched.get_labels(0).chromeos_root,
-        machine=self._dut.name)
-    # Give enough time for dut to complete reboot
-    # TODO(denik): Replace with the function checking machine availability.
-    time.sleep(30)
-
-    # Second phase to update intel_pstate in kernel cmdline.
-    kern_cmdline = '\n'.join([
-        'tmpfile=$(mktemp)',
-        'partnumb=%d',
-        'pstate=%s',
-        # Store kernel cmdline in a temp file.
-        '/usr/share/vboot/bin/make_dev_ssd.sh --partition ${partnumb}'
-        ' --save_config ${tmpfile}',
-        # Remove intel_pstate argument if present.
-        "sed -i -r 's/ intel_pstate=[A-Za-z_]+//g' ${tmpfile}.${partnumb}",
-        # Insert intel_pstate with a new value if it is set.
-        '[[ -n ${pstate} ]] &&'
-        ' sed -i -e \"s/ *$/ intel_pstate=${pstate}/\" ${tmpfile}.${partnumb}',
-        # Save the change in kernel cmdline.
-        # After completion we have to reboot.
-        '/usr/share/vboot/bin/make_dev_ssd.sh --partition ${partnumb}'
-        ' --set_config ${tmpfile}'
-    ])
-    kern_part2_cmdline_cmd = kern_cmdline % (2, intel_pstate)
-    self._logger.LogOutput(
-        'Command to change kernel command line: %s' % kern_part2_cmdline_cmd)
-    upd_part2_failed = ce.CrosRunCommand(
-        kern_part2_cmdline_cmd,
-        chromeos_root=self._sched.get_labels(0).chromeos_root,
-        machine=self._dut.name)
-    # Again here we are updating cmdline for partition 4
-    # in addition to partition 2. Without this some machines
-    # in the lab might fail.
-    kern_part4_cmdline_cmd = kern_cmdline % (4, intel_pstate)
-    self._logger.LogOutput(
-        'Command to change kernel command line: %s' % kern_part4_cmdline_cmd)
-    upd_part4_failed = ce.CrosRunCommand(
-        kern_part4_cmdline_cmd,
-        chromeos_root=self._sched.get_labels(0).chromeos_root,
-        machine=self._dut.name)
-    if upd_part2_failed or upd_part4_failed:
-      self._logger.LogFatal(
-          'ERROR. Failed to update kernel cmdline on partition %d.\n'
-          'intel_pstate update failed with status %d' %
-          (2 if upd_part2_failed else 4, upd_part2_failed or upd_part4_failed))
-
-    ce.CrosRunCommand(
-        'reboot && exit',
-        chromeos_root=self._sched.get_labels(0).chromeos_root,
-        machine=self._dut.name)
-    # Wait 30s after reboot.
-    time.sleep(30)
-
-    # Verification phase.
-    # Check that cmdline was updated.
-    # Throw an exception if not.
-    kern_cmdline_cmd = 'grep -q "intel_pstate=%s" /proc/cmdline' % intel_pstate
-    ret_code = ce.CrosRunCommand(
-        kern_cmdline_cmd,
-        chromeos_root=self._sched.get_labels(0).chromeos_root,
-        machine=self._dut.name)
-    if (intel_pstate and ret_code != good or
-        not intel_pstate and ret_code == good):
-      # Kernel cmdline doesn't match input intel_pstate.
-      self._logger.LogFatal(
-          'ERROR. Failed to update kernel cmdline. '
-          'Final verification failed with status %d' % ret_code)
-
-    self._logger.LogOutput('Kernel cmdline updated successfully.')
-
   def run(self):
     """Do the "run-test->(optionally reimage)->run-test" chore.
 
@@ -238,8 +91,12 @@
         else:
           self._logger.LogOutput('Update kernel cmdline if necessary '
                                  'and reboot')
-          if self._kerncmd_update_needed(intel_pstate):
-            self._update_kerncmd_intel_pstate(intel_pstate)
+          run_on_dut = DutWrapper(
+              self._sched.get_labels(0).chromeos_root,
+              self._dut.name,
+              logger=self._logger)
+          if run_on_dut.KerncmdUpdateNeeded(intel_pstate):
+            run_on_dut.UpdateKerncmdIntelPstate(intel_pstate)
 
           # Execute the br.
           self._execute_benchmark_run(br)
diff --git a/crosperf/suite_runner.py b/crosperf/suite_runner.py
index fbacf17..2311a72 100644
--- a/crosperf/suite_runner.py
+++ b/crosperf/suite_runner.py
@@ -9,11 +9,11 @@
 from __future__ import print_function
 
 import os
-import re
 import shlex
 import time
 
 from cros_utils import command_executer
+from cros_utils.device_setup_utils import DutWrapper
 
 TEST_THAT_PATH = '/usr/bin/test_that'
 # TODO: Need to check whether Skylab is installed and set up correctly.
@@ -69,102 +69,29 @@
     self.enable_aslr = enable_aslr
     self.dut_config = dut_config
 
-  def DutWrapper(self, machine_name, chromeos_root):
-    """Wrap DUT parameters inside.
-
-    Eventially CommandExecuter will reqiure only one
-    argument - command.
-    """
-
-    def RunCommandOnDut(command, ignore_status=False):
-      ret, msg, err_msg = self._ce.CrosRunCommandWOutput(
-          command, machine=machine_name, chromeos_root=chromeos_root)
-
-      if ret:
-        err_msg = ('Command execution on DUT %s failed.\n'
-                   'Failing command: %s\n'
-                   'returned %d\n'
-                   'Error message: %s' % (machine_name, command, ret, err_msg))
-        if ignore_status:
-          self.logger.LogError(err_msg +
-                               '\n(Failure is considered non-fatal. Continue.)')
-        else:
-          self.logger.LogFatal(err_msg)
-
-      return ret, msg, err_msg
-
-    return RunCommandOnDut
-
   def Run(self, cros_machine, label, benchmark, test_args, profiler_args):
     machine_name = cros_machine.name
     if not label.skylab:
       # Initialize command executer on DUT.
-      run_on_dut = self.DutWrapper(machine_name, label.chromeos_root)
+      run_on_dut = DutWrapper(
+          label.chromeos_root,
+          machine_name,
+          logger=self.logger,
+          log_level=self.log_level,
+          ce=self._ce,
+          dut_config=self.dut_config)
     for i in range(0, benchmark.retries + 1):
       if label.skylab:
-        # TODO: need to migrate DisableASLR and PinGovernorExecutionFrequencies
-        # since in skylab mode, we may not know the DUT until one is assigned
+        # TODO: need to migrate Device setups to autotest for skylab.
+        # Since in skylab mode, we may not know the DUT until one is assigned
         # to the test. For telemetry_Crosperf run, we can move them into the
         # server test script, for client runs, need to figure out wrapper to do
         # it before running.
         ret_tup = self.Skylab_Run(label, benchmark, test_args, profiler_args)
       else:
-        # Stop UI before configuring the DUT.
-        # This will accelerate setup (waiting for cooldown has x10 drop)
-        # and help to reset a Chrome state left after the previous test.
-        self.StopUI(run_on_dut)
-
-        # Unless the user turns on ASLR in the flag, we first disable ASLR
-        # before running the benchmarks
-        if not self.enable_aslr:
-          self.DisableASLR(run_on_dut)
-
-        # CPU usage setup comes first where we enable/disable cores.
-        self.SetupCpuUsage(run_on_dut)
-        cpu_online_status = self.GetCpuOnline(run_on_dut)
-        # List of online cores of type int (core number).
-        online_cores = [
-            core for core, status in cpu_online_status.items() if status
-        ]
-        if self.dut_config['cooldown_time']:
-          # Setup power conservative mode for effective cool down.
-          # Set ignore status since powersave may no be available
-          # on all platforms and we are going to handle it.
-          ret = self.SetCpuGovernor('powersave', run_on_dut, ignore_status=True)
-          if ret:
-            # "powersave" is not available, use "ondemand".
-            # Still not a fatal error if it fails.
-            ret = self.SetCpuGovernor(
-                'ondemand', run_on_dut, ignore_status=True)
-          # TODO(denik): Run comparison test for 'powersave' and 'ondemand'
-          # on scarlet and kevin64.
-          # We might have to consider reducing freq manually to the min
-          # if it helps to reduce waiting time.
-          wait_time = self.WaitCooldown(run_on_dut)
-          cros_machine.AddCooldownWaitTime(wait_time)
-
-        # Setup CPU governor for the benchmark run.
-        # It overwrites the previous governor settings.
-        governor = self.dut_config['governor']
-        # FIXME(denik): Pass online cores to governor setup.
-        self.SetCpuGovernor(governor, run_on_dut, ignore_status=False)
-
-        # Disable Turbo and Setup CPU freq should ALWAYS proceed governor setup
-        # since governor may change:
-        # - frequency;
-        # - turbo/boost.
-        self.DisableTurbo(run_on_dut)
-        self.SetupCpuFreq(run_on_dut, online_cores)
-        # FIXME(denik): Currently we are not recovering the previous cpufreq
-        # settings since we do reboot/setup every time anyway.
-        # But it may change in the future and then we have to recover the
-        # settings.
-
-        # DUT setup is done. Start a fresh new shiny UI.
-        self.StartUI(run_on_dut)
-
+        self.SetupDevice(run_on_dut, cros_machine)
         if benchmark.suite == 'telemetry_Crosperf':
-          self.DecreaseWaitTime(run_on_dut)
+          run_on_dut.DecreaseWaitTime()
           ret_tup = self.Telemetry_Crosperf_Run(machine_name, label, benchmark,
                                                 test_args, profiler_args)
         else:
@@ -184,277 +111,59 @@
         break
     return ret_tup
 
-  def DisableASLR(self, run_on_dut):
-    disable_aslr = ('set -e && '
-                    'if [[ -e /proc/sys/kernel/randomize_va_space ]]; then '
-                    '  echo 0 > /proc/sys/kernel/randomize_va_space; '
-                    'fi')
-    if self.log_level == 'average':
-      self.logger.LogOutput('Disable ASLR.')
-    run_on_dut(disable_aslr)
+  def SetupDevice(self, run_on_dut, cros_machine):
+    # Stop UI before configuring the DUT.
+    # This will accelerate setup (waiting for cooldown has x10 drop)
+    # and help to reset a Chrome state left after the previous test.
+    run_on_dut.StopUI()
 
-  def SetCpuGovernor(self, governor, run_on_dut, ignore_status=False):
-    set_gov_cmd = (
-        'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do '
-        # Skip writing scaling_governor if cpu is offline.
-        ' [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
-        '   && continue; '
-        ' cd $f; '
-        ' if [[ -e scaling_governor ]]; then '
-        '  echo %s > scaling_governor; fi; '
-        'done; ')
-    if self.log_level == 'average':
-      self.logger.LogOutput('Setup CPU Governor: %s.' % governor)
-    ret, _, _ = run_on_dut(set_gov_cmd % governor, ignore_status=ignore_status)
-    return ret
+    # Unless the user turns on ASLR in the flag, we first disable ASLR
+    # before running the benchmarks
+    if not self.enable_aslr:
+      run_on_dut.DisableASLR()
 
-  def DisableTurbo(self, run_on_dut):
-    dis_turbo_cmd = (
-        'if [[ -e /sys/devices/system/cpu/intel_pstate/no_turbo ]]; then '
-        '  if grep -q 0 /sys/devices/system/cpu/intel_pstate/no_turbo;  then '
-        '    echo -n 1 > /sys/devices/system/cpu/intel_pstate/no_turbo; '
-        '  fi; '
-        'fi; ')
-    if self.log_level == 'average':
-      self.logger.LogOutput('Disable Turbo.')
-    run_on_dut(dis_turbo_cmd)
+    # CPU usage setup comes first where we enable/disable cores.
+    run_on_dut.SetupCpuUsage()
+    cpu_online_status = run_on_dut.GetCpuOnline()
+    # List of online cores of type int (core number).
+    online_cores = [
+        core for core, status in cpu_online_status.items() if status
+    ]
+    if self.dut_config['cooldown_time']:
+      # Setup power conservative mode for effective cool down.
+      # Set ignore status since powersave may no be available
+      # on all platforms and we are going to handle it.
+      ret = run_on_dut.SetCpuGovernor('powersave', ignore_status=True)
+      if ret:
+        # "powersave" is not available, use "ondemand".
+        # Still not a fatal error if it fails.
+        ret = run_on_dut.SetCpuGovernor('ondemand', ignore_status=True)
+      # TODO(denik): Run comparison test for 'powersave' and 'ondemand'
+      # on scarlet and kevin64.
+      # We might have to consider reducing freq manually to the min
+      # if it helps to reduce waiting time.
+      wait_time = run_on_dut.WaitCooldown()
+      cros_machine.AddCooldownWaitTime(wait_time)
 
-  def WaitCooldown(self, run_on_dut):
-    waittime = 0
-    timeout_in_sec = int(self.dut_config['cooldown_time']) * 60
-    # Temperature from sensors come in uCelsius units.
-    temp_in_ucels = int(self.dut_config['cooldown_temp']) * 1000
-    sleep_interval = 30
+    # Setup CPU governor for the benchmark run.
+    # It overwrites the previous governor settings.
+    governor = self.dut_config['governor']
+    # FIXME(denik): Pass online cores to governor setup.
+    run_on_dut.SetCpuGovernor(governor, ignore_status=False)
 
-    # Wait until any of two events occurs:
-    # 1. CPU cools down to a specified temperature.
-    # 2. Timeout cooldown_time expires.
-    # For the case when targeted temperature is not reached within specified
-    # timeout the benchmark is going to start with higher initial CPU temp.
-    # In the worst case it may affect test results but at the same time we
-    # guarantee the upper bound of waiting time.
-    # TODO(denik): Report (or highlight) "high" CPU temperature in test results.
-    # "high" should be calculated based on empirical data per platform.
-    # Based on such reports we can adjust CPU configuration or
-    # cooldown limits accordingly.
-    while waittime < timeout_in_sec:
-      _, temp_output, _ = run_on_dut(
-          'cat /sys/class/thermal/thermal_zone*/temp', ignore_status=True)
-      if any(int(temp) > temp_in_ucels for temp in temp_output.split()):
-        time.sleep(sleep_interval)
-        waittime += sleep_interval
-      else:
-        # Exit the loop when:
-        # 1. Reported temp numbers from all thermal sensors do not exceed
-        # 'cooldown_temp' or
-        # 2. No data from the sensors.
-        break
+    # Disable Turbo and Setup CPU freq should ALWAYS proceed governor setup
+    # since governor may change:
+    # - frequency;
+    # - turbo/boost.
+    run_on_dut.DisableTurbo()
+    run_on_dut.SetupCpuFreq(online_cores)
+    # FIXME(denik): Currently we are not recovering the previous cpufreq
+    # settings since we do reboot/setup every time anyway.
+    # But it may change in the future and then we have to recover the
+    # settings.
 
-    self.logger.LogOutput('Cooldown wait time: %.1f min' % (waittime / 60))
-    return waittime
-
-  def SetupCpuUsage(self, run_on_dut):
-    """Setup CPU usage.
-
-    Based on self.dut_config['cpu_usage'] configure CPU cores
-    utilization.
-    """
-
-    if (self.dut_config['cpu_usage'] == 'big_only' or
-        self.dut_config['cpu_usage'] == 'little_only'):
-      _, arch, _ = run_on_dut('uname -m')
-
-      if arch.lower().startswith('arm') or arch.lower().startswith('aarch64'):
-        self.SetupArmCores(run_on_dut)
-
-  def SetupArmCores(self, run_on_dut):
-    """Setup ARM big/little cores."""
-
-    # CPU implemeters/part numbers of big/LITTLE CPU.
-    # Format: dict(CPU implementer: set(CPU part numbers))
-    LITTLE_CORES = {
-        '0x41': {
-            '0xd01',  # Cortex A32
-            '0xd03',  # Cortex A53
-            '0xd04',  # Cortex A35
-            '0xd05',  # Cortex A55
-        },
-    }
-    BIG_CORES = {
-        '0x41': {
-            '0xd07',  # Cortex A57
-            '0xd08',  # Cortex A72
-            '0xd09',  # Cortex A73
-            '0xd0a',  # Cortex A75
-            '0xd0b',  # Cortex A76
-        },
-    }
-
-    # Values of CPU Implementer and CPU part number are exposed by cpuinfo.
-    # Format:
-    # =================
-    # processor       : 0
-    # model name      : ARMv8 Processor rev 4 (v8l)
-    # BogoMIPS        : 48.00
-    # Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
-    # CPU implementer : 0x41
-    # CPU architecture: 8
-    # CPU variant     : 0x0
-    # CPU part        : 0xd03
-    # CPU revision    : 4
-
-    _, cpuinfo, _ = run_on_dut('cat /proc/cpuinfo')
-
-    # List of all CPU cores: 0, 1, ..
-    proc_matches = re.findall(r'^processor\s*: (\d+)$', cpuinfo, re.MULTILINE)
-    # List of all corresponding CPU implementers
-    impl_matches = re.findall(r'^CPU implementer\s*: (0x[\da-f]+)$', cpuinfo,
-                              re.MULTILINE)
-    # List of all corresponding CPU part numbers
-    part_matches = re.findall(r'^CPU part\s*: (0x[\da-f]+)$', cpuinfo,
-                              re.MULTILINE)
-    assert len(proc_matches) == len(impl_matches)
-    assert len(part_matches) == len(impl_matches)
-
-    all_cores = set(proc_matches)
-    dut_big_cores = {
-        core
-        for core, impl, part in zip(proc_matches, impl_matches, part_matches)
-        if impl in BIG_CORES and part in BIG_CORES[impl]
-    }
-    dut_lit_cores = {
-        core
-        for core, impl, part in zip(proc_matches, impl_matches, part_matches)
-        if impl in LITTLE_CORES and part in LITTLE_CORES[impl]
-    }
-
-    if self.dut_config['cpu_usage'] == 'big_only':
-      cores_to_enable = dut_big_cores
-      cores_to_disable = all_cores - dut_big_cores
-    elif self.dut_config['cpu_usage'] == 'little_only':
-      cores_to_enable = dut_lit_cores
-      cores_to_disable = all_cores - dut_lit_cores
-    else:
-      self.logger.LogError(
-          'cpu_usage=%s is not supported on ARM.\n'
-          'Ignore ARM CPU setup and continue.' % self.dut_config['cpu_usage'])
-      return
-
-    if cores_to_enable:
-      cmd_enable_cores = ('echo 1 | tee /sys/devices/system/cpu/cpu{%s}/online'
-                          % ','.join(sorted(cores_to_enable)))
-
-      cmd_disable_cores = ''
-      if cores_to_disable:
-        cmd_disable_cores = (
-            'echo 0 | tee /sys/devices/system/cpu/cpu{%s}/online' % ','.join(
-                sorted(cores_to_disable)))
-
-      run_on_dut('; '.join([cmd_enable_cores, cmd_disable_cores]))
-    else:
-      # If there are no cores enabled by dut_config then configuration
-      # is invalid for current platform and should be ignored.
-      self.logger.LogError(
-          '"cpu_usage" is invalid for targeted platform.\n'
-          'dut_config[cpu_usage]=%s\n'
-          'dut big cores: %s\n'
-          'dut little cores: %s\n'
-          'Ignore ARM CPU setup and continue.' % (self.dut_config['cpu_usage'],
-                                                  dut_big_cores, dut_lit_cores))
-
-  def GetCpuOnline(self, run_on_dut):
-    """Get online status of CPU cores.
-
-    Return dict of {int(cpu_num): <0|1>}.
-    """
-    get_cpu_online_cmd = ('paste -d" "'
-                          ' <(ls /sys/devices/system/cpu/cpu*/online)'
-                          ' <(cat /sys/devices/system/cpu/cpu*/online)')
-    _, online_output_str, _ = run_on_dut(get_cpu_online_cmd)
-
-    # Here is the output we expect to see:
-    # -----------------
-    # /sys/devices/system/cpu/cpu0/online 0
-    # /sys/devices/system/cpu/cpu1/online 1
-
-    cpu_online = {}
-    cpu_online_match = re.compile(r'^[/\S]+/cpu(\d+)/[/\S]+\s+(\d+)$')
-    for line in online_output_str.splitlines():
-      match = cpu_online_match.match(line)
-      if match:
-        cpu = int(match.group(1))
-        status = int(match.group(2))
-        cpu_online[cpu] = status
-    # At least one CPU has to be online.
-    assert cpu_online
-
-    return cpu_online
-
-  def SetupCpuFreq(self, run_on_dut, online_cores):
-    """Setup CPU frequency.
-
-    Based on self.dut_config['cpu_freq_pct'] setup frequency of online CPU cores
-    to a supported value which is less or equal to (freq_pct * max_freq / 100)
-    limited by min_freq.
-
-    NOTE: scaling_available_frequencies support is required.
-    Otherwise the function has no effect.
-    """
-    freq_percent = self.dut_config['cpu_freq_pct']
-    list_all_avail_freq_cmd = ('ls /sys/devices/system/cpu/cpu{%s}/cpufreq/'
-                               'scaling_available_frequencies')
-    # Ignore error to support general usage of frequency setup.
-    # Not all platforms support scaling_available_frequencies.
-    ret, all_avail_freq_str, _ = run_on_dut(
-        list_all_avail_freq_cmd % ','.join(str(core) for core in online_cores),
-        ignore_status=True)
-    if ret or not all_avail_freq_str:
-      # No scalable frequencies available for the core.
-      return ret
-    for avail_freq_path in all_avail_freq_str.split():
-      # Get available freq from every scaling_available_frequency path.
-      # Error is considered fatal in run_on_dut().
-      _, avail_freq_str, _ = run_on_dut('cat ' + avail_freq_path)
-      assert avail_freq_str
-
-      all_avail_freq = sorted(
-          int(freq_str) for freq_str in avail_freq_str.split())
-      min_freq = all_avail_freq[0]
-      max_freq = all_avail_freq[-1]
-      # Calculate the frequency we are targeting.
-      target_freq = round(max_freq * freq_percent / 100)
-      # More likely it's not in the list of supported frequencies
-      # and our goal is to find the one which is less or equal.
-      # Default is min and we will try to maximize it.
-      avail_ngt_target = min_freq
-      # Find the largest not greater than the target.
-      for next_largest in reversed(all_avail_freq):
-        if next_largest <= target_freq:
-          avail_ngt_target = next_largest
-          break
-
-      max_freq_path = avail_freq_path.replace('scaling_available_frequencies',
-                                              'scaling_max_freq')
-      min_freq_path = avail_freq_path.replace('scaling_available_frequencies',
-                                              'scaling_min_freq')
-      # With default ignore_status=False we expect 0 status or Fatal error.
-      run_on_dut('echo %s | tee %s %s' % (avail_ngt_target, max_freq_path,
-                                          min_freq_path))
-
-  def DecreaseWaitTime(self, run_on_dut):
-    """Change the ten seconds wait time for pagecycler to two seconds."""
-    FILE = '/usr/local/telemetry/src/tools/perf/page_sets/page_cycler_story.py'
-    ret = run_on_dut('ls ' + FILE)
-
-    if not ret:
-      sed_command = 'sed -i "s/_TTI_WAIT_TIME = 10/_TTI_WAIT_TIME = 2/g" '
-      run_on_dut(sed_command + FILE)
-
-  def StopUI(self, run_on_dut):
-    run_on_dut('stop ui')
-
-  def StartUI(self, run_on_dut):
-    run_on_dut('start ui')
+    # DUT setup is done. Start a fresh new shiny UI.
+    run_on_dut.StartUI()
 
   def Test_That_Run(self, machine, label, benchmark, test_args, profiler_args):
     """Run the test_that test.."""
diff --git a/crosperf/suite_runner_unittest.py b/crosperf/suite_runner_unittest.py
index bf8bc31..edde46a 100755
--- a/crosperf/suite_runner_unittest.py
+++ b/crosperf/suite_runner_unittest.py
@@ -22,82 +22,9 @@
 
 from cros_utils import command_executer
 from cros_utils import logger
+from cros_utils.device_setup_utils import DutWrapper
 from machine_manager import MockCrosMachine
 
-BIG_LITTLE_CPUINFO = """processor       : 0
-model name      : ARMv8 Processor rev 4 (v8l)
-BogoMIPS        : 48.00
-Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
-CPU implementer : 0x41
-CPU architecture: 8
-CPU variant     : 0x0
-CPU part        : 0xd03
-CPU revision    : 4
-
-processor       : 1
-model name      : ARMv8 Processor rev 4 (v8l)
-BogoMIPS        : 48.00
-Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
-CPU implementer : 0x41
-CPU architecture: 8
-CPU variant     : 0x0
-CPU part        : 0xd03
-CPU revision    : 4
-
-processor       : 2
-model name      : ARMv8 Processor rev 2 (v8l)
-BogoMIPS        : 48.00
-Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
-CPU implementer : 0x41
-CPU architecture: 8
-CPU variant     : 0x0
-CPU part        : 0xd08
-CPU revision    : 2
-"""
-LITTLE_ONLY_CPUINFO = """processor       : 0
-model name      : ARMv8 Processor rev 4 (v8l)
-BogoMIPS        : 48.00
-Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
-CPU implementer : 0x41
-CPU architecture: 8
-CPU variant     : 0x0
-CPU part        : 0xd03
-CPU revision    : 4
-
-processor       : 1
-model name      : ARMv8 Processor rev 4 (v8l)
-BogoMIPS        : 48.00
-Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
-CPU implementer : 0x41
-CPU architecture: 8
-CPU variant     : 0x0
-CPU part        : 0xd03
-CPU revision    : 4
-"""
-
-NOT_BIG_LITTLE_CPUINFO = """processor       : 0
-model name      : ARMv7 Processor rev 1 (v7l)
-Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4
-CPU implementer : 0x41
-CPU architecture: 7
-CPU variant     : 0x0
-CPU part        : 0xc0d
-CPU revision    : 1
-
-processor       : 1
-model name      : ARMv7 Processor rev 1 (v7l)
-Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4
-CPU implementer : 0x41
-CPU architecture: 7
-CPU variant     : 0x0
-CPU part        : 0xc0d
-CPU revision    : 1
-
-Hardware        : Rockchip (Device Tree)
-Revision        : 0000
-Serial          : 0000000000000000
-"""
-
 
 class SuiteRunnerTest(unittest.TestCase):
   """Class of SuiteRunner test."""
@@ -147,7 +74,6 @@
     self.telemetry_crosperf_args = []
     self.call_skylab_run = False
     self.call_telemetry_crosperf_run = False
-    self.call_disable_aslr = False
 
   def setUp(self):
     self.runner = suite_runner.SuiteRunner(
@@ -167,17 +93,11 @@
       self.call_test_that_run = False
       self.call_skylab_run = False
       self.call_telemetry_crosperf_run = False
-      self.call_disable_aslr = False
       self.skylab_run_args = []
       self.test_that_args = []
       self.telemetry_run_args = []
       self.telemetry_crosperf_args = []
 
-    def FakeDisableASLR(runner):
-      # pylint fix for unused variable.
-      del runner
-      self.call_disable_aslr = True
-
     def FakeSkylabRun(test_label, benchmark, test_args, profiler_args):
       self.skylab_run_args = [test_label, benchmark, test_args, profiler_args]
       self.call_skylab_run = True
@@ -204,17 +124,12 @@
       del command, ignore_status
       return 0, '', ''
 
-    self.runner.DisableASLR = FakeDisableASLR
     self.runner.Skylab_Run = FakeSkylabRun
     self.runner.Telemetry_Crosperf_Run = FakeTelemetryCrosperfRun
     self.runner.Test_That_Run = FakeTestThatRun
-    self.runner.SetupCpuUsage = mock.Mock()
-    self.runner.SetupCpuFreq = mock.Mock()
-    self.runner.DutWrapper = mock.Mock(return_value=FakeRunner)
-    self.runner.DisableTurbo = mock.Mock()
-    self.runner.SetCpuGovernor = mock.Mock()
-    self.runner.WaitCooldown = mock.Mock(return_value=0)
-    self.runner.GetCpuOnline = mock.Mock(return_value={0: 1, 1: 1, 2: 0})
+    self.runner.SetupDevice = mock.Mock()
+    DutWrapper.RunCommandOnDut = mock.Mock(return_value=FakeRunner)
+
     self.runner.dut_config['cooldown_time'] = 0
     self.runner.dut_config['governor'] = 'fake_governor'
     self.runner.dut_config['cpu_freq_pct'] = 65
@@ -228,68 +143,86 @@
     self.mock_label.skylab = True
     self.runner.Run(cros_machine, self.mock_label, self.telemetry_bench,
                     test_args, profiler_args)
-    self.assertFalse(self.call_disable_aslr)
     self.assertTrue(self.call_skylab_run)
     self.assertFalse(self.call_test_that_run)
     self.assertFalse(self.call_telemetry_crosperf_run)
     self.assertEqual(self.skylab_run_args,
                      [self.mock_label, self.telemetry_bench, '', ''])
-    self.runner.SetupCpuUsage.assert_not_called()
-    self.runner.SetupCpuFreq.assert_not_called()
-    self.runner.GetCpuOnline.assert_not_called()
-    self.runner.DutWrapper.assert_not_called()
-    self.runner.SetCpuGovernor.assert_not_called()
-    self.runner.DisableTurbo.assert_not_called()
-    self.runner.WaitCooldown.assert_not_called()
+    self.runner.SetupDevice.assert_not_called()
     self.mock_label.skylab = False
 
     reset()
     self.runner.Run(cros_machine, self.mock_label, self.test_that_bench,
                     test_args, profiler_args)
-    self.assertTrue(self.call_disable_aslr)
     self.assertTrue(self.call_test_that_run)
     self.assertFalse(self.call_telemetry_crosperf_run)
     self.assertEqual(
         self.test_that_args,
         ['fake_machine', self.mock_label, self.test_that_bench, '', ''])
-    self.runner.SetupCpuUsage.assert_called_once_with(FakeRunner)
-    self.runner.SetupCpuFreq.assert_called_once_with(FakeRunner, [0, 1])
-    self.runner.GetCpuOnline.assert_called_once_with(FakeRunner)
-    self.runner.DutWrapper.assert_called_once_with(
-        machine, self.mock_label.chromeos_root)
-    self.runner.SetCpuGovernor.assert_called_once_with(
-        'fake_governor', FakeRunner, ignore_status=False)
-    self.runner.DisableTurbo.assert_called_once_with(FakeRunner)
-    self.runner.WaitCooldown.assert_not_called()
 
     reset()
     self.runner.Run(cros_machine, self.mock_label,
                     self.telemetry_crosperf_bench, test_args, profiler_args)
-    self.assertTrue(self.call_disable_aslr)
     self.assertFalse(self.call_test_that_run)
     self.assertTrue(self.call_telemetry_crosperf_run)
     self.assertEqual(self.telemetry_crosperf_args, [
         'fake_machine', self.mock_label, self.telemetry_crosperf_bench, '', ''
     ])
-    self.runner.DutWrapper.assert_called_with(machine,
-                                              self.mock_label.chromeos_root)
 
-  def test_run_with_cooldown(self):
+  def test_setup_device(self):
 
     def FakeRunner(command, ignore_status=False):
       # pylint fix for unused variable.
       del command, ignore_status
       return 0, '', ''
 
-    self.runner.DisableASLR = mock.Mock()
-    self.runner.DutWrapper = mock.Mock(return_value=FakeRunner)
-    self.runner.DisableTurbo = mock.Mock()
-    self.runner.SetCpuGovernor = mock.Mock()
-    self.runner.SetupCpuUsage = mock.Mock()
-    self.runner.SetupCpuFreq = mock.Mock()
-    self.runner.WaitCooldown = mock.Mock(return_value=0)
-    self.runner.GetCpuOnline = mock.Mock(return_value={0: 0, 1: 1})
-    self.runner.Telemetry_Crosperf_Run = mock.Mock(return_value=(0, '', ''))
+    DutWrapper.RunCommandOnDut = mock.Mock(return_value=FakeRunner)
+    DutWrapper.DisableASLR = mock.Mock()
+    DutWrapper.SetupCpuUsage = mock.Mock()
+    DutWrapper.SetupCpuFreq = mock.Mock()
+    DutWrapper.DisableTurbo = mock.Mock()
+    DutWrapper.SetCpuGovernor = mock.Mock()
+    DutWrapper.WaitCooldown = mock.Mock(return_value=0)
+    DutWrapper.GetCpuOnline = mock.Mock(return_value={0: 1, 1: 1, 2: 0})
+    self.runner.dut_config['cooldown_time'] = 0
+    self.runner.dut_config['governor'] = 'fake_governor'
+    self.runner.dut_config['cpu_freq_pct'] = 65
+
+    machine = 'fake_machine'
+    cros_machine = MockCrosMachine(machine, self.mock_label.chromeos_root,
+                                   self.mock_logger)
+    mock_run_on_dut = DutWrapper(
+        self.mock_label.chromeos_root,
+        machine,
+        logger=self.mock_logger,
+        dut_config=self.runner.dut_config)
+
+    self.runner.SetupDevice(mock_run_on_dut, cros_machine)
+
+    DutWrapper.SetupCpuUsage.assert_called_once_with()
+    DutWrapper.SetupCpuFreq.assert_called_once_with([0, 1])
+    DutWrapper.GetCpuOnline.assert_called_once_with()
+    DutWrapper.SetCpuGovernor.assert_called_once_with(
+        'fake_governor', ignore_status=False)
+    DutWrapper.DisableTurbo.assert_called_once_with()
+    DutWrapper.WaitCooldown.assert_not_called()
+
+  def test_setup_device_with_cooldown(self):
+
+    def FakeRunner(command, ignore_status=False):
+      # pylint fix for unused variable.
+      del command, ignore_status
+      return 0, '', ''
+
+    DutWrapper.RunCommandOnDut = mock.Mock(return_value=FakeRunner)
+    DutWrapper.DisableASLR = mock.Mock()
+    DutWrapper.DisableTurbo = mock.Mock()
+    DutWrapper.SetCpuGovernor = mock.Mock()
+    DutWrapper.SetupCpuUsage = mock.Mock()
+    DutWrapper.SetupCpuFreq = mock.Mock()
+    DutWrapper.WaitCooldown = mock.Mock(return_value=0)
+    DutWrapper.GetCpuOnline = mock.Mock(return_value={0: 0, 1: 1})
+
     self.runner.dut_config['cooldown_time'] = 10
     self.runner.dut_config['governor'] = 'fake_governor'
     self.runner.dut_config['cpu_freq_pct'] = 75
@@ -297,521 +230,24 @@
     machine = 'fake_machine'
     cros_machine = MockCrosMachine(machine, self.mock_label.chromeos_root,
                                    self.mock_logger)
-    self.runner.Run(cros_machine, self.mock_label,
-                    self.telemetry_crosperf_bench, '', '')
+    mock_run_on_dut = DutWrapper(
+        self.mock_label.chromeos_root,
+        machine,
+        logger=self.mock_logger,
+        dut_config=self.runner.dut_config)
 
-    self.runner.WaitCooldown.assert_called_once_with(FakeRunner)
-    self.runner.DisableASLR.assert_called_once()
-    self.runner.Telemetry_Crosperf_Run.assert_called_once()
-    self.runner.DisableTurbo.assert_called_once_with(FakeRunner)
-    self.runner.SetupCpuUsage.assert_called_once_with(FakeRunner)
-    self.runner.SetupCpuFreq.assert_called_once_with(FakeRunner, [1])
-    self.runner.SetCpuGovernor.assert_called()
-    self.runner.GetCpuOnline.assert_called_once_with(FakeRunner)
-    self.assertGreater(self.runner.SetCpuGovernor.call_count, 1)
-    self.assertEqual(
-        self.runner.SetCpuGovernor.call_args,
-        mock.call('fake_governor', FakeRunner, ignore_status=False))
+    self.runner.SetupDevice(mock_run_on_dut, cros_machine)
 
-  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommandWOutput')
-  def test_dut_wrapper(self, mock_cros_runcmd):
-    self.mock_cmd_exec.CrosRunCommandWOutput = mock_cros_runcmd
-    mock_cros_runcmd.return_value = (0, '', '')
-    run_on_dut = self.runner.DutWrapper('lumpy.cros2', '/tmp/chromeos')
-    mock_cros_runcmd.assert_not_called()
-    run_on_dut('run command;')
-    mock_cros_runcmd.assert_called_once_with(
-        'run command;', chromeos_root='/tmp/chromeos', machine='lumpy.cros2')
-
-  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommandWOutput')
-  def test_dut_wrapper_fatal_error(self, mock_cros_runcmd):
-    self.mock_cmd_exec.CrosRunCommandWOutput = mock_cros_runcmd
-    # Command returns error 1.
-    mock_cros_runcmd.return_value = (1, '', 'Error!')
-    run_on_dut = self.runner.DutWrapper('lumpy.cros2', '/tmp/chromeos')
-    mock_cros_runcmd.assert_not_called()
-    run_on_dut('run command;')
-    mock_cros_runcmd.assert_called_once_with(
-        'run command;', chromeos_root='/tmp/chromeos', machine='lumpy.cros2')
-    # Error status causes log fatal.
-    self.assertEqual(
-        self.mock_logger.method_calls[-1],
-        mock.call.LogFatal('Command execution on DUT lumpy.cros2 failed.\n'
-                           'Failing command: run command;\nreturned 1\n'
-                           'Error message: Error!'))
-
-  @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommandWOutput')
-  def test_dut_wrapper_ignore_error(self, mock_cros_runcmd):
-    self.mock_cmd_exec.CrosRunCommandWOutput = mock_cros_runcmd
-    # Command returns error 1.
-    mock_cros_runcmd.return_value = (1, '', 'Error!')
-    run_on_dut = self.runner.DutWrapper('lumpy.cros2', '/tmp/chromeos')
-    run_on_dut('run command;', ignore_status=True)
-    mock_cros_runcmd.assert_called_once_with(
-        'run command;', chromeos_root='/tmp/chromeos', machine='lumpy.cros2')
-    # Error status is not fatal. LogError records the error message.
-    self.assertEqual(
-        self.mock_logger.method_calls[-1],
-        mock.call.LogError('Command execution on DUT lumpy.cros2 failed.\n'
-                           'Failing command: run command;\nreturned 1\n'
-                           'Error message: Error!\n'
-                           '(Failure is considered non-fatal. Continue.)'))
-
-  def test_disable_aslr(self):
-    run_on_dut = mock.Mock()
-    self.runner.DisableASLR(run_on_dut)
-    # pyformat: disable
-    set_cpu_cmd = ('set -e && '
-                   'if [[ -e /proc/sys/kernel/randomize_va_space ]]; then '
-                   '  echo 0 > /proc/sys/kernel/randomize_va_space; '
-                   'fi')
-    run_on_dut.assert_called_once_with(set_cpu_cmd)
-
-  def test_set_cpu_governor(self):
-    dut_runner = mock.Mock(return_value=(0, '', ''))
-    self.runner.SetCpuGovernor('new_governor', dut_runner, ignore_status=False)
-    set_cpu_cmd = (
-        'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do '
-        # Skip writing scaling_governor if cpu is not online.
-        ' [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
-        '   && continue; '
-        ' cd $f; '
-        ' if [[ -e scaling_governor ]]; then '
-        '  echo %s > scaling_governor; fi; '
-        'done; ')
-    dut_runner.assert_called_once_with(
-        set_cpu_cmd % 'new_governor', ignore_status=False)
-
-  def test_set_cpu_governor_propagate_error(self):
-    dut_runner = mock.Mock(return_value=(1, '', 'Error.'))
-    self.runner.SetCpuGovernor('non-exist_governor', dut_runner)
-    set_cpu_cmd = (
-        'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do '
-        # Skip writing scaling_governor if cpu is not online.
-        ' [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
-        '   && continue; '
-        ' cd $f; '
-        ' if [[ -e scaling_governor ]]; then '
-        '  echo %s > scaling_governor; fi; '
-        'done; ')
-    # By default error status is fatal.
-    dut_runner.assert_called_once_with(
-        set_cpu_cmd % 'non-exist_governor', ignore_status=False)
-
-  def test_set_cpu_governor_ignore_status(self):
-    dut_runner = mock.Mock(return_value=(1, '', 'Error.'))
-    ret_code = self.runner.SetCpuGovernor(
-        'non-exist_governor', dut_runner, ignore_status=True)
-    set_cpu_cmd = (
-        'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do '
-        # Skip writing scaling_governor if cpu is not online.
-        ' [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
-        '   && continue; '
-        ' cd $f; '
-        ' if [[ -e scaling_governor ]]; then '
-        '  echo %s > scaling_governor; fi; '
-        'done; ')
-    dut_runner.assert_called_once_with(
-        set_cpu_cmd % 'non-exist_governor', ignore_status=True)
-    self.assertEqual(ret_code, 1)
-
-  def test_disable_turbo(self):
-    dut_runner = mock.Mock(return_value=(0, '', ''))
-    self.runner.DisableTurbo(dut_runner)
-    set_cpu_cmd = (
-        # Disable Turbo in Intel pstate driver
-        'if [[ -e /sys/devices/system/cpu/intel_pstate/no_turbo ]]; then '
-        '  if grep -q 0 /sys/devices/system/cpu/intel_pstate/no_turbo;  then '
-        '    echo -n 1 > /sys/devices/system/cpu/intel_pstate/no_turbo; '
-        '  fi; '
-        'fi; ')
-    dut_runner.assert_called_once_with(set_cpu_cmd)
-
-  def test_get_cpu_online_two(self):
-    """Test one digit CPU #."""
-    dut_runner = mock.Mock(
-        return_value=(0, '/sys/devices/system/cpu/cpu0/online 0\n'
-                      '/sys/devices/system/cpu/cpu1/online 1\n', ''))
-    cpu_online = self.runner.GetCpuOnline(dut_runner)
-    self.assertEqual(cpu_online, {0: 0, 1: 1})
-
-  def test_get_cpu_online_twelve(self):
-    """Test two digit CPU #."""
-    dut_runner = mock.Mock(
-        return_value=(0, '/sys/devices/system/cpu/cpu0/online 1\n'
-                      '/sys/devices/system/cpu/cpu1/online 0\n'
-                      '/sys/devices/system/cpu/cpu10/online 1\n'
-                      '/sys/devices/system/cpu/cpu11/online 1\n'
-                      '/sys/devices/system/cpu/cpu2/online 1\n'
-                      '/sys/devices/system/cpu/cpu3/online 0\n'
-                      '/sys/devices/system/cpu/cpu4/online 1\n'
-                      '/sys/devices/system/cpu/cpu5/online 0\n'
-                      '/sys/devices/system/cpu/cpu6/online 1\n'
-                      '/sys/devices/system/cpu/cpu7/online 0\n'
-                      '/sys/devices/system/cpu/cpu8/online 1\n'
-                      '/sys/devices/system/cpu/cpu9/online 0\n', ''))
-    cpu_online = self.runner.GetCpuOnline(dut_runner)
-    self.assertEqual(cpu_online, {
-        0: 1,
-        1: 0,
-        2: 1,
-        3: 0,
-        4: 1,
-        5: 0,
-        6: 1,
-        7: 0,
-        8: 1,
-        9: 0,
-        10: 1,
-        11: 1
-    })
-
-  def test_get_cpu_online_no_output(self):
-    """Test error case, no output."""
-    dut_runner = mock.Mock(return_value=(0, '', ''))
-    with self.assertRaises(AssertionError):
-      self.runner.GetCpuOnline(dut_runner)
-
-  def test_get_cpu_online_command_error(self):
-    """Test error case, command error."""
-    dut_runner = mock.Mock(side_effect=AssertionError)
-    with self.assertRaises(AssertionError):
-      self.runner.GetCpuOnline(dut_runner)
-
-  @mock.patch.object(suite_runner.SuiteRunner, 'SetupArmCores')
-  def test_setup_cpu_usage_little_on_arm(self, mock_setup_arm):
-    self.runner.SetupArmCores = mock_setup_arm
-    dut_runner = mock.Mock(return_value=(0, 'armv7l', ''))
-    self.runner.dut_config['cpu_usage'] = 'little_only'
-    self.runner.SetupCpuUsage(dut_runner)
-    self.runner.SetupArmCores.assert_called_once_with(dut_runner)
-
-  @mock.patch.object(suite_runner.SuiteRunner, 'SetupArmCores')
-  def test_setup_cpu_usage_big_on_aarch64(self, mock_setup_arm):
-    self.runner.SetupArmCores = mock_setup_arm
-    dut_runner = mock.Mock(return_value=(0, 'aarch64', ''))
-    self.runner.dut_config['cpu_usage'] = 'big_only'
-    self.runner.SetupCpuUsage(dut_runner)
-    self.runner.SetupArmCores.assert_called_once_with(dut_runner)
-
-  @mock.patch.object(suite_runner.SuiteRunner, 'SetupArmCores')
-  def test_setup_cpu_usage_big_on_intel(self, mock_setup_arm):
-    self.runner.SetupArmCores = mock_setup_arm
-    dut_runner = mock.Mock(return_value=(0, 'x86_64', ''))
-    self.runner.dut_config['cpu_usage'] = 'big_only'
-    self.runner.SetupCpuUsage(dut_runner)
-    # Check that SetupArmCores not called with invalid setup.
-    self.runner.SetupArmCores.assert_not_called()
-
-  @mock.patch.object(suite_runner.SuiteRunner, 'SetupArmCores')
-  def test_setup_cpu_usage_all_on_intel(self, mock_setup_arm):
-    self.runner.SetupArmCores = mock_setup_arm
-    dut_runner = mock.Mock(return_value=(0, 'x86_64', ''))
-    self.runner.dut_config['cpu_usage'] = 'all'
-    self.runner.SetupCpuUsage(dut_runner)
-    # Check that SetupArmCores not called in general case.
-    self.runner.SetupArmCores.assert_not_called()
-
-  def test_setup_arm_cores_big_on_big_little(self):
-    dut_runner = mock.Mock(side_effect=[
-        (0, BIG_LITTLE_CPUINFO, ''),
-        (0, '', ''),
-    ])
-    self.runner.dut_config['cpu_usage'] = 'big_only'
-    self.runner.SetupArmCores(dut_runner)
-    dut_runner.assert_called_with(
-        'echo 1 | tee /sys/devices/system/cpu/cpu{2}/online; '
-        'echo 0 | tee /sys/devices/system/cpu/cpu{0,1}/online')
-
-  def test_setup_arm_cores_little_on_big_little(self):
-    dut_runner = mock.Mock(side_effect=[
-        (0, BIG_LITTLE_CPUINFO, ''),
-        (0, '', ''),
-    ])
-    self.runner.dut_config['cpu_usage'] = 'little_only'
-    self.runner.SetupArmCores(dut_runner)
-    dut_runner.assert_called_with(
-        'echo 1 | tee /sys/devices/system/cpu/cpu{0,1}/online; '
-        'echo 0 | tee /sys/devices/system/cpu/cpu{2}/online')
-
-  def test_setup_arm_cores_invalid_config(self):
-    dut_runner = mock.Mock(side_effect=[
-        (0, LITTLE_ONLY_CPUINFO, ''),
-        (0, '', ''),
-    ])
-    self.runner.dut_config['cpu_usage'] = 'big_only'
-    self.runner.SetupArmCores(dut_runner)
-    # Check that setup command is not sent when trying
-    # to use 'big_only' on a platform with all little cores.
-    dut_runner.assert_called_once_with('cat /proc/cpuinfo')
-
-  def test_setup_arm_cores_not_big_little(self):
-    dut_runner = mock.Mock(side_effect=[
-        (0, NOT_BIG_LITTLE_CPUINFO, ''),
-        (0, '', ''),
-    ])
-    self.runner.dut_config['cpu_usage'] = 'big_only'
-    self.runner.SetupArmCores(dut_runner)
-    # Check that setup command is not sent when trying
-    # to use 'big_only' on a platform w/o support of big/little.
-    dut_runner.assert_called_once_with('cat /proc/cpuinfo')
-
-  def test_setup_arm_cores_unsupported_cpu_usage(self):
-    dut_runner = mock.Mock(side_effect=[
-        (0, BIG_LITTLE_CPUINFO, ''),
-        (0, '', ''),
-    ])
-    self.runner.dut_config['cpu_usage'] = 'exclusive_cores'
-    self.runner.SetupArmCores(dut_runner)
-    # Check that setup command is not sent when trying to use
-    # 'exclusive_cores' on ARM CPU setup.
-    dut_runner.assert_called_once_with('cat /proc/cpuinfo')
-
-  def test_setup_cpu_freq_single_full(self):
-    online = [0]
-    dut_runner = mock.Mock(side_effect=[
-        (0,
-         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n',
-         ''),
-        (0, '1 2 3 4 5 6 7 8 9 10', ''),
-        (0, '', ''),
-    ])
-    self.runner.dut_config['cpu_freq_pct'] = 100
-    self.runner.SetupCpuFreq(dut_runner, online)
-    self.assertGreaterEqual(dut_runner.call_count, 3)
-    self.assertEqual(
-        dut_runner.call_args,
-        mock.call('echo 10 | tee '
-                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
-                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'))
-
-  def test_setup_cpu_freq_middle(self):
-    online = [0]
-    dut_runner = mock.Mock(side_effect=[
-        (0,
-         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n',
-         ''),
-        (0, '1 2 3 4 5 6 7 8 9 10', ''),
-        (0, '', ''),
-    ])
-    self.runner.dut_config['cpu_freq_pct'] = 60
-    self.runner.SetupCpuFreq(dut_runner, online)
-    self.assertGreaterEqual(dut_runner.call_count, 2)
-    self.assertEqual(
-        dut_runner.call_args,
-        mock.call('echo 6 | tee '
-                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
-                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'))
-
-  def test_setup_cpu_freq_lowest(self):
-    online = [0]
-    dut_runner = mock.Mock(side_effect=[
-        (0,
-         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n',
-         ''),
-        (0, '1 2 3 4 5 6 7 8 9 10', ''),
-        (0, '', ''),
-    ])
-    self.runner.dut_config['cpu_freq_pct'] = 0
-    self.runner.SetupCpuFreq(dut_runner, online)
-    self.assertGreaterEqual(dut_runner.call_count, 2)
-    self.assertEqual(
-        dut_runner.call_args,
-        mock.call('echo 1 | tee '
-                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
-                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'))
-
-  def test_setup_cpu_freq_multiple_middle(self):
-    online = [0, 1]
-    dut_runner = mock.Mock(side_effect=[
-        (0,
-         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n'
-         '/sys/devices/system/cpu/cpu1/cpufreq/scaling_available_frequencies\n',
-         ''),
-        (0, '1 2 3 4 5 6 7 8 9 10', ''),
-        (0, '', ''),
-        (0, '1 4 6 8 10 12 14 16 18 20', ''),
-        (0, '', ''),
-    ])
-    self.runner.dut_config['cpu_freq_pct'] = 70
-    self.runner.SetupCpuFreq(dut_runner, online)
-    self.assertEqual(dut_runner.call_count, 5)
-    self.assertEqual(
-        dut_runner.call_args_list[2],
-        mock.call('echo 7 | tee '
-                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq '
-                  '/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq'))
-    self.assertEqual(
-        dut_runner.call_args_list[4],
-        mock.call('echo 14 | tee '
-                  '/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq '
-                  '/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq'))
-
-  def test_setup_cpu_freq_no_scaling_available(self):
-    online = [0, 1]
-    dut_runner = mock.Mock(return_value=(2, '', 'No such file or directory'))
-    self.runner.dut_config['cpu_freq_pct'] = 50
-    self.runner.SetupCpuFreq(dut_runner, online)
-    dut_runner.assert_called_once()
-    self.assertNotRegexpMatches(dut_runner.call_args_list[0][0][0],
-                                '^echo.*scaling_max_freq$')
-
-  def test_setup_cpu_freq_multiple_no_access(self):
-    online = [0, 1]
-    dut_runner = mock.Mock(side_effect=[
-        (0,
-         '/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies\n'
-         '/sys/devices/system/cpu/cpu1/cpufreq/scaling_available_frequencies\n',
-         ''),
-        (0, '1 4 6 8 10 12 14 16 18 20', ''),
-        AssertionError(),
-    ])
-    self.runner.dut_config['cpu_freq_pct'] = 30
-    # Error status causes log fatal.
-    with self.assertRaises(AssertionError):
-      self.runner.SetupCpuFreq(dut_runner, online)
-
-  @mock.patch.object(time, 'sleep')
-  def test_wait_cooldown_nowait(self, mock_sleep):
-    mock_sleep.return_value = 0
-    dut_runner = mock.Mock(return_value=(0, '39000', ''))
-    self.runner.dut_config['cooldown_time'] = 10
-    self.runner.dut_config['cooldown_temp'] = 40
-    wait_time = self.runner.WaitCooldown(dut_runner)
-    # Send command to DUT only once to check temperature
-    # and make sure it does not exceed the threshold.
-    dut_runner.assert_called_once()
-    mock_sleep.assert_not_called()
-    self.assertEqual(wait_time, 0)
-
-  @mock.patch.object(time, 'sleep')
-  def test_wait_cooldown_needwait_once(self, mock_sleep):
-    """Wait one iteration for cooldown.
-
-    Set large enough timeout and changing temperature
-    output. Make sure it exits when expected value
-    received.
-    Expect that WaitCooldown check temp twice.
-    """
-    mock_sleep.return_value = 0
-    dut_runner = mock.Mock(side_effect=[(0, '41000', ''), (0, '39999', '')])
-    self.runner.dut_config['cooldown_time'] = 100
-    self.runner.dut_config['cooldown_temp'] = 40
-    wait_time = self.runner.WaitCooldown(dut_runner)
-    dut_runner.assert_called()
-    self.assertEqual(dut_runner.call_count, 2)
-    mock_sleep.assert_called()
-    self.assertGreater(wait_time, 0)
-
-  @mock.patch.object(time, 'sleep')
-  def test_wait_cooldown_needwait(self, mock_sleep):
-    """Test exit by timeout.
-
-    Send command to DUT checking the temperature and
-    check repeatedly until timeout goes off.
-    Output from temperature sensor never changes.
-    """
-    mock_sleep.return_value = 0
-    dut_runner = mock.Mock(return_value=(0, '41000', ''))
-    self.runner.dut_config['cooldown_time'] = 60
-    self.runner.dut_config['cooldown_temp'] = 40
-    wait_time = self.runner.WaitCooldown(dut_runner)
-    dut_runner.assert_called()
-    self.assertGreater(dut_runner.call_count, 2)
-    mock_sleep.assert_called()
-    self.assertGreater(wait_time, 0)
-
-  @mock.patch.object(time, 'sleep')
-  def test_wait_cooldown_needwait_multtemp(self, mock_sleep):
-    """Wait until all temps go down.
-
-    Set large enough timeout and changing temperature
-    output. Make sure it exits when expected value
-    for all temperatures received.
-    Expect 3 checks.
-    """
-    mock_sleep.return_value = 0
-    dut_runner = mock.Mock(side_effect=[
-        (0, '41000\n20000\n30000\n45000', ''),
-        (0, '39000\n20000\n30000\n41000', ''),
-        (0, '39000\n20000\n30000\n31000', ''),
-    ])
-    self.runner.dut_config['cooldown_time'] = 100
-    self.runner.dut_config['cooldown_temp'] = 40
-    wait_time = self.runner.WaitCooldown(dut_runner)
-    dut_runner.assert_called()
-    self.assertEqual(dut_runner.call_count, 3)
-    mock_sleep.assert_called()
-    self.assertGreater(wait_time, 0)
-
-  @mock.patch.object(time, 'sleep')
-  def test_wait_cooldown_thermal_error(self, mock_sleep):
-    """Handle error status.
-
-    Any error should be considered non-fatal.
-    """
-    mock_sleep.return_value = 0
-    dut_runner = mock.Mock(side_effect=[
-        (1, '39000\n20000\n30000\n41000', 'Thermal error'),
-        (1, '39000\n20000\n30000\n31000', 'Thermal error'),
-    ])
-    self.runner.dut_config['cooldown_time'] = 10
-    self.runner.dut_config['cooldown_temp'] = 40
-    wait_time = self.runner.WaitCooldown(dut_runner)
-    # Check that errors are ignored.
-    dut_runner.assert_called_with(
-        'cat /sys/class/thermal/thermal_zone*/temp', ignore_status=True)
-    self.assertEqual(dut_runner.call_count, 2)
-    # Check that we are waiting even when an error is returned
-    # as soon as data is coming.
-    mock_sleep.assert_called()
-    self.assertGreater(wait_time, 0)
-
-  @mock.patch.object(time, 'sleep')
-  def test_wait_cooldown_thermal_no_output(self, mock_sleep):
-    """Handle no output.
-
-    Check handling of empty stdout.
-    """
-    mock_sleep.return_value = 0
-    dut_runner = mock.Mock(side_effect=[(1, '', 'Thermal error')])
-    self.runner.dut_config['cooldown_time'] = 10
-    self.runner.dut_config['cooldown_temp'] = 40
-    wait_time = self.runner.WaitCooldown(dut_runner)
-    # Check that errors are ignored.
-    dut_runner.assert_called_once_with(
-        'cat /sys/class/thermal/thermal_zone*/temp', ignore_status=True)
-    # No wait.
-    mock_sleep.assert_not_called()
-    self.assertEqual(wait_time, 0)
-
-  @mock.patch.object(time, 'sleep')
-  def test_wait_cooldown_thermal_ws_output(self, mock_sleep):
-    """Handle whitespace output.
-
-    Check handling of whitespace only.
-    """
-    mock_sleep.return_value = 0
-    dut_runner = mock.Mock(side_effect=[(1, '\n', 'Thermal error')])
-    self.runner.dut_config['cooldown_time'] = 10
-    self.runner.dut_config['cooldown_temp'] = 40
-    wait_time = self.runner.WaitCooldown(dut_runner)
-    # Check that errors are ignored.
-    dut_runner.assert_called_once_with(
-        'cat /sys/class/thermal/thermal_zone*/temp', ignore_status=True)
-    # No wait.
-    mock_sleep.assert_not_called()
-    self.assertEqual(wait_time, 0)
-
-  def test_stop_ui(self):
-    dut_runner = mock.Mock(return_value=(0, '', ''))
-    self.runner.StopUI(dut_runner)
-    dut_runner.assert_called_once_with('stop ui')
-
-  def test_start_ui(self):
-    dut_runner = mock.Mock(return_value=(0, '', ''))
-    self.runner.StartUI(dut_runner)
-    dut_runner.assert_called_once_with('start ui')
+    DutWrapper.WaitCooldown.assert_called_once_with()
+    DutWrapper.DisableASLR.assert_called_once()
+    DutWrapper.DisableTurbo.assert_called_once_with()
+    DutWrapper.SetupCpuUsage.assert_called_once_with()
+    DutWrapper.SetupCpuFreq.assert_called_once_with([1])
+    DutWrapper.SetCpuGovernor.assert_called()
+    DutWrapper.GetCpuOnline.assert_called_once_with()
+    self.assertGreater(DutWrapper.SetCpuGovernor.call_count, 1)
+    self.assertEqual(DutWrapper.SetCpuGovernor.call_args,
+                     mock.call('fake_governor', ignore_status=False))
 
   @mock.patch.object(command_executer.CommandExecuter, 'CrosRunCommand')
   @mock.patch.object(command_executer.CommandExecuter,