• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Tests for the pw_build.gn_config module."""
15
16import unittest
17
18from typing import Iterable
19
20from pw_build.gn_config import (
21    consolidate_configs,
22    GnConfig,
23    GN_CONFIG_FLAGS,
24)
25from pw_build.gn_utils import GnLabel, MalformedGnError
26
27
28class TestGnConfig(unittest.TestCase):
29    """Tests for gn_config.GnConfig.
30
31    Attributes:
32        config: A common config for testing.
33    """
34
35    def compare(self, actual: Iterable[str], *expected: str):
36        """Sorts lists and compares them."""
37        self.assertEqual(sorted(expected), sorted(list(actual)))
38
39    def setUp(self):
40        self.config = GnConfig()
41
42    def test_bool_empty(self):
43        """Identifies an empty config correctly."""
44        self.assertFalse(self.config)
45
46    def test_bool_nonempty(self):
47        """Identifies a non-empty config correctly."""
48        self.config.add('defines', 'KEY=VAL')
49        self.assertTrue(self.config)
50
51    def test_has_none(self):
52        """Indicates a config does not have a flag when it has no values."""
53        self.assertFalse(self.config.has('cflags'))
54
55    def test_has_single(self):
56        """Indicates a config has a flag when it has one value."""
57        self.config.add('cflags', '-frobinator')
58        self.assertTrue(self.config.has('cflags'))
59
60    def test_has_multiple(self):
61        """Indicates a config has a flag when it has multiple values."""
62        self.config.add('cflags', '-frobinator')
63        self.config.add('cflags', '-fizzbuzzer')
64        self.assertTrue(self.config.has('cflags'))
65
66    def test_has_two_flags(self):
67        """Indicates a config has a flag when it has multiple flags."""
68        self.config.add('cflags', '-frobinator')
69        self.config.add('cflags_c', '-foobarbaz')
70        self.assertTrue(self.config.has('cflags'))
71
72    def test_add_and_get(self):
73        """Tests ability to add valid flags to a config."""
74        for flag in GN_CONFIG_FLAGS:
75            self.config.add(flag, f'{flag}_value1')
76            self.config.add(flag, f'{flag}_value2', f'{flag}_value3')
77        for flag in GN_CONFIG_FLAGS:
78            self.compare(
79                self.config.get(flag),
80                f'{flag}_value1',
81                f'{flag}_value2',
82                f'{flag}_value3',
83            )
84
85    def test_add_and_get_invalid(self):
86        """Tests ability to add only valid flags to a config."""
87        with self.assertRaises(MalformedGnError):
88            self.config.add('invalid', 'value')
89
90    def test_take_missing(self):
91        """Tests ability to return an empty list when taking a missing flag."""
92        self.compare(self.config.take('cflags'))
93        self.assertFalse(self.config.has('defines'))
94        self.assertFalse(self.config.has('cflags'))
95
96    def test_take(self):
97        """Tests ability to remove and return values from a config."""
98        self.config.add('defines', 'KEY1=VAL1', 'KEY2=VAL2')
99        self.config.add('cflags', '-frobinator', '-fizzbuzzer')
100        self.assertTrue(self.config.has('defines'))
101        self.assertTrue(self.config.has('cflags'))
102
103        self.compare(self.config.take('cflags'), '-frobinator', '-fizzbuzzer')
104        self.assertTrue(self.config.has('defines'))
105        self.assertFalse(self.config.has('cflags'))
106
107    def test_deduplicate_no_label(self):
108        """Tests raising an error from deduplicating a labelless config."""
109        cfg1 = GnConfig()
110        cfg1.add('defines', 'KEY1=VAL1')
111        with self.assertRaises(ValueError):
112            list(self.config.deduplicate(cfg1))
113
114    def test_deduplicate_empty(self):
115        """Tests deduplicating an empty config."""
116        cfg2 = GnConfig()
117        cfg2.label = ':cfg2'
118        self.assertFalse(list(self.config.deduplicate(cfg2)))
119
120    def test_deduplicate_not_subset(self):
121        """Tests deduplicating a config whose values are not a subset."""
122        self.config.add('defines', 'KEY1=VAL1', 'KEY2=VAL2')
123        self.config.add('cflags', '-frobinator', '-fizzbuzzer', '-foobarbaz')
124
125        cfg3 = GnConfig()
126        cfg3.label = ':cfg3'
127        cfg3.add('defines', 'KEY1=VAL1', 'KEY3=VAL3')
128        self.assertFalse(list(self.config.deduplicate(cfg3)))
129
130    def test_deduplicate(self):
131        """Tests deduplicating multiple overlapping configs."""
132        self.config.add('defines', 'KEY1=VAL1', 'KEY2=VAL2')
133        self.config.add('cflags', '-frobinator', '-fizzbuzzer', '-foobarbaz')
134
135        cfg4 = GnConfig()
136        cfg4.label = ':cfg4'
137        cfg4.add('defines', 'KEY1=VAL1')
138        cfg4.add('cflags', '-frobinator')
139
140        cfg5 = GnConfig()
141        cfg5.label = ':cfg5'
142        cfg5.add('defines', 'KEY1=VAL1')
143        cfg5.add('cflags', '-foobarbaz')
144
145        cfg6 = GnConfig()
146        cfg6.label = ':skipped'
147        cfg6.add('cflags', '-foobarbaz')
148
149        cfgs = [
150            str(cfg.label) for cfg in self.config.deduplicate(cfg4, cfg5, cfg6)
151        ]
152        self.assertEqual(cfgs, [':cfg4', ':cfg5'])
153        self.compare(self.config.get('defines'), 'KEY2=VAL2')
154        self.compare(self.config.get('cflags'), '-fizzbuzzer')
155
156    def test_extract_public(self):
157        """Tests ability to removes and return `public_config` values."""
158        self.config.add('public_defines', 'KEY1=VAL1', 'KEY2=VAL2')
159        self.config.add('defines', 'KEY3=VAL3', 'KEY4=VAL4')
160        self.config.add('include_dirs', 'foo', 'bar', 'baz')
161        self.config.add('cflags', '-frobinator', '-fizzbuzzer')
162
163        config = self.config.extract_public()
164
165        self.assertFalse(self.config.has('public_defines'))
166        self.assertFalse(self.config.has('include_dirs'), 'KEY2=VAL2')
167        self.compare(self.config.get('defines'), 'KEY3=VAL3', 'KEY4=VAL4')
168        self.compare(self.config.get('cflags'), '-frobinator', '-fizzbuzzer')
169
170        self.assertFalse(config.has('public_defines'))
171        self.compare(config.get('defines'), 'KEY1=VAL1', 'KEY2=VAL2')
172        self.compare(config.get('include_dirs'), 'foo', 'bar', 'baz')
173        self.assertFalse(config.has('cflags'))
174
175    def test_consolidate_configs(self):
176        """Tests ability to merges configs into the smallest exact cover."""
177        cfg1 = GnConfig()
178        cfg1.add('cflags', 'one', 'a', 'b')
179        cfg1.add('defines', 'k=1')
180        cfg1.add('include_dirs', 'foo', 'bar')
181
182        cfg2 = GnConfig()
183        cfg2.add('cflags', 'one', 'a', 'b', 'c')
184        cfg2.add('defines', 'k=1')
185        cfg2.add('include_dirs', 'foo', 'baz')
186
187        cfg3 = GnConfig()
188        cfg3.add('cflags', 'one', 'two', 'a', 'b')
189        cfg3.add('defines', 'k=1')
190        cfg3.add('include_dirs', 'foo', 'bar')
191
192        cfg4 = GnConfig()
193        cfg4.add('cflags', 'one', 'b', 'c')
194        cfg4.add('defines', 'k=0')
195        cfg4.add('include_dirs', 'foo', 'baz')
196
197        label = GnLabel('//foo/bar')
198        common = list(consolidate_configs(label, cfg1, cfg2, cfg3, cfg4))
199        self.assertEqual(len(common), 3)
200        self.assertEqual(str(common[0].label), '//foo/bar:bar_public_config1')
201        self.assertEqual(str(common[1].label), '//foo/bar:bar_config1')
202        self.assertEqual(str(common[2].label), '//foo/bar:bar_config2')
203        self.compare(common[0].get('include_dirs'), 'foo')
204        self.compare(common[1].get('cflags'), 'one', 'b')
205        self.compare(common[2].get('cflags'), 'a')
206        self.compare(common[2].get('defines'), 'k=1')
207
208
209if __name__ == '__main__':
210    unittest.main()
211