def test_target_overrides_separate_namespace(self): conf = Config({ "config": [ ConfigSetting(namespace="dontchangeme", name="network-default-interface-type", help_text="", value="WIFI"), ConfigSetting(namespace="changeme", name="network-default-interface-type", help_text="", value="WIFI"), ] }) conf.update({ "overrides": [ Override(namespace="changeme", name="network-default-interface-type", value="ETHERNET") ] }) dontchangeme, changeme, *_ = conf["config"] assert changeme.namespace == "changeme" assert changeme.value == "ETHERNET" assert dontchangeme.namespace == "dontchangeme" assert dontchangeme.value == "WIFI"
def test_warns_and_skips_override_for_undefined_config_parameter( self, caplog): conf = Config() override_name = "this-does-not-exist" conf.update(prepare({"target_overrides": {"*": {override_name: ""}}})) assert override_name in caplog.text assert not conf
def test_target_overrides_handled(self): conf = Config({ "config": [ ConfigSetting(namespace="target", name="network-default-interface-type", help_text="", value="WIFI") ], "device_has": ["TEST"], }) conf.update({ "overrides": [ Override(namespace="target", name="network-default-interface-type", value="ETHERNET"), Override(namespace="target", name="device_has", value={"OVERRIDDEN"}), ] }) network_iface, *_ = conf["config"] assert network_iface.value == "ETHERNET" assert conf["device_has"] == {"OVERRIDDEN"}
def test_macros_are_appended_to(self): conf = Config({"macros": {"A"}}) conf.update({"macros": {"B"}}) conf.update({"macros": {"B"}}) assert conf["macros"] == {"A", "B"}
def test_requires_are_appended_to(self): conf = Config({"requires": {"A"}}) conf.update({"requires": {"B"}}) conf.update({"requires": {"C"}}) assert conf["requires"] == {"A", "B", "C"}
def from_config(cls, config: Config) -> "FileFilterData": """Extract file filters from a Config object.""" return cls( labels=config.get("labels", set()) | config.get("extra_labels", set()), features=set(config.get("features", set())), components=set(config.get("components", set())), requires=set(config.get("requires", set())), )
def test_lib_overrides_handled(self): conf = Config( { "config": [ ConfigSetting(namespace="lib", name="network-default-interface-type", help_text="", value="WIFI") ], } ) conf.update({"overrides": [Override(namespace="lib", name="network-default-interface-type", value="ETHERNET")]}) network_iface, *_ = conf["config"] assert network_iface.value == "ETHERNET"
def test_raises_when_trying_to_add_duplicate_config_setting(self): conf = Config( prepare({"config": { "param": { "value": 0 } }}, source_name="lib")) with pytest.raises(ValueError, match="lib.param already defined"): conf.update( prepare({"config": { "param": { "value": 0 } }}, source_name="lib"))
def _assemble_config_from_sources_and_lib_files( target_attributes: dict, mbed_lib_files: Iterable[Path], mbed_app_file: Optional[Path] = None) -> Config: previous_cumulative_data = None target_source = Source.from_target(target_attributes) current_cumulative_data = CumulativeData.from_sources([target_source]) while previous_cumulative_data != current_cumulative_data: current_labels = current_cumulative_data.labels | current_cumulative_data.extra_labels filtered_files = _filter_files(mbed_lib_files, current_labels, current_cumulative_data.features, current_cumulative_data.components) mbed_lib_sources = [ Source.from_mbed_lib(file, current_labels) for file in filtered_files ] all_sources = [target_source] + mbed_lib_sources if mbed_app_file: all_sources = all_sources + [ Source.from_mbed_app(mbed_app_file, current_labels) ] previous_cumulative_data = current_cumulative_data current_cumulative_data = CumulativeData.from_sources(all_sources) _update_target_attributes(target_attributes, current_cumulative_data) return Config.from_sources(all_sources)
def test_builds_config_from_sources(self): source_a = SourceFactory(config={ "bool": True, "string": "foo" }, macros=["MACRO_A=A"]) source_b = SourceFactory(config={"number": 1}, overrides={"bool": False}, macros=["MACRO_B=B"]) config = Config.from_sources([source_a, source_b]) self.assertEqual( config.options, { "bool": Option.build("bool", True, source_a).set_value( False, source_b), "number": Option.build("number", 1, source_b), "string": Option.build("string", "foo", source_a), }, ) self.assertEqual( config.macros, { "MACRO_A": Macro.build("MACRO_A=A", source_a), "MACRO_B": Macro.build("MACRO_B=B", source_b) }, )
def test_keeps_old_option_data(self): source_a = SourceFactory( config={"bool": { "help": "A simple bool", "value": True }}) source_b = SourceFactory(overrides={"bool": False}) config = Config.from_sources([source_a, source_b]) self.assertEqual(config.options["bool"].help_text, "A simple bool")
def test_ignores_present_option(self): source = SourceFactory(config={ "mbed_component.present": { "help": "Mbed Component", "value": True } }) config = Config.from_sources([source]) self.assertFalse(config.options)
def test_cumulative_fields_can_be_modified(self): conf = Config({"device_has": {"FLASHING_LIGHTS"}, "macros": {"A"}}) conf.update({ "overrides": [ Override(namespace="target", name="device_has", modifier="add", value={"OTHER_STUFF"}), Override(namespace="target", name="device_has", modifier="remove", value={"FLASHING_LIGHTS"}), Override(namespace="lib", name="macros", modifier="remove", value={"A"}), Override(namespace="target", name="macros", modifier="add", value={"B"}), ] }) conf.update({ "overrides": [ Override(namespace="target", name="macros", modifier="add", value={"B"}) ] }) assert conf["device_has"] == {"OTHER_STUFF"} assert conf["macros"] == {"B"}
def _assemble_config_from_sources( target_attributes: dict, mbed_lib_files: List[Path], mbed_app_file: Optional[Path] = None) -> Config: config = Config(source.prepare(target_attributes, source_name="target")) previous_filter_data = None app_data = None if mbed_app_file: # We need to obtain the file filter data from mbed_app.json so we can select the correct set of mbed_lib.json # files to include in the config. We don't want to update the config object with all of the app settings yet # as we won't be able to apply overrides correctly until all relevant mbed_lib.json files have been parsed. app_data = source.from_file( mbed_app_file, default_name="app", target_filters=FileFilterData.from_config(config).labels) _get_app_filter_labels(app_data, config) current_filter_data = FileFilterData.from_config(config) while previous_filter_data != current_filter_data: filtered_files = _filter_files(mbed_lib_files, current_filter_data) for config_file in filtered_files: config.update( source.from_file(config_file, target_filters=current_filter_data.labels)) # Remove any mbed_lib files we've already visited from the list so we don't parse them multiple times. mbed_lib_files.remove(config_file) previous_filter_data = current_filter_data current_filter_data = FileFilterData.from_config(config) # Apply mbed_app.json data last so config parameters are overriden in the correct order. if app_data: config.update(app_data) return config
def test_config_updated(self): conf = Config() conf.update(prepare({"config": {"param": {"value": 0}}}, source_name="lib")) conf.update(prepare({"config": {"param2": {"value": 0}}}, source_name="lib2")) assert conf["config"][0].name == "param" assert conf["config"][1].name == "param2"
def test_ignores_present_option(self): source = prepare({ "name": "mbed_component", "config": { "present": { "help": "Mbed Component", "value": True } } }) config = Config(source) assert not config["config"]
def test_returns_quoted_content(self, fake_target): config = Config(prepare(fake_target)) # Add an option whose value contains quotes to the config. config["config"] = [ ConfigSetting( name="mqtt-host", namespace="iotc", help_text="", value='{"mqtt.2030.ltsapis.goog", IOTC_MQTT_PORT}', ) ] result = render_mbed_config_cmake_template(config, TOOLCHAIN_NAME, "target_name") assert '"-DMBED_CONF_IOTC_MQTT_HOST={\\"mqtt.2030.ltsapis.goog\\", IOTC_MQTT_PORT}"' in result
def test_returns_rendered_content(self, fake_target): config = Config(prepare(fake_target)) result = render_mbed_config_cmake_template(config, TOOLCHAIN_NAME, "target_name") for label in fake_target["labels"] + fake_target["extra_labels"]: assert label in result for macro in fake_target["features"] + fake_target["components"] + [ TOOLCHAIN_NAME ]: assert macro in result for toolchain in fake_target["supported_c_libs"]: assert toolchain in result for supported_c_libs in toolchain: assert supported_c_libs in result for supported_application_profiles in fake_target[ "supported_application_profiles"]: assert supported_application_profiles in result
def _assemble_config_from_sources_and_lib_files( target_source: Source, mbed_lib_files: Iterable[Path], mbed_app_file: Optional[Path] = None) -> Config: previous_cumulative_data = None current_cumulative_data = CumulativeData.from_sources([target_source]) while previous_cumulative_data != current_cumulative_data: filtered_files = _filter_files(mbed_lib_files, current_cumulative_data) mbed_lib_sources = [ Source.from_mbed_lib(file, current_cumulative_data.labels) for file in filtered_files ] all_sources = [target_source] + mbed_lib_sources if mbed_app_file: all_sources = all_sources + [ Source.from_mbed_app(mbed_app_file, current_cumulative_data.labels) ] previous_cumulative_data = current_cumulative_data current_cumulative_data = CumulativeData.from_sources(all_sources) return Config.from_sources(all_sources)
def test_assembles_config_using_all_relevant_files(self): target = { "config": { "foo": { "value": None } }, "macros": [], "labels": ["A"], "extra_labels": [], "features": ["RED"], "components": [], "c_lib": "std", "printf_lib": "minimal-printf", } mbed_lib_files = [ { "path": Path("subdir", "FEATURE_RED", "mbed_lib.json"), "json_contents": { "name": "red", "config": { "bool": { "value": False } }, "target_overrides": { "A": { "bool": True, "target.features_add": ["BLUE"], "target.components_add": ["LEG"] } }, "macros": ["RED_MACRO"], }, }, { "path": Path("TARGET_A", "mbed_lib.json"), "json_contents": { "name": "a", "config": { "number": { "value": 123 } }, "target_overrides": { "*": { "target.features_add": ["RED"] } }, }, }, { "path": Path("COMPONENT_LEG", "mbed_lib.json"), "json_contents": { "name": "leg", "config": { "number-of-fingers": { "value": 5 } }, "macros": ["LEG_MACRO"], }, }, ] unused_mbed_lib_file = { "path": Path("subdir", "FEATURE_BROWN", "mbed_lib.json"), "json_contents": { "name": "brown", "target_overrides": { "*": { "red.bool": "DON'T USE ME" } }, "macros": ["DONT_USE_THIS_MACRO"], }, } mbed_app_file = { "path": Path("mbed_app.json"), "json_contents": { "target_overrides": { "*": { "target.foo": "bar" } } }, } with TemporaryDirectory() as directory: created_mbed_lib_files = create_files(directory, mbed_lib_files) created_mbed_app_file = create_files(directory, [mbed_app_file])[0] create_files(directory, [unused_mbed_lib_file]) subject = _assemble_config_from_sources( target, find_files("mbed_lib.json", Path(directory)), created_mbed_app_file) mbed_lib_sources = [ prepare(decode_json_file(Path(directory, file)), target_filters=["A"]) for file in created_mbed_lib_files ] mbed_app_source = prepare(decode_json_file( Path(directory, created_mbed_app_file)), target_filters=["A"]) expected_config = Config(prepare(target, source_name="target")) for source in mbed_lib_sources + [mbed_app_source]: expected_config.update(source) subject["config"].sort(key=lambda x: x.name) expected_config["config"].sort(key=lambda x: x.name) assert subject == expected_config
def _get_app_filter_labels(mbed_app_data: dict, config: Config) -> None: requires = mbed_app_data.get("requires") if requires: config["requires"] = requires config.update(_get_file_filter_overrides(mbed_app_data))
def test_does_not_explode_on_override_keys_used_by_other_parsers(self): for key in CUMULATIVE_OVERRIDE_KEYS_IN_SOURCE + BOOTLOADER_OVERRIDE_KEYS_IN_SOURCE: with self.subTest("Ignores override key '{key}'"): source_a = SourceFactory() source_b = SourceFactory(overrides={key: "boom?"}) Config.from_sources([source_a, source_b])
def test_raises_when_trying_to_override_existing_macro(self): source_a = SourceFactory(macros=["MACRO_A=X"]) source_b = SourceFactory(macros=["MACRO_A=Y"]) with self.assertRaises(ValueError): Config.from_sources([source_a, source_b])
def test_raises_when_trying_to_override_unset_option(self): source_a = SourceFactory(config={"bool": True}) source_b = SourceFactory(overrides={"string": "hello"}) with self.assertRaises(ValueError): Config.from_sources([source_a, source_b])
def test_assembles_config_using_all_relevant_files(self): target = { "config": { "foo": None }, "macros": {}, "labels": set("A"), "extra_labels": set(), "features": set("RED"), "components": set(), "c_lib": "std", "printf_lib": "minimal-printf", } mbed_lib_files = [ { "path": Path("TARGET_A", "mbed_lib.json"), "json_contents": { "name": "a", "config": { "number": 123 }, "target_overrides": { "*": { "target.features_add": ["RED"] } }, }, }, { "path": Path("subdir", "FEATURE_RED", "mbed_lib.json"), "json_contents": { "name": "red", "config": { "bool": False }, "target_overrides": { "A": { "bool": True, "target.features_add": ["BLUE"], "target.components_add": ["LEG"] } }, "macros": ["RED_MACRO"], }, }, { "path": Path("COMPONENT_LEG", "mbed_lib.json"), "json_contents": { "name": "leg", "config": { "number-of-fingers": 5 }, "macros": ["LEG_MACRO"] }, }, ] unused_mbed_lib_file = { "path": Path("subdir", "FEATURE_BROWN", "mbed_lib.json"), "json_contents": { "name": "brown", "target_overrides": { "*": { "red.bool": "DON'T USE ME" } }, "macros": ["DONT_USE_THIS_MACRO"], }, } mbed_app_file = { "path": Path("mbed_app.json"), "json_contents": { "target_overrides": { "*": { "target.foo": "bar" } } }, } with TemporaryDirectory() as directory: created_mbed_lib_files = create_files(directory, mbed_lib_files) created_mbed_app_file = create_files(directory, [mbed_app_file])[0] create_files(directory, [unused_mbed_lib_file]) subject = _assemble_config_from_sources_and_lib_files( target, find_files("mbed_lib.json", Path(directory)), created_mbed_app_file) mbed_lib_sources = [ Source.from_mbed_lib(Path(directory, file), ["A"]) for file in created_mbed_lib_files ] mbed_app_source = Source.from_mbed_app(created_mbed_app_file, ["A"]) expected_config = Config.from_sources( [Source.from_target(target)] + mbed_lib_sources + [mbed_app_source]) self.assertEqual(subject, expected_config)