• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2022 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
5import argparse
6import unittest
7
8from crossbench import compat
9from crossbench.probes.probe import Probe, ProbeConfigParser
10from tests import test_helper
11
12
13class MockProbe(Probe):
14  """
15  Probe DOC Text
16  """
17
18
19class CustomArgType:
20
21  def __init__(self, value):
22    self.value = value
23
24
25def custom_arg_type(value):
26  return CustomArgType(value)
27
28
29class ProbeConfigTestCase(unittest.TestCase):
30
31  def test_help_text(self):
32    parser = ProbeConfigParser(MockProbe)
33    parser.add_argument("bool", type=bool)
34    parser.add_argument("bool_default", type=bool, default=False)
35    parser.add_argument("str_empty_default", type=str, default="")
36    parser.add_argument("bool_list", type=bool, default=(False,), is_list=True)
37    parser.add_argument("any_list", type=None, default=(1,), is_list=True)
38    parser.add_argument(
39        "empty_default_list", type=None, default=[], is_list=True)
40    parser.add_argument("custom_type", type=custom_arg_type)
41    parser.add_argument("custom_help", type=bool, help="custom help")
42    help_text = str(parser)
43    self.assertIn("Probe DOC Text", help_text)
44    self.assertIn("bool_default", help_text)
45    self.assertIn("str_empty_default", help_text)
46    self.assertIn("bool_list", help_text)
47    self.assertIn("any_list", help_text)
48    self.assertIn("empty_default_list", help_text)
49    self.assertIn("custom_type", help_text)
50    self.assertIn("custom_help", help_text)
51
52  def test_invalid_config_duplicate(self):
53    parser = ProbeConfigParser(MockProbe)
54    parser.add_argument("bool", type=bool)
55    with self.assertRaises(ValueError):
56      parser.add_argument("bool", type=bool)
57
58  def test_config_defaults(self):
59    parser = ProbeConfigParser(MockProbe)
60    with self.assertRaises(ValueError):
61      parser.add_argument("bool", type=bool, default=1)
62    parser.add_argument("any", type=object, default=1)
63
64  def test_config_defaults_list(self):
65    parser = ProbeConfigParser(MockProbe)
66    with self.assertRaisesRegex(ValueError, "True"):
67      parser.add_argument("bool", type=bool, is_list=True, default=True)
68    with self.assertRaisesRegex(ValueError, "string"):
69      parser.add_argument("str", type=str, is_list=True, default="str")
70    with self.assertRaises(ValueError):
71      parser.add_argument("bool", type=bool, is_list=True, default=(1, 1))
72    parser.add_argument("bool", type=bool, is_list=True, default=(True, False))
73    parser.add_argument(
74        "custom_list",
75        type=lambda x: x + 1,
76        is_list=True,
77        default=(True, False))
78
79  def test_bool_missing_property(self):
80    parser = ProbeConfigParser(MockProbe)
81    parser.add_argument("bool_argument_name", type=bool, required=True)
82    with self.assertRaises(argparse.ArgumentTypeError):
83      parser.kwargs_from_config({})
84    with self.assertRaises(argparse.ArgumentTypeError):
85      parser.kwargs_from_config({"other": True})
86
87  def test_bool_invalid_value(self):
88    parser = ProbeConfigParser(MockProbe)
89    parser.add_argument("bool_argument_name", type=bool)
90    parser_required = ProbeConfigParser(MockProbe)
91    parser_required.add_argument("bool_argument_name", type=bool, required=True)
92    with self.assertRaises(argparse.ArgumentTypeError):
93      parser.kwargs_from_config({"bool_argument_name": "not a bool"})
94    with self.assertRaises(argparse.ArgumentTypeError):
95      parser.kwargs_from_config({"bool_argument_name": ""})
96    with self.assertRaises(argparse.ArgumentTypeError):
97      parser.kwargs_from_config({"bool_argument_name": {}})
98    with self.assertRaises(argparse.ArgumentTypeError):
99      parser.kwargs_from_config({"bool_argument_name": []})
100    # Argument is not required, this should pass
101    parser.kwargs_from_config({"bool_argument_name": None})
102    with self.assertRaises(argparse.ArgumentTypeError):
103      parser_required.kwargs_from_config({"bool_argument_name": None})
104    with self.assertRaises(argparse.ArgumentTypeError):
105      parser.kwargs_from_config({"bool_argument_name": 0})
106
107  def test_bool_default(self):
108    parser = ProbeConfigParser(MockProbe)
109    parser.add_argument("bool_argument_name", type=bool, default=False)
110
111    config_data = {}
112    kwargs = parser.kwargs_from_config(config_data)
113    self.assertDictEqual(config_data, {})
114    self.assertDictEqual(kwargs, {"bool_argument_name": False})
115
116    config_data = {"bool_argument_name": True}
117    kwargs = parser.kwargs_from_config(config_data)
118    self.assertDictEqual(config_data, {"bool_argument_name": True})
119    self.assertDictEqual(kwargs, {"bool_argument_name": True})
120
121  def test_bool(self):
122    parser = ProbeConfigParser(MockProbe)
123    parser.add_argument("bool_argument_name", type=bool)
124    config_data = {"bool_argument_name": True}
125    kwargs = parser.kwargs_from_config(config_data)
126    self.assertDictEqual(config_data, {"bool_argument_name": True})
127    self.assertDictEqual(kwargs, {"bool_argument_name": True})
128
129  def test_int_list_invalid(self):
130    parser = ProbeConfigParser(MockProbe)
131    parser.add_argument("int_list", type=int, is_list=True, default=[111, 222])
132    with self.assertRaises(argparse.ArgumentTypeError):
133      parser.kwargs_from_config({"int_list": 9})
134    with self.assertRaises(argparse.ArgumentTypeError):
135      parser.kwargs_from_config({"int_list": ["0", "1"]})
136    with self.assertRaises(argparse.ArgumentTypeError):
137      parser.kwargs_from_config({"int_list": "0,1"})
138
139  def test_int_list(self):
140    parser = ProbeConfigParser(MockProbe)
141    parser.add_argument("int_list", type=int, is_list=True, default=[111, 222])
142    kwargs = parser.kwargs_from_config({})
143    self.assertDictEqual(kwargs, {"int_list": [111, 222]})
144
145    config_data = {"int_list": [0, 1]}
146    kwargs = parser.kwargs_from_config(config_data)
147    self.assertDictEqual(config_data, {"int_list": [0, 1]})
148    self.assertDictEqual(kwargs, {"int_list": [0, 1]})
149
150  def test_custom_type(self):
151    parser = ProbeConfigParser(MockProbe)
152    parser.add_argument("custom", type=custom_arg_type)
153    config_data = {"custom": [1, 2, "stuff"]}
154    kwargs = parser.kwargs_from_config(config_data)
155    self.assertDictEqual(config_data, {"custom": [1, 2, "stuff"]})
156    result = kwargs["custom"]
157    self.assertIsInstance(result, CustomArgType)
158    self.assertListEqual(result.value, [1, 2, "stuff"])
159
160  def test_no_type(self):
161    parser = ProbeConfigParser(MockProbe)
162    parser.add_argument("custom", type=None)
163    for data in ["", "a", {"a": 1}, set(), []]:
164      config_data = {"custom": data}
165      kwargs = parser.kwargs_from_config(config_data)
166      self.assertIs(kwargs["custom"], data)
167
168  def test_no_type_choices(self):
169    parser = ProbeConfigParser(MockProbe)
170    with self.assertRaises(ValueError):
171      parser.add_argument("unused", type=None, choices=[1, 1, 1])
172    with self.assertRaises(AssertionError):
173      parser.add_argument("unused", type=None, choices=[])
174    parser.add_argument("choice", type=None, choices=["a", "b"])
175    with self.assertRaises(argparse.ArgumentTypeError):
176      parser.kwargs_from_config({"choice": ""})
177    with self.assertRaises(argparse.ArgumentTypeError):
178      parser.kwargs_from_config({"choice": "unknown"})
179    kwargs = parser.kwargs_from_config({"choice": "a"})
180    self.assertIs(kwargs["choice"], "a")
181    kwargs = parser.kwargs_from_config({"choice": "b"})
182    self.assertIs(kwargs["choice"], "b")
183
184  def test_enum_type(self):
185
186    class MyEnum(compat.StrEnum):
187      ONE = "one"
188      TWO = "two"
189
190    parser = ProbeConfigParser(MockProbe)
191    with self.assertRaises(AssertionError):
192      parser.add_argument("unused", type=MyEnum, choices=["ONE", "TWO"])
193    with self.assertRaises(AssertionError):
194      parser.add_argument("unused", type=MyEnum, choices=["one"])
195    parser.add_argument("my-enum-one", type=MyEnum, choices=[MyEnum.ONE])
196    with self.assertRaises(argparse.ArgumentTypeError):
197      parser.kwargs_from_config({"my-enum-one": ""})
198    kwargs = parser.kwargs_from_config({"my-enum-one": "two"})
199    self.assertIs(kwargs["my-enum-one"], MyEnum.TWO)
200    kwargs = parser.kwargs_from_config({"my-enum-one": "one"})
201    self.assertIs(kwargs["my-enum-one"], MyEnum.ONE)
202
203    parser.add_argument("my-enum", type=MyEnum)
204    kwargs = parser.kwargs_from_config({"my-enum": "one"})
205    self.assertIs(kwargs["my-enum"], MyEnum.ONE)
206    kwargs = parser.kwargs_from_config({"my-enum": "two"})
207    self.assertIs(kwargs["my-enum"], MyEnum.TWO)
208    with self.assertRaises(argparse.ArgumentTypeError):
209      parser.kwargs_from_config({"my-enum": ""})
210    with self.assertRaises(argparse.ArgumentTypeError):
211      parser.kwargs_from_config({"my-enum": "three"})
212    with self.assertRaises(argparse.ArgumentTypeError):
213      parser.kwargs_from_config({"my-enum": "TWO"})
214
215  def test_enum_with_help(self):
216
217    class MyEnum(compat.StrEnumWithHelp):
218      ONE = ("oneX", "the one help")
219      TWO = ("twoX", "the two help")
220
221    parser = ProbeConfigParser(MockProbe)
222    parser.add_argument("my-enum", type=MyEnum, choices=[MyEnum.ONE])
223    text = str(parser)
224    self.assertIn("the one help", text)
225    self.assertIn("the two help", text)
226    self.assertIn("oneX", text)
227    self.assertIn("twoX", text)
228
229
230if __name__ == "__main__":
231  test_helper.run_pytest(__file__)
232