• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from __future__ import annotations
6
7import functools
8import os
9from typing import Any, Dict, Optional, Tuple
10
11from crossbench import path as pth
12from crossbench.plt.base import SubprocessError
13from crossbench.plt.posix import PosixPlatform
14from crossbench.plt.remote import RemotePlatformMixin
15
16
17class LinuxPlatform(PosixPlatform):
18  SEARCH_PATHS: Tuple[pth.AnyPath, ...] = (
19      pth.AnyPosixPath("."),
20      pth.AnyPosixPath("/usr/local/sbin"),
21      pth.AnyPosixPath("/usr/local/bin"),
22      pth.AnyPosixPath("/usr/sbin"),
23      pth.AnyPosixPath("/usr/bin"),
24      pth.AnyPosixPath("/sbin"),
25      pth.AnyPosixPath("/bin"),
26      pth.AnyPosixPath("/opt/google"),
27  )
28
29  @property
30  def is_linux(self) -> bool:
31    return True
32
33  @property
34  def name(self) -> str:
35    return "linux"
36
37  def check_system_monitoring(self, disable: bool = False) -> bool:
38    return True
39
40  @functools.cached_property
41  def device(self) -> str:  #pylint: disable=invalid-overridden-method
42    try:
43      id_dir = self.path("/sys/devices/virtual/dmi/id")
44      vendor = self.cat(id_dir / "sys_vendor").strip()
45      product = self.cat(id_dir / "product_name").strip()
46      return f"{vendor} {product}"
47    except (FileNotFoundError, SubprocessError):
48      return "UNKNOWN"
49
50  @functools.cached_property
51  def cpu(self) -> str:  #pylint: disable=invalid-overridden-method
52    cpu_str = "UNKNOWN"
53    for line in self.cat(self.path("/proc/cpuinfo")).splitlines():
54      if line.startswith("model name"):
55        _, cpu_str = line.split(":", maxsplit=2)
56        break
57    if cores_info := self._get_cpu_cores_info():
58      cpu_str = f"{cpu_str} {cores_info}"
59    return cpu_str
60
61  @property
62  def has_display(self) -> bool:
63    return "DISPLAY" in os.environ
64
65  @property
66  def is_battery_powered(self) -> bool:
67    if self.is_local:
68      return super().is_battery_powered
69    if self.which("on_ac_power"):
70      return self.sh("on_ac_power", check=False).returncode == 1
71    return False
72
73  def system_details(self) -> Dict[str, Any]:
74    details = super().system_details()
75    for info_bin in ("lscpu", "inxi"):
76      if self.which(info_bin):
77        details[info_bin] = self.sh_stdout(info_bin)
78    return details
79
80  def search_binary(self, app_or_bin: pth.AnyPathLike) -> Optional[pth.AnyPath]:
81    app_or_bin_path: pth.AnyPath = self.path(app_or_bin)
82    if not app_or_bin_path.parts:
83      raise ValueError("Got empty path")
84    if result_path := self.which(app_or_bin_path):
85      if not self.exists(result_path):
86        raise RuntimeError(f"{result_path} does not exist.")
87      return result_path
88    for path in self.SEARCH_PATHS:
89      # Recreate Path object for easier pyfakefs testing
90      result_path = self.path(path) / app_or_bin_path
91      if self.exists(result_path):
92        return result_path
93    return None
94
95  def screenshot(self, result_path: pth.AnyPath) -> None:
96    # TODO: maybe use imagemagick's 'import' as more portable alternative
97    self.sh("gnome-screenshot", "--file", result_path)
98
99
100class RemoteLinuxPlatform(RemotePlatformMixin, LinuxPlatform):
101  pass
102