| # Copyright 2023 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Tests for the pw_build.gn_config module.""" |
| |
| import unittest |
| |
| from typing import Iterable |
| |
| from pw_build.gn_config import ( |
| consolidate_configs, |
| GnConfig, |
| GN_CONFIG_FLAGS, |
| ) |
| from pw_build.gn_utils import GnLabel, MalformedGnError |
| |
| |
| class TestGnConfig(unittest.TestCase): |
| """Tests for gn_config.GnConfig. |
| |
| Attributes: |
| config: A common config for testing. |
| """ |
| |
| def compare(self, actual: Iterable[str], *expected: str): |
| """Sorts lists and compares them.""" |
| self.assertEqual(sorted(expected), sorted(list(actual))) |
| |
| def setUp(self): |
| self.config = GnConfig() |
| |
| def test_bool_empty(self): |
| """Identifies an empty config correctly.""" |
| self.assertFalse(self.config) |
| |
| def test_bool_nonempty(self): |
| """Identifies a non-empty config correctly.""" |
| self.config.add('defines', 'KEY=VAL') |
| self.assertTrue(self.config) |
| |
| def test_has_none(self): |
| """Indicates a config does not have a flag when it has no values.""" |
| self.assertFalse(self.config.has('cflags')) |
| |
| def test_has_single(self): |
| """Indicates a config has a flag when it has one value.""" |
| self.config.add('cflags', '-frobinator') |
| self.assertTrue(self.config.has('cflags')) |
| |
| def test_has_multiple(self): |
| """Indicates a config has a flag when it has multiple values.""" |
| self.config.add('cflags', '-frobinator') |
| self.config.add('cflags', '-fizzbuzzer') |
| self.assertTrue(self.config.has('cflags')) |
| |
| def test_has_two_flags(self): |
| """Indicates a config has a flag when it has multiple flags.""" |
| self.config.add('cflags', '-frobinator') |
| self.config.add('cflags_c', '-foobarbaz') |
| self.assertTrue(self.config.has('cflags')) |
| |
| def test_add_and_get(self): |
| """Tests ability to add valid flags to a config.""" |
| for flag in GN_CONFIG_FLAGS: |
| self.config.add(flag, f'{flag}_value1') |
| self.config.add(flag, f'{flag}_value2', f'{flag}_value3') |
| for flag in GN_CONFIG_FLAGS: |
| self.compare( |
| self.config.get(flag), |
| f'{flag}_value1', |
| f'{flag}_value2', |
| f'{flag}_value3', |
| ) |
| |
| def test_add_and_get_invalid(self): |
| """Tests ability to add only valid flags to a config.""" |
| with self.assertRaises(MalformedGnError): |
| self.config.add('invalid', 'value') |
| |
| def test_take_missing(self): |
| """Tests ability to return an empty list when taking a missing flag.""" |
| self.compare(self.config.take('cflags')) |
| self.assertFalse(self.config.has('defines')) |
| self.assertFalse(self.config.has('cflags')) |
| |
| def test_take(self): |
| """Tests ability to remove and return values from a config.""" |
| self.config.add('defines', 'KEY1=VAL1', 'KEY2=VAL2') |
| self.config.add('cflags', '-frobinator', '-fizzbuzzer') |
| self.assertTrue(self.config.has('defines')) |
| self.assertTrue(self.config.has('cflags')) |
| |
| self.compare(self.config.take('cflags'), '-frobinator', '-fizzbuzzer') |
| self.assertTrue(self.config.has('defines')) |
| self.assertFalse(self.config.has('cflags')) |
| |
| def test_deduplicate_no_label(self): |
| """Tests raising an error from deduplicating a labelless config.""" |
| cfg1 = GnConfig() |
| cfg1.add('defines', 'KEY1=VAL1') |
| with self.assertRaises(ValueError): |
| list(self.config.deduplicate(cfg1)) |
| |
| def test_deduplicate_empty(self): |
| """Tests deduplicating an empty config.""" |
| cfg2 = GnConfig() |
| cfg2.label = ':cfg2' |
| self.assertFalse(list(self.config.deduplicate(cfg2))) |
| |
| def test_deduplicate_not_subset(self): |
| """Tests deduplicating a config whose values are not a subset.""" |
| self.config.add('defines', 'KEY1=VAL1', 'KEY2=VAL2') |
| self.config.add('cflags', '-frobinator', '-fizzbuzzer', '-foobarbaz') |
| |
| cfg3 = GnConfig() |
| cfg3.label = ':cfg3' |
| cfg3.add('defines', 'KEY1=VAL1', 'KEY3=VAL3') |
| self.assertFalse(list(self.config.deduplicate(cfg3))) |
| |
| def test_deduplicate(self): |
| """Tests deduplicating multiple overlapping configs.""" |
| self.config.add('defines', 'KEY1=VAL1', 'KEY2=VAL2') |
| self.config.add('cflags', '-frobinator', '-fizzbuzzer', '-foobarbaz') |
| |
| cfg4 = GnConfig() |
| cfg4.label = ':cfg4' |
| cfg4.add('defines', 'KEY1=VAL1') |
| cfg4.add('cflags', '-frobinator') |
| |
| cfg5 = GnConfig() |
| cfg5.label = ':cfg5' |
| cfg5.add('defines', 'KEY1=VAL1') |
| cfg5.add('cflags', '-foobarbaz') |
| |
| cfg6 = GnConfig() |
| cfg6.label = ':skipped' |
| cfg6.add('cflags', '-foobarbaz') |
| |
| cfgs = [ |
| str(cfg.label) for cfg in self.config.deduplicate(cfg4, cfg5, cfg6) |
| ] |
| self.assertEqual(cfgs, [':cfg4', ':cfg5']) |
| self.compare(self.config.get('defines'), 'KEY2=VAL2') |
| self.compare(self.config.get('cflags'), '-fizzbuzzer') |
| |
| def test_extract_public(self): |
| """Tests ability to removes and return `public_config` values.""" |
| self.config.add('public_defines', 'KEY1=VAL1', 'KEY2=VAL2') |
| self.config.add('defines', 'KEY3=VAL3', 'KEY4=VAL4') |
| self.config.add('include_dirs', 'foo', 'bar', 'baz') |
| self.config.add('cflags', '-frobinator', '-fizzbuzzer') |
| |
| config = self.config.extract_public() |
| |
| self.assertFalse(self.config.has('public_defines')) |
| self.assertFalse(self.config.has('include_dirs'), 'KEY2=VAL2') |
| self.compare(self.config.get('defines'), 'KEY3=VAL3', 'KEY4=VAL4') |
| self.compare(self.config.get('cflags'), '-frobinator', '-fizzbuzzer') |
| |
| self.assertFalse(config.has('public_defines')) |
| self.compare(config.get('defines'), 'KEY1=VAL1', 'KEY2=VAL2') |
| self.compare(config.get('include_dirs'), 'foo', 'bar', 'baz') |
| self.assertFalse(config.has('cflags')) |
| |
| def test_consolidate_configs(self): |
| """Tests ability to merges configs into the smallest exact cover.""" |
| cfg1 = GnConfig() |
| cfg1.add('cflags', 'one', 'a', 'b') |
| cfg1.add('defines', 'k=1') |
| cfg1.add('include_dirs', 'foo', 'bar') |
| |
| cfg2 = GnConfig() |
| cfg2.add('cflags', 'one', 'a', 'b', 'c') |
| cfg2.add('defines', 'k=1') |
| cfg2.add('include_dirs', 'foo', 'baz') |
| |
| cfg3 = GnConfig() |
| cfg3.add('cflags', 'one', 'two', 'a', 'b') |
| cfg3.add('defines', 'k=1') |
| cfg3.add('include_dirs', 'foo', 'bar') |
| |
| cfg4 = GnConfig() |
| cfg4.add('cflags', 'one', 'b', 'c') |
| cfg4.add('defines', 'k=0') |
| cfg4.add('include_dirs', 'foo', 'baz') |
| |
| label = GnLabel('//foo/bar') |
| common = list(consolidate_configs(label, cfg1, cfg2, cfg3, cfg4)) |
| self.assertEqual(len(common), 3) |
| self.assertEqual(str(common[0].label), '//foo/bar:bar_public_config1') |
| self.assertEqual(str(common[1].label), '//foo/bar:bar_config1') |
| self.assertEqual(str(common[2].label), '//foo/bar:bar_config2') |
| self.compare(common[0].get('include_dirs'), 'foo') |
| self.compare(common[1].get('cflags'), 'one', 'b') |
| self.compare(common[2].get('cflags'), 'a') |
| self.compare(common[2].get('defines'), 'k=1') |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |