diff --git a/platformio/datamodel.py b/platformio/datamodel.py index 92121114..4583646a 100644 --- a/platformio/datamodel.py +++ b/platformio/datamodel.py @@ -172,7 +172,12 @@ class DataModel(object): setattr(self, name, value) def __eq__(self, other): - raise NotImplementedError + assert isinstance(other, DataModel) + if self.get_field_names() != other.get_field_names(): + return False + if len(self.get_exceptions()) != len(other.get_exceptions()): + return False + return self.as_dict() == other.as_dict() def __repr__(self): fields = [] diff --git a/platformio/package/manifest/model.py b/platformio/package/manifest/model.py index bfbbfa24..5045725d 100644 --- a/platformio/package/manifest/model.py +++ b/platformio/package/manifest/model.py @@ -79,6 +79,14 @@ class ManifestModel(DataModel): export = DataField(type=ExportModel) examples = DataField(type=DictOfType(ExampleModel)) + # platform.json specific + title = DataField(max_length=100) + + # package.json specific + system = DataField( + type=ListOfType(DataField(max_length=50, regex=r"^[a-z\d\-_]+$")) + ) + class StrictManifestModel(ManifestModel, StrictDataModel): pass diff --git a/platformio/package/manifest/parser.py b/platformio/package/manifest/parser.py index c2489a97..291fbae6 100644 --- a/platformio/package/manifest/parser.py +++ b/platformio/package/manifest/parser.py @@ -401,3 +401,44 @@ class LibraryPropertiesManifestParser(BaseManifestParser): "include": include, "exclude": ["extras", "docs", "tests", "test", "*.doxyfile", "*.pdf"], } + + +class PlatformJsonManifestParser(BaseManifestParser): + def parse(self, contents): + data = json.loads(contents) + if "frameworks" in data: + data["frameworks"] = self._parse_frameworks(data["frameworks"]) + return data + + @staticmethod + def _parse_frameworks(raw): + if not isinstance(raw, dict): + return None + return [name.lower() for name in raw.keys()] + + +class PackageJsonManifestParser(BaseManifestParser): + def parse(self, contents): + data = json.loads(contents) + data = self._parse_system(data) + data = self._parse_homepage(data) + return data + + @staticmethod + def _parse_system(data): + if "system" not in data: + return data + if data["system"] in ("*", ["*"]): + del data["system"] + return data + if not isinstance(data["system"], list): + data["system"] = [data["system"]] + data["system"] = [s.strip().lower() for s in data["system"]] + return data + + @staticmethod + def _parse_homepage(data): + if "url" in data: + data["homepage"] = data["url"] + del data["url"] + return data diff --git a/tests/test_pkgmanifest.py b/tests/test_pkgmanifest.py index a465f8c3..453ef9a9 100644 --- a/tests/test_pkgmanifest.py +++ b/tests/test_pkgmanifest.py @@ -206,13 +206,13 @@ def test_library_json_model(): } } """ - data = parser.ManifestParserFactory.new( + mp = parser.ManifestParserFactory.new( contents, parser.ManifestFileType.LIBRARY_JSON ) - model = ManifestModel(**data.as_dict()) + model = StrictManifestModel(**mp.as_dict()) assert model.repository.url == "https://github.com/bblanchon/ArduinoJson.git" assert model.examples["JsonHttpClient"].files == ["JsonHttpClient.ino"] - assert model == ManifestModel( + assert model == StrictManifestModel( **{ "name": "ArduinoJson", "keywords": ["json", "rest", "http", "web"], @@ -226,10 +226,10 @@ def test_library_json_model(): "version": "6.12.0", "authors": [ { + "name": "Benoit Blanchon", "url": "https://blog.benoitblanchon.fr", "maintainer": False, "email": None, - "name": "Benoit Blanchon", } ], "export": { @@ -265,12 +265,12 @@ category=Display url=https://github.com/olikraus/u8glib architectures=avr,sam """ - data = parser.ManifestParserFactory.new( + mp = parser.ManifestParserFactory.new( contents, parser.ManifestFileType.LIBRARY_PROPERTIES ) - model = ManifestModel(**data.as_dict()) + model = StrictManifestModel(**mp.as_dict()) assert not model.get_exceptions() - assert model == ManifestModel( + assert model == StrictManifestModel( **{ "license": None, "description": ( @@ -304,6 +304,120 @@ architectures=avr,sam ) +def test_platform_json_model(): + contents = """ +{ + "name": "atmelavr", + "title": "Atmel AVR", + "description": "Atmel AVR 8- and 32-bit MCUs deliver a unique combination of performance, power efficiency and design flexibility. Optimized to speed time to market-and easily adapt to new ones-they are based on the industrys most code-efficient architecture for C and assembly programming.", + "url": "http://www.atmel.com/products/microcontrollers/avr/default.aspx", + "homepage": "http://platformio.org/platforms/atmelavr", + "license": "Apache-2.0", + "engines": { + "platformio": "<5" + }, + "repository": { + "type": "git", + "url": "https://github.com/platformio/platform-atmelavr.git" + }, + "version": "1.15.0", + "frameworks": { + "arduino": { + "package": "framework-arduinoavr", + "script": "builder/frameworks/arduino.py" + }, + "simba": { + "package": "framework-simba", + "script": "builder/frameworks/simba.py" + } + }, + "packages": { + "toolchain-atmelavr": { + "type": "toolchain", + "version": "~1.50400.0" + }, + "framework-arduinoavr": { + "type": "framework", + "optional": true, + "version": "~4.2.0" + }, + "framework-simba": { + "type": "framework", + "optional": true, + "version": ">=7.0.0" + }, + "tool-avrdude": { + "type": "uploader", + "optional": true, + "version": "~1.60300.0" + } + } +} +""" + mp = parser.ManifestParserFactory.new( + contents, parser.ManifestFileType.PLATFORM_JSON + ) + model = ManifestModel(**mp.as_dict()) + assert sorted(model.frameworks) == sorted(["arduino", "simba"]) + assert model == ManifestModel( + **{ + "name": "atmelavr", + "title": "Atmel AVR", + "description": ( + "Atmel AVR 8- and 32-bit MCUs deliver a unique combination of " + "performance, power efficiency and design flexibility. Optimized to " + "speed time to market-and easily adapt to new ones-they are based " + "on the industrys most code-efficient architecture for C and " + "assembly programming." + ), + "homepage": "http://platformio.org/platforms/atmelavr", + "license": "Apache-2.0", + "repository": { + "url": "https://github.com/platformio/platform-atmelavr.git", + "type": "git", + "branch": None, + }, + "frameworks": ["simba", "arduino"], + "version": "1.15.0", + } + ) + + +def test_package_json_model(): + contents = """ +{ + "name": "tool-scons", + "description": "SCons software construction tool", + "url": "http://www.scons.org", + "version": "3.30101.0" +} +""" + mp = parser.ManifestParserFactory.new( + contents, parser.ManifestFileType.PACKAGE_JSON + ) + model = ManifestModel(**mp.as_dict()) + assert model.system is None + assert model.homepage == "http://www.scons.org" + assert model == ManifestModel( + **{ + "name": "tool-scons", + "description": "SCons software construction tool", + "homepage": "http://www.scons.org", + "version": "3.30101.0", + } + ) + + mp = parser.ManifestParserFactory.new( + '{"system": "*"}', parser.ManifestFileType.PACKAGE_JSON + ) + assert "system" not in mp.as_dict() + + mp = parser.ManifestParserFactory.new( + '{"system": "darwin_x86_64"}', parser.ManifestFileType.PACKAGE_JSON + ) + assert mp.as_dict()["system"] == ["darwin_x86_64"] + + def test_broken_models(): # non-strict mode assert len(ManifestModel(name="MyPackage").get_exceptions()) == 4