• 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
8import unittest
9
10from crossbench.cli.config.browser_variants import (FlagsConfig,
11                                                    FlagsGroupConfig,
12                                                    FlagsVariantConfig)
13from crossbench.config import ConfigError
14from crossbench.exception import ArgumentTypeMultiException
15from crossbench.flags.base import Flags
16from tests import test_helper
17
18
19class FlagsConfigTestCase(unittest.TestCase):
20
21  def test_invalid_empty(self):
22    with self.assertRaises(ArgumentTypeMultiException) as cm:
23      FlagsConfig.parse("")
24    self.assertIn("empty", str(cm.exception).lower())
25    with self.assertRaises(ConfigError) as cm:
26      FlagsConfig.parse_str("")
27    self.assertIn("empty", str(cm.exception).lower())
28
29  def test_empty_dict(self):
30    config = FlagsConfig.parse({})
31    self.assertFalse(config)
32
33  def test_parse_empty_group(self):
34    config = FlagsConfig.parse({
35        "a": None,
36        "b": {},
37        "c": tuple(),
38    })
39    self.assertEqual(len(config), 3)
40    for group in config.values():
41      self.assertFalse(group)
42    self.assertFalse(config["a"])
43    self.assertFalse(config["b"])
44    self.assertFalse(config["c"])
45
46  def test_parse_str(self):
47    config = FlagsConfig.parse("--foo --bar")
48    self.assertEqual(len(config), 1)
49    self.assertEqual(str(config["default"][0].flags), "--foo --bar")
50
51  def test_parse_single_str_groups(self):
52    config = FlagsConfig.parse({
53        "a": "--foo=1 --bar",
54        "b": "--foo=2 --bar",
55    })
56    self.assertEqual(len(config), 2)
57    self.assertEqual(len(config["a"]), 1)
58    self.assertEqual(len(config["b"]), 1)
59    flags_a = config["a"][0].flags
60    flags_b = config["b"][0].flags
61    self.assertEqual(len(flags_a), 2)
62    self.assertEqual(len(flags_b), 2)
63    self.assertEqual(str(flags_a), "--foo=1 --bar")
64    self.assertEqual(str(flags_b), "--foo=2 --bar")
65
66  def test_parse_single_dict_groups(self):
67    config = FlagsConfig.parse({
68        "a": {
69            "--foo": "1",
70            "--bar": None,
71        },
72        "b": {
73            "--foo": "2",
74            "--bar": None
75        }
76    })
77    self.assertEqual(len(config), 2)
78    self.assertEqual(len(config["a"]), 1)
79    self.assertEqual(len(config["b"]), 1)
80    flags_a = config["a"][0].flags
81    flags_b = config["b"][0].flags
82    self.assertEqual(len(flags_a), 2)
83    self.assertEqual(len(flags_b), 2)
84    self.assertEqual(str(flags_a), "--foo=1 --bar")
85    self.assertEqual(str(flags_b), "--foo=2 --bar")
86
87  def test_parse_multi_str_groups(self):
88    config = FlagsConfig.parse({
89        "a": [
90            "--foo=1 --bar=1",
91            "--foo=1 --bar=2",
92        ],
93        "b": "--foo=2 --bar",
94    })
95    self.assertEqual(len(config), 2)
96    self.assertEqual(len(config["a"]), 2)
97    self.assertEqual(len(config["b"]), 1)
98    labels = tuple(v.label for v in config["a"])  # pylint: disable=no-member
99    self.assertTupleEqual(labels, ("foo=1_bar=1", "foo=1_bar=2"))
100    variants_a = config["a"]
101    flags_a_1 = variants_a[0].flags
102    flags_a_2 = variants_a[1].flags
103    self.assertEqual(str(flags_a_1), "--foo=1 --bar=1")
104    self.assertEqual(str(flags_a_2), "--foo=1 --bar=2")
105
106    flags_b = config["b"][0].flags
107    self.assertEqual(len(flags_b), 2)
108    self.assertEqual(str(flags_b), "--foo=2 --bar")
109
110  def test_parse_multi_dict_str_groups(self):
111    config = FlagsConfig.parse({
112        "a": {
113            "label_a_1": "--foo=1 --bar=1",
114            "label_a_2": "--foo=1 --bar=2",
115        }
116    })
117    self.assertEqual(len(config), 1)
118    self.assertEqual(len(config["a"]), 2)
119
120    self.assertTupleEqual(
121        tuple(v.label for v in config["a"]), ("label_a_1", "label_a_2"))
122    variants_a = config["a"]
123    flags_a_1 = variants_a[0].flags
124    flags_a_2 = variants_a[1].flags
125    self.assertEqual(str(flags_a_1), "--foo=1 --bar=1")
126    self.assertEqual(str(flags_a_2), "--foo=1 --bar=2")
127
128  def test_parse_multi_dict_dict_groups(self):
129    config = FlagsConfig.parse({
130        "a": {
131            "label_a_1": {
132                "--foo": "1",
133                "--bar": "1"
134            },
135            "label_a_2": {
136                "--bar": "2",
137                "--foo": "1",
138            }
139        }
140    })
141    self.assertEqual(len(config), 1)
142    self.assertEqual(len(config["a"]), 2)
143    self.assertTupleEqual(
144        tuple(v.label for v in config["a"]), ("label_a_1", "label_a_2"))
145    variants_a = config["a"]
146    flags_a_1 = variants_a[0].flags
147    flags_a_2 = variants_a[1].flags
148    self.assertEqual(str(flags_a_1), "--foo=1 --bar=1")
149    self.assertEqual(str(flags_a_2), "--bar=2 --foo=1")
150
151  def test_parse_variants_groups(self):
152    config = FlagsConfig.parse(
153        {"a": {
154            "--foo": [None, "1"],
155            "--bar": ["1", "2"],
156        }})
157    self.assertEqual(len(config), 1)
158    self.assertEqual(len(config["a"]), 4)
159
160    self.assertTupleEqual(
161        tuple(v.label for v in config["a"]),
162        ("bar=1", "bar=2", "foo=1_bar=1", "foo=1_bar=2"))
163    variants_a = config["a"]
164    self.assertEqual(str(variants_a[0].flags), "--bar=1")
165    self.assertEqual(str(variants_a[1].flags), "--bar=2")
166    self.assertEqual(str(variants_a[2].flags), "--foo=1 --bar=1")
167    self.assertEqual(str(variants_a[3].flags), "--foo=1 --bar=2")
168
169
170class FlagsVariantConfigTestCase(unittest.TestCase):
171
172  def test_empty(self):
173    empty = FlagsVariantConfig("default")
174    self.assertEqual(empty.label, "default")
175    self.assertFalse(empty.flags)
176    self.assertEqual(empty.index, 0)
177
178  def test_merge_copy(self):
179    flags_a = Flags.parse("--foo-a")
180    flags_b = Flags.parse("--bar-b=1")
181    variant_a = FlagsVariantConfig("label_a", 0, flags_a)
182    variant_b = FlagsVariantConfig("label_b", 1, flags_b)
183    variant = variant_a.merge_copy(variant_b)
184    self.assertEqual(variant.label, "label_a_label_b")
185    self.assertEqual(str(variant.flags), "--foo-a --bar-b=1")
186    self.assertEqual(variant.index, 0)
187
188    variant = variant_a.merge_copy(variant_b, index=11, label="custom_label")
189    self.assertEqual(variant.label, "custom_label")
190    self.assertEqual(str(variant.flags), "--foo-a --bar-b=1")
191    self.assertEqual(variant.index, 11)
192
193  def test_equal(self):
194    variant_a = FlagsVariantConfig.parse("label_a", 0, "--foo=a")
195    variant_b = FlagsVariantConfig.parse("label_b", 1, "--foo=a")
196    variant_c = FlagsVariantConfig.parse("label_b", 1, "--foo=b")
197    self.assertEqual(variant_a, variant_b)
198    self.assertEqual(variant_b, variant_a)
199    self.assertNotEqual(variant_a, variant_c)
200    self.assertNotEqual(variant_b, variant_c)
201    variants = set((variant_a,))
202    self.assertIn(variant_a, variants)
203    self.assertIn(variant_b, variants)
204    self.assertNotIn(variant_c, variants)
205
206
207class FlagsGroupConfigTestCase(unittest.TestCase):
208
209  def test_parse_empty(self):
210    for empty in (None, [], (), {}, "", "  "):
211      with self.subTest(flags=empty):
212        self.assertFalse(FlagsGroupConfig.parse(empty))
213
214  def test_parse_invalid(self):
215    for invalid in (-1, 0, 1):
216      with self.subTest(invalid=invalid):
217        with self.assertRaises(ConfigError):
218          FlagsGroupConfig.parse(invalid)
219
220  def test_parse_str_single(self):
221    group = FlagsGroupConfig.parse("--foo-a=1")
222    self.assertEqual(len(group), 1)
223    self.assertEqual(str(group[0].flags), "--foo-a=1")
224    self.assertEqual(group[0].label, "default")
225
226  def test_parse_str_multiple(self):
227    group = FlagsGroupConfig.parse(("--foo-a=1 --bar", "--foo-a=2"))
228    self.assertEqual(len(group), 2)
229    self.assertEqual(str(group[0].flags), "--foo-a=1 --bar")
230    self.assertEqual(str(group[1].flags), "--foo-a=2")
231
232  def test_parse_str_multiple_empty(self):
233    group = FlagsGroupConfig.parse(("", "--foo", "-foo=v1"))
234    self.assertEqual(len(group), 3)
235    self.assertEqual(str(group[0].flags), "")
236    self.assertEqual(str(group[1].flags), "--foo")
237    self.assertEqual(str(group[2].flags), "-foo=v1")
238
239  def test_parse_dict_simple(self):
240    group = FlagsGroupConfig.parse({"--foo": "1", "--bar": "2"})
241    self.assertEqual(len(group), 1)
242    self.assertEqual(str(group[0].flags), "--foo=1 --bar=2")
243    self.assertEqual(group[0].label, "default")
244
245  def test_parse_dict_invalid_variant(self):
246    for invalid in (-1, 0):
247      with self.subTest(invalid=invalid):
248        with self.assertRaises(ValueError):
249          FlagsGroupConfig.parse({
250              "--foo": "1",
251              "--invalid": invalid,
252              "--bar": "2",
253          })
254
255  def test_parse_duplicate_variant_value(self):
256    for duplicate in (None, "", "value"):
257      with self.subTest(duplicate=duplicate):
258        with self.assertRaises(ValueError) as cm:
259          FlagsGroupConfig.parse({"--duplicate": [duplicate, duplicate]})
260        self.assertIn("duplicate", str(cm.exception))
261    with self.assertRaises(ConfigError) as cm:
262      FlagsGroupConfig.parse(
263          ["--foo --duplicate='foo'", "--foo --duplicate='foo'"])
264    self.assertIn("duplicate", str(cm.exception))
265
266  def test_parse_dict_single_with_labels(self):
267    group = FlagsGroupConfig.parse({
268        "config_1": "--foo=1 --bar",
269        "config_2": "",
270    })
271    self.assertEqual(len(group), 2)
272    self.assertEqual(str(group[0].flags), "--foo=1 --bar")
273    self.assertEqual(str(group[1].flags), "")
274    self.assertEqual(group[0].label, "config_1")
275    self.assertEqual(group[1].label, "config_2")
276    for index, group in enumerate(group):
277      self.assertEqual(group.index, index)
278
279  def test_parse_dict_with_labels_duplicate_flags(self):
280    with self.assertRaises(argparse.ArgumentTypeError) as cm:
281      _ = FlagsGroupConfig.parse({
282          "config_1": "--foo=1 --bar",
283          "config_2": "--foo=1 --bar",
284      })
285    self.assertIn("duplicate", str(cm.exception).lower())
286    self.assertIn("--foo=1 --bar", str(cm.exception).lower())
287
288  def test_parse_dict_single(self):
289    group = FlagsGroupConfig.parse({
290        "--foo": "1",
291        "--bar": None,
292    })
293    self.assertEqual(len(group), 1)
294    self.assertEqual(str(group[0].flags), "--foo=1 --bar")
295
296  def test_parse_dict_multiple_3_x_1(self):
297    group = FlagsGroupConfig.parse({
298        "--foo": [None, "1", "2"],
299        "--bar": None,
300    })
301    self.assertEqual(len(group), 3)
302    self.assertEqual(str(group[0].flags), "--bar")
303    self.assertEqual(str(group[1].flags), "--foo=1 --bar")
304    self.assertEqual(str(group[2].flags), "--foo=2 --bar")
305    for index, group in enumerate(group):
306      self.assertEqual(group.index, index)
307
308  def test_parse_dict_multiple_2_x_2(self):
309    group = FlagsGroupConfig.parse({
310        "--foo": [None, "a"],
311        "--bar": [None, "b"],
312    })
313    self.assertEqual(len(group), 4)
314    self.assertEqual(str(group[0].flags), "")
315    self.assertEqual(str(group[1].flags), "--bar=b")
316    self.assertEqual(str(group[2].flags), "--foo=a")
317    self.assertEqual(str(group[3].flags), "--foo=a --bar=b")
318    self.assertEqual(group[0].label, "default")
319    self.assertEqual(group[1].label, "bar=b")
320    self.assertEqual(group[2].label, "foo=a")
321    self.assertEqual(group[3].label, "foo=a_bar=b")
322    for index, group in enumerate(group):
323      self.assertEqual(group.index, index)
324
325  def test_product_single(self):
326    group_a = FlagsGroupConfig.parse("--foo-a=1")
327    group_b = FlagsGroupConfig.parse("--foo-b=1")
328    self.assertEqual(group_a[0].label, "default")
329    self.assertEqual(group_b[0].label, "default")
330    group = group_a.product(group_b)
331    self.assertEqual(len(group), 1)
332    self.assertEqual(str(group[0].flags), "--foo-a=1 --foo-b=1")
333    self.assertEqual(group[0].label, "default")
334
335  def test_product_empty_empty(self):
336    group_a = FlagsGroupConfig()
337    group_b = FlagsGroupConfig()
338    group = group_a.product(group_b)
339    self.assertFalse(group)
340    group = group_a.product(group_b, group_b, group_b)
341    self.assertFalse(group)
342
343  def test_product_same(self):
344    group_a = FlagsGroupConfig.parse("--foo-b=1")
345    self.assertEqual(group_a[0].label, "default")
346    group = group_a.product(group_a)
347    self.assertEqual(len(group), 1)
348    self.assertEqual(str(group[0].flags), "--foo-b=1")
349    self.assertEqual(group[0].label, "default")
350    group = group_a.product(group_a, group_a, group_a)
351    self.assertEqual(len(group), 1)
352    self.assertEqual(str(group[0].flags), "--foo-b=1")
353    self.assertEqual(group[0].label, "default")
354
355  def test_product_same_values(self):
356    group_a = FlagsGroupConfig.parse("--foo-b=1")
357    group_b = FlagsGroupConfig.parse("--foo-b=1")
358    group = group_a.product(group_b)
359    self.assertEqual(len(group), 1)
360    self.assertEqual(str(group[0].flags), "--foo-b=1")
361    group = group_a.product(group_a, group_a, group_a)
362    self.assertEqual(len(group), 1)
363    self.assertEqual(str(group[0].flags), "--foo-b=1")
364
365  def test_product_empty(self):
366    group_a = FlagsGroupConfig.parse("")
367    group_b = FlagsGroupConfig.parse("--foo-b=1")
368    group = group_a.product(group_b)
369    self.assertEqual(len(group), 1)
370    self.assertEqual(str(group[0].flags), "--foo-b=1")
371    group = group_b.product(group_a)
372    self.assertEqual(len(group), 1)
373    self.assertEqual(str(group[0].flags), "--foo-b=1")
374
375  def test_product_2_x_1(self):
376    group_a = FlagsGroupConfig.parse((
377        None,
378        "--foo-a=1",
379    ))
380    group_b = FlagsGroupConfig.parse("--foo-b=1")
381    group = group_a.product(group_b)
382    self.assertEqual(len(group), 2)
383    self.assertEqual(str(group[0].flags), "--foo-b=1")
384    self.assertEqual(str(group[1].flags), "--foo-a=1 --foo-b=1")
385    self.assertEqual(group[0].label, "default")
386    self.assertEqual(group[1].label, "foo_a=1")
387
388  def test_product_2_x_2(self):
389    group_a = FlagsGroupConfig.parse((
390        None,
391        "--foo-a=1",
392    ))
393    group_b = FlagsGroupConfig.parse((None, "--foo-b=1"))
394    group = group_a.product(group_b)
395    self.assertEqual(len(group), 4)
396    self.assertEqual(str(group[0].flags), "")
397    self.assertEqual(str(group[1].flags), "--foo-b=1")
398    self.assertEqual(str(group[2].flags), "--foo-a=1")
399    self.assertEqual(str(group[3].flags), "--foo-a=1 --foo-b=1")
400    self.assertEqual(group[0].label, "default")
401    self.assertEqual(group[1].label, "foo_b=1")
402    self.assertEqual(group[2].label, "foo_a=1")
403    self.assertEqual(group[3].label, "foo_a=1_foo_b=1")
404    for index, group in enumerate(group):
405      self.assertEqual(group.index, index)
406
407  def test_product_conflicting(self):
408    group_a = FlagsGroupConfig.parse(("--foo=1"))
409    group_b = FlagsGroupConfig.parse(("--foo=2"))
410    with self.assertRaises(ValueError) as cm:
411      group_a.product(group_b)
412    self.assertIn("different previous value", str(cm.exception))
413
414
415if __name__ == "__main__":
416  test_helper.run_pytest(__file__)
417