• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2024 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 argparse
8from unittest import mock
9
10import hjson
11
12from crossbench import path as pth
13from crossbench.cli.config.probe import ProbeConfig, ProbeListConfig
14from crossbench.probes.power_sampler import PowerSamplerProbe
15from crossbench.probes.v8.log import V8LogProbe
16from crossbench.types import JsonDict
17from tests import test_helper
18from tests.crossbench.base import CrossbenchFakeFsTestCase
19
20
21class TestProbeConfig(CrossbenchFakeFsTestCase):
22  # pylint: disable=expression-not-assigned
23
24  def parse_config(self, config_data) -> ProbeListConfig:
25    probe_config_file = pth.LocalPath("/probe.config.hjson")
26    with probe_config_file.open("w", encoding="utf-8") as f:
27      hjson.dump(config_data, f)
28    return ProbeListConfig.parse(probe_config_file)
29
30  def test_invalid_empty(self):
31    with self.assertRaises(argparse.ArgumentTypeError):
32      self.parse_config({}).probes
33    with self.assertRaises(argparse.ArgumentTypeError):
34      self.parse_config({"foo": {}}).probes
35
36  def test_invalid_names(self):
37    with self.assertRaises(argparse.ArgumentTypeError):
38      self.parse_config({"probes": {"invalid probe name": {}}}).probes
39
40  def test_empty(self):
41    config = self.parse_config({"probes": {}})
42    self.assertListEqual(config.probes, [])
43
44  def test_single_v8_log(self):
45    js_flags = ["--log-maps", "--log-function-events"]
46    config = self.parse_config(
47        {"probes": {
48            "v8.log": {
49                "prof": True,
50                "js_flags": js_flags,
51            }
52        }})
53    self.assertTrue(len(config.probes), 1)
54    probe = config.probes[0]
55    assert isinstance(probe, V8LogProbe)
56    for flag in js_flags + ["--log-all"]:
57      self.assertIn(flag, probe.js_flags)
58
59  def test_from_cli_args(self):
60    file = pth.LocalPath("probe.config.hjson")
61    js_flags = ["--log-maps", "--log-function-events"]
62    config_data: JsonDict = {
63        "probes": {
64            "v8.log": {
65                "prof": True,
66                "js_flags": js_flags,
67            }
68        }
69    }
70    with file.open("w", encoding="utf-8") as f:
71      hjson.dump(config_data, f)
72    args = mock.Mock(probe_config=file)
73    config = ProbeListConfig.from_cli_args(args)
74    self.assertTrue(len(config.probes), 1)
75    probe = config.probes[0]
76    assert isinstance(probe, V8LogProbe)
77    for flag in js_flags + ["--log-all"]:
78      self.assertIn(flag, probe.js_flags)
79
80  def test_inline_config(self):
81    mock_d8_file = pth.LocalPath("out/d8")
82    self.fs.create_file(mock_d8_file, st_size=8 * 1024)
83    config_data = {"d8_binary": str(mock_d8_file)}
84    args = mock.Mock(probe_config=None, throw=True, wraps=False)
85
86    args.probe = [
87        ProbeConfig.parse(f"v8.log{hjson.dumps(config_data)}"),
88    ]
89    config = ProbeListConfig.from_cli_args(args)
90    self.assertTrue(len(config.probes), 1)
91    probe = config.probes[0]
92    self.assertTrue(isinstance(probe, V8LogProbe))
93
94    args.probe = [
95        ProbeConfig.parse(f"v8.log:{hjson.dumps(config_data)}"),
96    ]
97    config = ProbeListConfig.from_cli_args(args)
98    self.assertTrue(len(config.probes), 1)
99    probe = config.probes[0]
100    self.assertTrue(isinstance(probe, V8LogProbe))
101
102  def test_inline_config_invalid(self):
103    mock_d8_file = pth.LocalPath("out/d8")
104    self.fs.create_file(mock_d8_file)
105    config_data = {"d8_binary": str(mock_d8_file)}
106    trailing_brace = "}"
107    with self.assertRaises(argparse.ArgumentTypeError):
108      ProbeConfig.parse(f"v8.log{hjson.dumps(config_data)}{trailing_brace}")
109    with self.assertRaises(argparse.ArgumentTypeError):
110      ProbeConfig.parse(f"v8.log:{hjson.dumps(config_data)}{trailing_brace}")
111    with self.assertRaises(argparse.ArgumentTypeError):
112      ProbeConfig.parse("v8.log::")
113
114  def test_inline_config_dir_instead_of_file(self):
115    mock_dir = pth.LocalPath("some/dir")
116    mock_dir.mkdir(parents=True)
117    config_data = {"d8_binary": str(mock_dir)}
118    args = mock.Mock(
119        probe=[ProbeConfig.parse(f"v8.log{hjson.dumps(config_data)}")],
120        probe_config=None,
121        throw=True,
122        wraps=False)
123    with self.assertRaises(argparse.ArgumentTypeError) as cm:
124      ProbeListConfig.from_cli_args(args)
125    self.assertIn(str(mock_dir), str(cm.exception))
126
127  def test_inline_config_non_existent_file(self):
128    config_data = {"d8_binary": "does/not/exist/d8"}
129    args = mock.Mock(
130        probe=[ProbeConfig.parse(f"v8.log{hjson.dumps(config_data)}")],
131        probe_config=None,
132        throw=True,
133        wraps=False)
134    with self.assertRaises(argparse.ArgumentTypeError) as cm:
135      ProbeListConfig.from_cli_args(args)
136    expected_path = pth.LocalPath("does/not/exist/d8")
137    self.assertIn(str(expected_path), str(cm.exception))
138
139  def test_multiple_probes(self):
140    powersampler_bin = pth.LocalPath("/powersampler.bin")
141    powersampler_bin.touch()
142    config = self.parse_config({
143        "probes": {
144            "v8.log": {
145                "log_all": True,
146            },
147            "powersampler": {
148                "bin_path": str(powersampler_bin)
149            }
150        }
151    })
152    self.assertTrue(len(config.probes), 2)
153    log_probe = config.probes[0]
154    assert isinstance(log_probe, V8LogProbe)
155    powersampler_probe = config.probes[1]
156    assert isinstance(powersampler_probe, PowerSamplerProbe)
157    self.assertEqual(powersampler_probe.bin_path, powersampler_bin)
158
159
160if __name__ == "__main__":
161  test_helper.run_pytest(__file__)
162