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