From f0d12c40ad3ed4c632153b9e80a7206aa1a3566d Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 27 Dec 2020 11:55:18 -0800 Subject: [PATCH 1/6] Make schema.Validate() general purpose In order to work with the nested data structures of the platform configuration files, it's necessary to modify this function. Although the necessary properties.Map conversion could be done by schema.Validate(), this will not support working with the package index. So it's best to make it support all requirements now, rather than having to modify it yet again. --- .../libraryproperties/libraryproperties.go | 14 ++++- internal/rule/schema/schema.go | 11 +--- internal/rule/schema/schema_test.go | 55 ++++++++++++------- 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/internal/project/library/libraryproperties/libraryproperties.go b/internal/project/library/libraryproperties/libraryproperties.go index 7bf4f5f26..40b1b7591 100644 --- a/internal/project/library/libraryproperties/libraryproperties.go +++ b/internal/project/library/libraryproperties/libraryproperties.go @@ -46,9 +46,17 @@ func Validate(libraryProperties *properties.Map) map[compliancelevel.Type]schema schemaObject[compliancelevel.Strict] = schema.Compile("arduino-library-properties-strict-schema.json", referencedSchemaFilenames, schemadata.Asset) } - validationResults[compliancelevel.Permissive] = schema.Validate(libraryProperties, schemaObject[compliancelevel.Permissive]) - validationResults[compliancelevel.Specification] = schema.Validate(libraryProperties, schemaObject[compliancelevel.Specification]) - validationResults[compliancelevel.Strict] = schema.Validate(libraryProperties, schemaObject[compliancelevel.Strict]) + // Convert the library.properties data from the native properties.Map type to the interface type required by the schema + // validation package. + libraryPropertiesMap := libraryProperties.AsMap() + libraryPropertiesInterface := make(map[string]interface{}, len(libraryPropertiesMap)) + for k, v := range libraryPropertiesMap { + libraryPropertiesInterface[k] = v + } + + validationResults[compliancelevel.Permissive] = schema.Validate(libraryPropertiesInterface, schemaObject[compliancelevel.Permissive]) + validationResults[compliancelevel.Specification] = schema.Validate(libraryPropertiesInterface, schemaObject[compliancelevel.Specification]) + validationResults[compliancelevel.Strict] = schema.Validate(libraryPropertiesInterface, schemaObject[compliancelevel.Strict]) return validationResults } diff --git a/internal/rule/schema/schema.go b/internal/rule/schema/schema.go index db81e158f..21fb16fa5 100644 --- a/internal/rule/schema/schema.go +++ b/internal/rule/schema/schema.go @@ -24,7 +24,6 @@ import ( "io/ioutil" "path" - "github.com/arduino/go-properties-orderedmap" "github.com/ory/jsonschema/v3" "github.com/sirupsen/logrus" "github.com/xeipuuv/gojsonreference" @@ -80,15 +79,7 @@ func Compile(schemaFilename string, referencedSchemaFilenames []string, dataLoad // Validate validates an instance against a JSON schema and returns nil if it was success, or the // jsonschema.ValidationError object otherwise. -func Validate(instanceObject *properties.Map, schemaObject Schema) ValidationResult { - // Convert the instance data from the native properties.Map type to the interface type required by the schema - // validation package. - instanceObjectMap := instanceObject.AsMap() - instanceInterface := make(map[string]interface{}, len(instanceObjectMap)) - for k, v := range instanceObjectMap { - instanceInterface[k] = v - } - +func Validate(instanceInterface map[string]interface{}, schemaObject Schema) ValidationResult { validationError := schemaObject.Compiled.ValidateInterface(instanceInterface) result, _ := validationError.(*jsonschema.ValidationError) validationResult := ValidationResult{ diff --git a/internal/rule/schema/schema_test.go b/internal/rule/schema/schema_test.go index d5a07bbe4..b51689416 100644 --- a/internal/rule/schema/schema_test.go +++ b/internal/rule/schema/schema_test.go @@ -46,6 +46,23 @@ func init() { ) } +// propertiesToMap converts properties.Map data structures to map[string]interface. +func propertiesToMap(propertiesInput *properties.Map) map[string]interface{} { + mapOutput := make(map[string]interface{}) + keys := propertiesInput.FirstLevelKeys() + for _, key := range keys { + subTree := propertiesInput.SubTree(key) + if subTree.Size() > 0 { + // This key contains a map, recurse it. + mapOutput[key] = propertiesToMap(subTree) + } else { + // This key contains a string, no more recursion is possible. + mapOutput[key] = propertiesInput.Get(key) + } + } + return mapOutput +} + func TestCompile(t *testing.T) { require.Panics(t, func() { Compile("valid-schema-with-references.json", []string{"nonexistent.json"}, testdata.Asset) @@ -82,35 +99,35 @@ func TestCompile(t *testing.T) { func TestValidate(t *testing.T) { schemaObject := Compile("valid-schema.json", []string{}, testdata.Asset) propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, schemaObject) + validationResult := Validate(propertiesToMap(propertiesMap), schemaObject) require.Nil(t, validationResult.Result) - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.Nil(t, validationResult.Result) propertiesMap.Set("property1", "a") - validationResult = Validate(propertiesMap, schemaObject) + validationResult = Validate(propertiesToMap(propertiesMap), schemaObject) require.Equal(t, "#/property1", validationResult.Result.InstancePtr) require.Equal(t, "#/properties/property1/minLength", validationResult.Result.SchemaPtr) } func TestRequiredPropertyMissing(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences) + validationResult := Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.False(t, RequiredPropertyMissing("property1", validationResult)) propertiesMap.Remove("property1") - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.True(t, RequiredPropertyMissing("property1", validationResult)) } func TestPropertyPatternMismatch(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences) + validationResult := Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.False(t, PropertyPatternMismatch("property2", validationResult)) propertiesMap.Set("property2", "fOo") - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.True(t, PropertyPatternMismatch("property2", validationResult)) require.False(t, PropertyPatternMismatch("property1", validationResult)) @@ -118,51 +135,51 @@ func TestPropertyPatternMismatch(t *testing.T) { func TestPropertyLessThanMinLength(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences) + validationResult := Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.False(t, PropertyLessThanMinLength("property1", validationResult)) propertiesMap.Set("property1", "a") - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.True(t, PropertyLessThanMinLength("property1", validationResult)) } func TestPropertyGreaterThanMaxLength(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences) + validationResult := Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.False(t, PropertyGreaterThanMaxLength("property1", validationResult)) propertiesMap.Set("property1", "12345") - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.True(t, PropertyGreaterThanMaxLength("property1", validationResult)) } func TestPropertyEnumMismatch(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences) + validationResult := Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.False(t, PropertyEnumMismatch("property3", validationResult)) propertiesMap.Set("property3", "invalid") - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.True(t, PropertyEnumMismatch("property3", validationResult)) } func TestMisspelledOptionalPropertyFound(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences) + validationResult := Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.False(t, MisspelledOptionalPropertyFound(validationResult)) propertiesMap.Set("porperties", "foo") - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.True(t, MisspelledOptionalPropertyFound(validationResult)) } func TestValidationErrorMatch(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences) + validationResult := Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.False(t, ValidationErrorMatch("", "", "", "", validationResult)) propertiesMap.Set("property2", "fOo") - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.False(t, ValidationErrorMatch("nomatch", "nomatch", "nomatch", "nomatch", validationResult)) require.False(t, ValidationErrorMatch("^#/property2$", "nomatch", "nomatch", "nomatch", validationResult)) require.False(t, ValidationErrorMatch("^#/property2$", "/pattern$", "nomatch", "nomatch", validationResult)) @@ -171,12 +188,12 @@ func TestValidationErrorMatch(t *testing.T) { require.True(t, ValidationErrorMatch("", "", "", "", validationResult)) propertiesMap.Set("property3", "bAz") - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.True(t, ValidationErrorMatch("^#/property3$", "/pattern$", "", "", validationResult), "Match pointer below logic inversion keyword") propertiesMap = properties.NewFromHashmap(validMap) propertiesMap.Remove("property1") - validationResult = Validate(propertiesMap, validSchemaWithReferences) + validationResult = Validate(propertiesToMap(propertiesMap), validSchemaWithReferences) require.False(t, ValidationErrorMatch("nomatch", "nomatch", "nomatch", "nomatch", validationResult)) require.True(t, ValidationErrorMatch("", "", "", "^#/property1$", validationResult)) } From 2b4b5360058f6a96eaa1c4b3b1acc8a4fa71cbc3 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 28 Dec 2020 07:19:58 -0800 Subject: [PATCH 2/6] Correct typo in doc comment There was a copy/paste error in the comment. --- internal/project/platform/boardstxt/boardstxt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/project/platform/boardstxt/boardstxt.go b/internal/project/platform/boardstxt/boardstxt.go index e46848f29..e340c67b1 100644 --- a/internal/project/platform/boardstxt/boardstxt.go +++ b/internal/project/platform/boardstxt/boardstxt.go @@ -24,7 +24,7 @@ import ( "github.com/arduino/go-properties-orderedmap" ) -// Properties parses the library.properties from the given path and returns the data. +// Properties parses the boards.txt from the given path and returns the data. func Properties(platformPath *paths.Path) (*properties.Map, error) { return properties.SafeLoadFromPath(platformPath.Join("boards.txt")) } From 4ab9fe48105a0794676a02e98229e09471416642 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sat, 2 Jan 2021 02:52:12 -0800 Subject: [PATCH 3/6] Correct source folder name for JSON schemas --- Taskfile.yml | 2 +- .../arduino-library-properties-definitions-schema.json | 2 +- .../arduino-library-properties-permissive-schema.json | 2 +- etc/schemas/arduino-library-properties-schema.json | 2 +- .../arduino-library-properties-strict-schema.json | 2 +- etc/schemas/general-definitions-schema.json | 2 +- internal/rule/schema/schemadata/bindata.go | 10 +++++----- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 8e4ae0683..130ba1ff8 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -41,7 +41,7 @@ tasks: schema:compile: desc: Compile JSON schema cmds: - - npx ajv-cli compile -s "./etc/schema/*.json" + - npx ajv-cli compile -s "./etc/schemas/*.json" check: desc: Lint and check formatting of all files diff --git a/etc/schemas/arduino-library-properties-definitions-schema.json b/etc/schemas/arduino-library-properties-definitions-schema.json index 03e7aac2a..acb398704 100644 --- a/etc/schemas/arduino-library-properties-definitions-schema.json +++ b/etc/schemas/arduino-library-properties-definitions-schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/arduino-library-properties-definitions-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-library-properties-definitions-schema.json", "title": "Shared definitions for the Arduino library.properties schemas", "type": "object", "definitions": { diff --git a/etc/schemas/arduino-library-properties-permissive-schema.json b/etc/schemas/arduino-library-properties-permissive-schema.json index 3b51ecb55..9bf33b109 100644 --- a/etc/schemas/arduino-library-properties-permissive-schema.json +++ b/etc/schemas/arduino-library-properties-permissive-schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/arduino-library-properties-permissive-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-library-properties-permissive-schema.json", "title": "Arduino library.properties JSON permissive schema", "description": "library.properties is the metadata file for Arduino libraries. This schema defines the minimum requirements for this file. See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-metadata", "$comment": "For information on the Arduino library.properties format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", diff --git a/etc/schemas/arduino-library-properties-schema.json b/etc/schemas/arduino-library-properties-schema.json index 847c234a1..ab1cadda2 100644 --- a/etc/schemas/arduino-library-properties-schema.json +++ b/etc/schemas/arduino-library-properties-schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/arduino-library-properties-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-library-properties-schema.json", "title": "Arduino library.properties JSON schema", "description": "library.properties is the metadata file for Arduino libraries. See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-metadata", "$comment": "For information on the Arduino library.properties format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", diff --git a/etc/schemas/arduino-library-properties-strict-schema.json b/etc/schemas/arduino-library-properties-strict-schema.json index b8b169a44..dc708edcc 100644 --- a/etc/schemas/arduino-library-properties-strict-schema.json +++ b/etc/schemas/arduino-library-properties-strict-schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/arduino-library-properties-strict-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-library-properties-strict-schema.json", "title": "Arduino library.properties strict JSON schema", "description": "library.properties is the metadata file for Arduino libraries. This schema defines the recommended format. See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-metadata", "$comment": "For information on the Arduino library.properties format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", diff --git a/etc/schemas/general-definitions-schema.json b/etc/schemas/general-definitions-schema.json index 3c192b1a5..513aff82c 100644 --- a/etc/schemas/general-definitions-schema.json +++ b/etc/schemas/general-definitions-schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/general-definitions-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/general-definitions-schema.json", "title": "Shared definitions", "description": "Definitions for use in schemas.", "type": "object", diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index fb847fb1d..34814bcd7 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -60,7 +60,7 @@ func (fi bindataFileInfo) Sys() interface{} { var _arduinoLibraryPropertiesDefinitionsSchemaJson = []byte(`{ "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/arduino-library-properties-definitions-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-library-properties-definitions-schema.json", "title": "Shared definitions for the Arduino library.properties schemas", "type": "object", "definitions": { @@ -942,7 +942,7 @@ func arduinoLibraryPropertiesDefinitionsSchemaJson() (*asset, error) { var _arduinoLibraryPropertiesPermissiveSchemaJson = []byte(`{ "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/arduino-library-properties-permissive-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-library-properties-permissive-schema.json", "title": "Arduino library.properties JSON permissive schema", "description": "library.properties is the metadata file for Arduino libraries. This schema defines the minimum requirements for this file. See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-metadata", "$comment": "For information on the Arduino library.properties format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", @@ -1022,7 +1022,7 @@ func arduinoLibraryPropertiesPermissiveSchemaJson() (*asset, error) { var _arduinoLibraryPropertiesSchemaJson = []byte(`{ "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/arduino-library-properties-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-library-properties-schema.json", "title": "Arduino library.properties JSON schema", "description": "library.properties is the metadata file for Arduino libraries. See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-metadata", "$comment": "For information on the Arduino library.properties format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", @@ -1102,7 +1102,7 @@ func arduinoLibraryPropertiesSchemaJson() (*asset, error) { var _arduinoLibraryPropertiesStrictSchemaJson = []byte(`{ "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/arduino-library-properties-strict-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-library-properties-strict-schema.json", "title": "Arduino library.properties strict JSON schema", "description": "library.properties is the metadata file for Arduino libraries. This schema defines the recommended format. See: https://arduino.github.io/arduino-cli/latest/library-specification/#library-metadata", "$comment": "For information on the Arduino library.properties format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", @@ -1182,7 +1182,7 @@ func arduinoLibraryPropertiesStrictSchemaJson() (*asset, error) { var _generalDefinitionsSchemaJson = []byte(`{ "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schema/general-definitions-schema.json", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/general-definitions-schema.json", "title": "Shared definitions", "description": "Definitions for use in schemas.", "type": "object", From e688ba0191b04f13bc7deee2f3b7ddfc686c6789 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 3 Jan 2021 18:17:02 -0800 Subject: [PATCH 4/6] Add JSON schema for boards.txt This schema defines the required data structure of the boards.txt configuration file of Arduino boards platforms. --- ...arduino-boards-txt-definitions-schema.json | 1031 +++++++++++++++ .../arduino-boards-txt-permissive-schema.json | 18 + etc/schemas/arduino-boards-txt-schema.json | 18 + .../arduino-boards-txt-strict-schema.json | 18 + .../project/platform/boardstxt/boardstxt.go | 70 +- .../platform/boardstxt/boardstxt_test.go | 94 ++ .../boardstxt/boardstxtschema_test.go | 345 +++++ .../boardstxt/testdata/valid/boards.txt | 6 + internal/project/projectdata/platform.go | 33 + internal/project/projectdata/platform_test.go | 2 +- .../ruleconfiguration/ruleconfiguration.go | 368 ++++++ internal/rule/rulefunction/platform.go | 499 +++++++ internal/rule/rulefunction/platform_test.go | 276 ++++ .../boards.txt | 23 + .../boards.txt | 21 + .../boards.txt | 23 + .../boards.txt | 21 + .../boards.txt | 25 + .../boards.txt | 26 + .../boards.txt | 26 + .../boards.txt | 26 + .../boardID-name-LT-boards.txt/boards.txt | 23 + .../boards.txt | 21 + .../boards.txt | 29 + .../boards.txt | 26 + .../boards.txt | 26 + .../boards.txt | 23 + .../boards.txt | 21 + .../boards.txt | 23 + .../boards.txt | 21 + .../boards.txt | 23 + .../boards.txt | 21 + .../boards.txt | 26 + .../boards.txt | 26 + .../boards.txt | 29 + .../menu-menuID-LT-boards.txt/boards.txt | 11 + .../menu-menuID-valid-boards.txt/boards.txt | 10 + .../platforms/no-boards-boards.txt/boards.txt | 0 .../platforms/no-menus-boards.txt/boards.txt | 7 + .../platforms/valid-boards.txt/boards.txt | 19 +- internal/rule/schema/schemadata/bindata.go | 1165 +++++++++++++++++ 41 files changed, 4515 insertions(+), 4 deletions(-) create mode 100644 etc/schemas/arduino-boards-txt-definitions-schema.json create mode 100644 etc/schemas/arduino-boards-txt-permissive-schema.json create mode 100644 etc/schemas/arduino-boards-txt-schema.json create mode 100644 etc/schemas/arduino-boards-txt-strict-schema.json create mode 100644 internal/project/platform/boardstxt/boardstxt_test.go create mode 100644 internal/project/platform/boardstxt/boardstxtschema_test.go create mode 100644 internal/project/platform/boardstxt/testdata/valid/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-build-board-LT-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-build-board-missing-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-build-core-LT-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-build-core-missing-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-compiler-x-extra_flags-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-debug-tool-LT-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-hide-invalid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-menu-menuID-LT-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-name-LT-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-name-missing-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-pid-n-invalid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-serial-disableDTR-invalid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-serial-disableRTS-invalid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_data_size-invalid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_data_size-missing-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_size-invalid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_size-missing-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-LT-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-missing-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-upload-use_1200bps_touch-invalid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-upload-wait_for_upload_port-invalid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/boardID-vid-n-invalid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/menu-menuID-LT-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/menu-menuID-valid-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/no-boards-boards.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/no-menus-boards.txt/boards.txt diff --git a/etc/schemas/arduino-boards-txt-definitions-schema.json b/etc/schemas/arduino-boards-txt-definitions-schema.json new file mode 100644 index 000000000..875736d8a --- /dev/null +++ b/etc/schemas/arduino-boards-txt-definitions-schema.json @@ -0,0 +1,1031 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-boards-txt-definitions-schema.json", + "title": "Shared definitions for the Arduino boards.txt schemas", + "definitions": { + "propertiesObjects": { + "menu": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menu/base/object" + }, + { + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/propertiesObjects/menuMenuID/permissive/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menu/base/object" + }, + { + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/propertiesObjects/menuMenuID/specification/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menu/base/object" + }, + { + "patternProperties": { + ".+": { + "$ref": "#/definitions/propertiesObjects/menuMenuID/permissive/object" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "menuMenuID": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menuMenuID/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menuMenuID/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menuMenuID/base/object" + } + ] + } + } + }, + "boardID": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardIDName/permissive/object" + }, + "build.board": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/permissive/object" + }, + "build.core": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/permissive/object" + }, + "debug.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/permissive/object" + }, + "hide": { + "$ref": "#/definitions/propertiesObjects/boardIDHide/permissive/object" + }, + "serial.disableDTR": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/permissive/object" + }, + "serial.disableRTS": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/permissive/object" + }, + "upload.maximum_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/permissive/object" + }, + "upload.maximum_data_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/permissive/object" + }, + "upload.protocol": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" + }, + "upload.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" + }, + "upload.use_1200bps_touch": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" + }, + "upload.wait_for_upload_port": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/permissive/object" + } + } + }, + { + "patternProperties": { + "menu\\..+\\..+$": { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/permissive/object" + }, + "^[vp]id\\.[0-9]+$": { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" + } + } + }, + { + "$ref": "#/definitions/propertyNamesObjects/permissive/object" + }, + { + "$ref": "#/definitions/requiredObjects/boardID/permissive/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardIDName/specification/object" + }, + "build.board": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/specification/object" + }, + "build.core": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/specification/object" + }, + "debug.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/specification/object" + }, + "hide": { + "$ref": "#/definitions/propertiesObjects/boardIDHide/specification/object" + }, + "serial.disableDTR": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/specification/object" + }, + "serial.disableRTS": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/specification/object" + }, + "upload.maximum_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/specification/object" + }, + "upload.maximum_data_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/specification/object" + }, + "upload.protocol": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" + }, + "upload.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" + }, + "upload.use_1200bps_touch": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" + }, + "upload.wait_for_upload_port": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/specification/object" + } + } + }, + { + "patternProperties": { + "menu\\..+\\..+$": { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/specification/object" + }, + "^[vp]id\\.[0-9]+$": { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" + } + } + }, + { + "$ref": "#/definitions/propertyNamesObjects/specification/object" + }, + { + "$ref": "#/definitions/requiredObjects/boardID/specification/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardIDName/strict/object" + }, + "build.board": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/strict/object" + }, + "build.core": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/strict/object" + }, + "debug.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/strict/object" + }, + "hide": { + "$ref": "#/definitions/propertiesObjects/boardIDHide/strict/object" + }, + "serial.disableDTR": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/strict/object" + }, + "serial.disableRTS": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/strict/object" + }, + "upload.maximum_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/strict/object" + }, + "upload.maximum_data_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/strict/object" + }, + "upload.protocol": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" + }, + "upload.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" + }, + "upload.use_1200bps_touch": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" + }, + "upload.wait_for_upload_port": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/strict/object" + } + } + }, + { + "patternProperties": { + "menu\\..+\\..+$": { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/strict/object" + }, + "^[vp]id\\.[0-9]+$": { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object" + } + } + }, + { + "$ref": "#/definitions/propertyNamesObjects/strict/object" + }, + { + "$ref": "#/definitions/requiredObjects/boardID/strict/object" + } + ] + } + } + }, + "boardIDName": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDName/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDName/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDName/base/object" + } + ] + } + } + }, + "boardIDBuildBoard": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + } + }, + "boardIDBuildCore": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + } + }, + "boardIDDebugTool": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" + } + ] + } + } + }, + "boardIDHide": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDHide/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDHide/base/object" + }, + { + "enum": [""] + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDHide/specification/object" + } + ] + } + } + }, + "boardIDMenuMenuIDOptionID": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/base/object" + } + ] + } + } + }, + "boardIDSerialDisableDTR": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/base/object" + } + ] + } + } + }, + "boardIDSerialDisableRTS": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/base/object" + } + ] + } + } + }, + "boardIDUploadMaximumSize": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "pattern": "^[0-9]+$" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/base/object" + } + ] + } + } + }, + "boardIDUploadMaximumDataSize": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "pattern": "^[0-9]+$" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/base/object" + } + ] + } + } + }, + "boardIDUploadProtocol": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/base/object" + } + ] + } + } + }, + "boardIDUploadTool": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/base/object" + } + ] + } + } + }, + "boardIDUploadUse1200bpsTouch": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/base/object" + } + ] + } + } + }, + "boardIDUploadWaitForUploadPort": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/base/object" + } + ] + } + } + }, + "boardIDXidN": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "pattern": "^0[xX][0-9a-fA-F]{4}$" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/base/object" + } + ] + } + } + } + }, + "propertyNamesObjects": { + "base": { + "object": {} + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertyNamesObjects/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertyNamesObjects/base/object" + } + ] + } + }, + "strict": { + "definitions": { + "userExtraFlagsProperties": { + "propertyNames": { + "not": { + "pattern": "^compiler\\.((c)|(c\\.elf)|(S)|(cpp)|(ar)|(objcopy.eep)|(elf2hex))\\.extra_flags$" + } + } + } + }, + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertyNamesObjects/base/object" + }, + { + "$ref": "#/definitions/propertyNamesObjects/strict/definitions/userExtraFlagsProperties" + } + ] + } + } + }, + "requiredObjects": { + "boardID": { + "base": { + "object": { + "allOf": [ + { + "required": ["name", "build.core", "upload.tool"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/boardID/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/boardID/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/boardID/base/object" + }, + { + "required": ["build.board", "upload.maximum_size", "upload.maximum_data_size"] + } + ] + } + } + } + }, + "enumObjects": { + "booleanString": { + "enum": ["true", "false"] + } + } + } +} diff --git a/etc/schemas/arduino-boards-txt-permissive-schema.json b/etc/schemas/arduino-boards-txt-permissive-schema.json new file mode 100644 index 000000000..e9709c457 --- /dev/null +++ b/etc/schemas/arduino-boards-txt-permissive-schema.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-boards-txt-permissive-schema.json", + "title": "Arduino boards.txt JSON permissive schema", + "description": "boards.txt contains the boards definitions of Arduino platforms. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + "$comment": "For information on the boards.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "properties": { + "menu": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/menu/permissive/object" + } + }, + "patternProperties": { + "^([^m].*|m([^e].*)?|me([^n].*)?|men([^u].*)?|menu.+)$": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/boardID/permissive/object" + } + } +} diff --git a/etc/schemas/arduino-boards-txt-schema.json b/etc/schemas/arduino-boards-txt-schema.json new file mode 100644 index 000000000..74fca4f29 --- /dev/null +++ b/etc/schemas/arduino-boards-txt-schema.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-boards-txt-schema.json", + "title": "Arduino boards.txt JSON schema", + "description": "boards.txt contains the boards definitions of Arduino platforms. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + "$comment": "For information on the boards.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "properties": { + "menu": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/menu/specification/object" + } + }, + "patternProperties": { + "^([^m].*|m([^e].*)?|me([^n].*)?|men([^u].*)?|menu.+)$": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/boardID/specification/object" + } + } +} diff --git a/etc/schemas/arduino-boards-txt-strict-schema.json b/etc/schemas/arduino-boards-txt-strict-schema.json new file mode 100644 index 000000000..4603c4e1f --- /dev/null +++ b/etc/schemas/arduino-boards-txt-strict-schema.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-boards-txt-strict-schema.json", + "title": "Arduino boards.txt JSON strict schema", + "description": "boards.txt contains the boards definitions of Arduino platforms. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + "$comment": "For information on the boards.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "properties": { + "menu": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/menu/strict/object" + } + }, + "patternProperties": { + "^([^m].*|m([^e].*)?|me([^n].*)?|men([^u].*)?|menu.+)$": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/boardID/strict/object" + } + } +} diff --git a/internal/project/platform/boardstxt/boardstxt.go b/internal/project/platform/boardstxt/boardstxt.go index e340c67b1..39b6d8a8e 100644 --- a/internal/project/platform/boardstxt/boardstxt.go +++ b/internal/project/platform/boardstxt/boardstxt.go @@ -20,11 +20,79 @@ See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boards package boardstxt import ( + "github.com/arduino/arduino-lint/internal/rule/schema" + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" + "github.com/arduino/arduino-lint/internal/rule/schema/schemadata" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" ) // Properties parses the boards.txt from the given path and returns the data. func Properties(platformPath *paths.Path) (*properties.Map, error) { - return properties.SafeLoadFromPath(platformPath.Join("boards.txt")) + return properties.LoadFromPath(platformPath.Join("boards.txt")) +} + +var schemaObject = make(map[compliancelevel.Type]schema.Schema) + +// Validate validates boards.txt data against the JSON schema and returns a map of the result for each compliance level. +func Validate(boardsTxt *properties.Map) map[compliancelevel.Type]schema.ValidationResult { + referencedSchemaFilenames := []string{ + "general-definitions-schema.json", + "arduino-boards-txt-definitions-schema.json", + } + + var validationResults = make(map[compliancelevel.Type]schema.ValidationResult) + + if schemaObject[compliancelevel.Permissive].Compiled == nil { // Only compile the schemas once. + schemaObject[compliancelevel.Permissive] = schema.Compile("arduino-boards-txt-permissive-schema.json", referencedSchemaFilenames, schemadata.Asset) + schemaObject[compliancelevel.Specification] = schema.Compile("arduino-boards-txt-schema.json", referencedSchemaFilenames, schemadata.Asset) + schemaObject[compliancelevel.Strict] = schema.Compile("arduino-boards-txt-strict-schema.json", referencedSchemaFilenames, schemadata.Asset) + } + + /* + Convert the boards.txt data from the native properties.Map type to the interface type required by the schema + validation package. + Even though boards.txt has a multi-level nested data structure, the format has the odd characteristic of allowing a + key to be both an object and a string simultaneously, which is not compatible with Golang maps or JSON. So the data + structure used is a map of the first level keys (necessary to accommodate the board IDs) to the full remainder of + the keys (rather than recursing through the key levels individually), to string values. + */ + boardsTxtInterface := make(map[string]interface{}) + keys := boardsTxt.FirstLevelKeys() + for _, key := range keys { + subtreeMap := boardsTxt.SubTree(key).AsMap() + // This level also must be converted to map[string]interface{}. + subtreeInterface := make(map[string]interface{}) + for subtreeKey, subtreeValue := range subtreeMap { + subtreeInterface[subtreeKey] = subtreeValue + } + boardsTxtInterface[key] = subtreeInterface + } + + validationResults[compliancelevel.Permissive] = schema.Validate(boardsTxtInterface, schemaObject[compliancelevel.Permissive]) + validationResults[compliancelevel.Specification] = schema.Validate(boardsTxtInterface, schemaObject[compliancelevel.Specification]) + validationResults[compliancelevel.Strict] = schema.Validate(boardsTxtInterface, schemaObject[compliancelevel.Strict]) + + return validationResults +} + +// MenuIDs returns the list of menu IDs from the given boards.txt properties. +func MenuIDs(boardsTxt *properties.Map) []string { + // Each menu must have a property defining its title with the format `menu.MENU_ID=MENU_TITLE`. + return boardsTxt.SubTree("menu").FirstLevelKeys() +} + +// BoardIDs returns the list of board IDs from the given boards.txt properties. +func BoardIDs(boardsTxt *properties.Map) []string { + boardIDs := boardsTxt.FirstLevelKeys() + boardIDCount := 0 + for _, boardID := range boardIDs { + if boardID != "menu" { + // This element is a board ID, retain it in the section of the array that will be returned. + boardIDs[boardIDCount] = boardID + boardIDCount++ + } + } + + return boardIDs[:boardIDCount] } diff --git a/internal/project/platform/boardstxt/boardstxt_test.go b/internal/project/platform/boardstxt/boardstxt_test.go new file mode 100644 index 000000000..b7be0850b --- /dev/null +++ b/internal/project/platform/boardstxt/boardstxt_test.go @@ -0,0 +1,94 @@ +// This file is part of arduino-lint. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-lint. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package boardstxt + +import ( + "testing" + + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var testDataPath *paths.Path + +var validBoardsTxtMap map[string]string + +func init() { + workingDirectory, err := paths.Getwd() + if err != nil { + panic(err) + } + testDataPath = workingDirectory.Join("testdata") + + validBoardsTxtMap = map[string]string{ + "uno.name": "Arduino Uno", + "uno.build.board": "AVR_UNO", + "uno.build.core": "arduino", + "uno.upload.tool": "avrdude", + "uno.upload.maximum_size": "123", + "uno.upload.maximum_data_size": "123", + } +} + +func TestProperties(t *testing.T) { + propertiesOutput, err := Properties(testDataPath.Join("valid")) + require.Nil(t, err) + + assert.True(t, properties.NewFromHashmap(validBoardsTxtMap).Equals(propertiesOutput)) +} + +func TestValidate(t *testing.T) { + boardsTxt := properties.NewFromHashmap(validBoardsTxtMap) + validationResult := Validate(boardsTxt) + + assert.Nil(t, validationResult[compliancelevel.Permissive].Result) + assert.Nil(t, validationResult[compliancelevel.Specification].Result) + assert.Nil(t, validationResult[compliancelevel.Strict].Result) + + boardsTxt.Remove("uno.name") // Remove required property. + validationResult = Validate(boardsTxt) + assert.NotNil(t, validationResult[compliancelevel.Permissive].Result) + assert.NotNil(t, validationResult[compliancelevel.Specification].Result) + assert.NotNil(t, validationResult[compliancelevel.Strict].Result) +} + +func TestMenuIDs(t *testing.T) { + boardsTxt := properties.NewFromHashmap(validBoardsTxtMap) + + assert.ElementsMatch(t, []string{}, MenuIDs(boardsTxt), "No menu IDs") + + boardsTxt.Set("menu", "noooo") + assert.ElementsMatch(t, []string{}, MenuIDs(boardsTxt), "Some silly defined a menu property without a subproperty") + + boardsTxt.Set("menu.foo", "asdf") + boardsTxt.Set("menu.bar", "zxcv") + boardsTxt.Set("baz.name", "qwer") + assert.ElementsMatch(t, []string{"foo", "bar"}, MenuIDs(boardsTxt), "Has menu IDs") +} + +func TestBoardIDs(t *testing.T) { + boardsTxt := properties.NewFromHashmap(validBoardsTxtMap) + + assert.ElementsMatch(t, []string{"uno"}, BoardIDs(boardsTxt)) + + boardsTxt.Set("menu.foo", "asdf") + boardsTxt.Set("menu.bar", "zxcv") + boardsTxt.Set("baz.name", "qwer") + assert.ElementsMatch(t, []string{"uno", "baz"}, BoardIDs(boardsTxt)) +} diff --git a/internal/project/platform/boardstxt/boardstxtschema_test.go b/internal/project/platform/boardstxt/boardstxtschema_test.go new file mode 100644 index 000000000..c400b7958 --- /dev/null +++ b/internal/project/platform/boardstxt/boardstxtschema_test.go @@ -0,0 +1,345 @@ +// This file is part of arduino-lint. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-lint. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +// This file contains tests for the boards.txt JSON schema. +package boardstxt_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/arduino/arduino-lint/internal/project/platform/boardstxt" + "github.com/arduino/arduino-lint/internal/rule/schema" + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" + "github.com/arduino/go-properties-orderedmap" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var validBoardsTxtRaw = []byte(` + menu.cpu=Processor + nano.name=Arduino Nano + nano.upload.tool=avrdude + nano.upload.protocol=arduino + nano.upload.maximum_size=123 + nano.upload.maximum_data_size=123 + nano.build.board=AVR_NANO + nano.build.core=arduino + nano.menu.cpu.atmega328=ATmega328P +`) + +func TestSchemaValid(t *testing.T) { + validBoardsTxtProperties, err := properties.LoadFromBytes(validBoardsTxtRaw) + require.Nil(t, err) + + validationResult := boardstxt.Validate(validBoardsTxtProperties) + + assert.Nil(t, validationResult[compliancelevel.Permissive].Result) + assert.Nil(t, validationResult[compliancelevel.Specification].Result) + assert.Nil(t, validationResult[compliancelevel.Strict].Result) +} + +func TestMinLength(t *testing.T) { + testTables := []struct { + propertyName string + validationErrorPropertyName string + minLength int + complianceLevel compliancelevel.Type + }{ + {"menu.foo", "menu/foo", 1, compliancelevel.Permissive}, + {"menu.foo", "menu/foo", 1, compliancelevel.Specification}, + {"menu.foo", "menu/foo", 1, compliancelevel.Strict}, + + {"foo.name", "foo/name", 1, compliancelevel.Permissive}, + {"foo.name", "foo/name", 1, compliancelevel.Specification}, + {"foo.name", "foo/name", 1, compliancelevel.Strict}, + + {"foo.build.board", "foo/build\\.board", 1, compliancelevel.Permissive}, + {"foo.build.board", "foo/build\\.board", 1, compliancelevel.Specification}, + {"foo.build.board", "foo/build\\.board", 1, compliancelevel.Strict}, + + {"foo.build.core", "foo/build\\.core", 1, compliancelevel.Permissive}, + {"foo.build.core", "foo/build\\.core", 1, compliancelevel.Specification}, + {"foo.build.core", "foo/build\\.core", 1, compliancelevel.Strict}, + + {"foo.debug.tool", "foo/debug\\.tool", 1, compliancelevel.Permissive}, + {"foo.debug.tool", "foo/debug\\.tool", 1, compliancelevel.Specification}, + {"foo.debug.tool", "foo/debug\\.tool", 1, compliancelevel.Strict}, + + {"foo.menu.bar.baz", "foo/menu\\.bar\\.baz", 1, compliancelevel.Permissive}, + {"foo.menu.bar.baz", "foo/menu\\.bar\\.baz", 1, compliancelevel.Specification}, + {"foo.menu.bar.baz", "foo/menu\\.bar\\.baz", 1, compliancelevel.Strict}, + + {"foo.upload.tool", "foo/upload\\.tool", 1, compliancelevel.Permissive}, + {"foo.upload.tool", "foo/upload\\.tool", 1, compliancelevel.Specification}, + {"foo.upload.tool", "foo/upload\\.tool", 1, compliancelevel.Strict}, + } + + // Test schema validation results with value length < minimum. + for _, testTable := range testTables { + boardsTxt, err := properties.LoadFromBytes(validBoardsTxtRaw) + require.Nil(t, err) + boardsTxt.Set(testTable.propertyName, strings.Repeat("a", testTable.minLength-1)) + + t.Run(fmt.Sprintf("%s less than minimum length of %d (%s)", testTable.propertyName, testTable.minLength, testTable.complianceLevel), func(t *testing.T) { + assert.True(t, schema.PropertyLessThanMinLength(testTable.propertyName, boardstxt.Validate(boardsTxt)[testTable.complianceLevel])) + }) + + // Test schema validation results with minimum value length. + boardsTxt, err = properties.LoadFromBytes(validBoardsTxtRaw) + require.Nil(t, err) + boardsTxt.Set(testTable.propertyName, strings.Repeat("a", testTable.minLength)) + + t.Run(fmt.Sprintf("%s at minimum length of %d (%s)", testTable.propertyName, testTable.minLength, testTable.complianceLevel), func(t *testing.T) { + assert.False(t, schema.PropertyLessThanMinLength(testTable.validationErrorPropertyName, boardstxt.Validate(boardsTxt)[testTable.complianceLevel])) + }) + } +} + +func TestEmpty(t *testing.T) { + // None of the root properties are required, so an empty boards.txt is valid. + validBoardsTxtProperties, err := properties.LoadFromBytes([]byte{}) + require.Nil(t, err) + + validationResult := boardstxt.Validate(validBoardsTxtProperties) + + assert.Nil(t, validationResult[compliancelevel.Permissive].Result) + assert.Nil(t, validationResult[compliancelevel.Specification].Result) + assert.Nil(t, validationResult[compliancelevel.Strict].Result) +} + +func TestRequired(t *testing.T) { + testTables := []struct { + propertyName string + validationErrorPropertyName string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + {"menu.cpu", "menu", compliancelevel.Permissive, assert.False}, + {"menu.cpu", "menu", compliancelevel.Specification, assert.False}, + {"menu.cpu", "menu", compliancelevel.Strict, assert.False}, + + {"nano.name", "nano/name", compliancelevel.Permissive, assert.True}, + {"nano.name", "nano/name", compliancelevel.Specification, assert.True}, + {"nano.name", "nano/name", compliancelevel.Strict, assert.True}, + + {"nano.upload.tool", "nano/upload\\.tool", compliancelevel.Permissive, assert.True}, + {"nano.upload.tool", "nano/upload\\.tool", compliancelevel.Specification, assert.True}, + {"nano.upload.tool", "nano/upload\\.tool", compliancelevel.Strict, assert.True}, + + {"nano.upload.maximum_size", "nano/upload\\.maximum_size", compliancelevel.Permissive, assert.False}, + {"nano.upload.maximum_size", "nano/upload\\.maximum_size", compliancelevel.Specification, assert.False}, + {"nano.upload.maximum_size", "nano/upload\\.maximum_size", compliancelevel.Strict, assert.True}, + + {"nano.upload.maximum_data_size", "nano/upload\\.maximum_data_size", compliancelevel.Permissive, assert.False}, + {"nano.upload.maximum_data_size", "nano/upload\\.maximum_data_size", compliancelevel.Specification, assert.False}, + {"nano.upload.maximum_data_size", "nano/upload\\.maximum_data_size", compliancelevel.Strict, assert.True}, + + {"nano.upload.protocol", "nano/upload\\.protocol", compliancelevel.Permissive, assert.False}, + {"nano.upload.protocol", "nano/upload\\.protocol", compliancelevel.Specification, assert.False}, + {"nano.upload.protocol", "nano/upload\\.protocol", compliancelevel.Strict, assert.False}, + + {"nano.build.board", "nano/build\\.board", compliancelevel.Permissive, assert.False}, + {"nano.build.board", "nano/build\\.board", compliancelevel.Specification, assert.False}, + {"nano.build.board", "nano/build\\.board", compliancelevel.Strict, assert.True}, + + {"nano.build.core", "nano/build\\.core", compliancelevel.Permissive, assert.True}, + {"nano.build.core", "nano/build\\.core", compliancelevel.Specification, assert.True}, + {"nano.build.core", "nano/build\\.core", compliancelevel.Strict, assert.True}, + } + + for _, testTable := range testTables { + boardsTxt, err := properties.LoadFromBytes(validBoardsTxtRaw) + require.Nil(t, err) + boardsTxt.Remove(testTable.propertyName) + + validationResult := boardstxt.Validate(boardsTxt) + t.Run(fmt.Sprintf("%s (%s)", testTable.propertyName, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.RequiredPropertyMissing(testTable.validationErrorPropertyName, validationResult[testTable.complianceLevel])) + }) + } +} + +func TestEnum(t *testing.T) { + testTables := []struct { + propertyName string + validationErrorPropertyName string + propertyValue string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + {"nano.hide", "nano/hide", "true", compliancelevel.Permissive, assert.False}, + {"nano.hide", "nano/hide", "true", compliancelevel.Specification, assert.True}, + {"nano.hide", "nano/hide", "true", compliancelevel.Strict, assert.True}, + {"nano.hide", "nano/hide", "false", compliancelevel.Permissive, assert.False}, + {"nano.hide", "nano/hide", "false", compliancelevel.Specification, assert.True}, + {"nano.hide", "nano/hide", "false", compliancelevel.Strict, assert.True}, + {"nano.hide", "nano/hide", "", compliancelevel.Permissive, assert.False}, + {"nano.hide", "nano/hide", "", compliancelevel.Specification, assert.False}, + {"nano.hide", "nano/hide", "", compliancelevel.Strict, assert.False}, + + {"nano.serial.disableDTR", "nano/serial\\.disableDTR", "true", compliancelevel.Permissive, assert.False}, + {"nano.serial.disableDTR", "nano/serial\\.disableDTR", "true", compliancelevel.Specification, assert.False}, + {"nano.serial.disableDTR", "nano/serial\\.disableDTR", "true", compliancelevel.Strict, assert.False}, + {"nano.serial.disableDTR", "nano/serial\\.disableDTR", "false", compliancelevel.Permissive, assert.False}, + {"nano.serial.disableDTR", "nano/serial\\.disableDTR", "false", compliancelevel.Specification, assert.False}, + {"nano.serial.disableDTR", "nano/serial\\.disableDTR", "false", compliancelevel.Strict, assert.False}, + {"nano.serial.disableDTR", "nano/serial\\.disableDTR", "foo", compliancelevel.Permissive, assert.True}, + {"nano.serial.disableDTR", "nano/serial\\.disableDTR", "foo", compliancelevel.Specification, assert.True}, + {"nano.serial.disableDTR", "nano/serial\\.disableDTR", "foo", compliancelevel.Strict, assert.True}, + + {"nano.serial.disableRTS", "nano/serial\\.disableRTS", "true", compliancelevel.Permissive, assert.False}, + {"nano.serial.disableRTS", "nano/serial\\.disableRTS", "true", compliancelevel.Specification, assert.False}, + {"nano.serial.disableRTS", "nano/serial\\.disableRTS", "true", compliancelevel.Strict, assert.False}, + {"nano.serial.disableRTS", "nano/serial\\.disableRTS", "false", compliancelevel.Permissive, assert.False}, + {"nano.serial.disableRTS", "nano/serial\\.disableRTS", "false", compliancelevel.Specification, assert.False}, + {"nano.serial.disableRTS", "nano/serial\\.disableRTS", "false", compliancelevel.Strict, assert.False}, + {"nano.serial.disableRTS", "nano/serial\\.disableRTS", "foo", compliancelevel.Permissive, assert.True}, + {"nano.serial.disableRTS", "nano/serial\\.disableRTS", "foo", compliancelevel.Specification, assert.True}, + {"nano.serial.disableRTS", "nano/serial\\.disableRTS", "foo", compliancelevel.Strict, assert.True}, + + {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "true", compliancelevel.Permissive, assert.False}, + {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "true", compliancelevel.Specification, assert.False}, + {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "true", compliancelevel.Strict, assert.False}, + {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "false", compliancelevel.Permissive, assert.False}, + {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "false", compliancelevel.Specification, assert.False}, + {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "false", compliancelevel.Strict, assert.False}, + {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "foo", compliancelevel.Permissive, assert.True}, + {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "foo", compliancelevel.Specification, assert.True}, + {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "foo", compliancelevel.Strict, assert.True}, + + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "true", compliancelevel.Permissive, assert.False}, + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "true", compliancelevel.Specification, assert.False}, + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "true", compliancelevel.Strict, assert.False}, + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "false", compliancelevel.Permissive, assert.False}, + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "false", compliancelevel.Specification, assert.False}, + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "false", compliancelevel.Strict, assert.False}, + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "foo", compliancelevel.Permissive, assert.True}, + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "foo", compliancelevel.Specification, assert.True}, + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "foo", compliancelevel.Strict, assert.True}, + } + + for _, testTable := range testTables { + boardsTxt, err := properties.LoadFromBytes(validBoardsTxtRaw) + require.Nil(t, err) + boardsTxt.Set(testTable.propertyName, testTable.propertyValue) + + validationResult := boardstxt.Validate(boardsTxt) + + t.Run(fmt.Sprintf("%s: %s (%s)", testTable.propertyName, testTable.propertyValue, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.PropertyEnumMismatch(testTable.validationErrorPropertyName, validationResult[testTable.complianceLevel])) + }) + } +} + +func TestPattern(t *testing.T) { + testTables := []struct { + propertyName string + validationErrorPropertyName string + propertyValue string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + {"nano.upload.maximum_size", "nano/upload\\.maximum_size", "123", compliancelevel.Permissive, assert.False}, + {"nano.upload.maximum_size", "nano/upload\\.maximum_size", "123", compliancelevel.Specification, assert.False}, + {"nano.upload.maximum_size", "nano/upload\\.maximum_size", "123", compliancelevel.Strict, assert.False}, + {"nano.upload.maximum_size", "nano/upload\\.maximum_size", "foo", compliancelevel.Permissive, assert.True}, + {"nano.upload.maximum_size", "nano/upload\\.maximum_size", "foo", compliancelevel.Specification, assert.True}, + {"nano.upload.maximum_size", "nano/upload\\.maximum_size", "foo", compliancelevel.Strict, assert.True}, + + {"nano.upload.maximum_data_size", "nano/upload\\.maximum_data_size", "123", compliancelevel.Permissive, assert.False}, + {"nano.upload.maximum_data_size", "nano/upload\\.maximum_data_size", "123", compliancelevel.Specification, assert.False}, + {"nano.upload.maximum_data_size", "nano/upload\\.maximum_data_size", "123", compliancelevel.Strict, assert.False}, + {"nano.upload.maximum_data_size", "nano/upload\\.maximum_data_size", "foo", compliancelevel.Permissive, assert.True}, + {"nano.upload.maximum_data_size", "nano/upload\\.maximum_data_size", "foo", compliancelevel.Specification, assert.True}, + {"nano.upload.maximum_data_size", "nano/upload\\.maximum_data_size", "foo", compliancelevel.Strict, assert.True}, + + {"nano.vid.0", "nano/vid\\.0", "0xABCD", compliancelevel.Permissive, assert.False}, + {"nano.vid.0", "nano/vid\\.0", "0xABCD", compliancelevel.Specification, assert.False}, + {"nano.vid.0", "nano/vid\\.0", "0xABCD", compliancelevel.Strict, assert.False}, + {"nano.vid.0", "nano/vid\\.0", "foo", compliancelevel.Permissive, assert.True}, + {"nano.vid.0", "nano/vid\\.0", "foo", compliancelevel.Specification, assert.True}, + {"nano.vid.0", "nano/vid\\.0", "foo", compliancelevel.Strict, assert.True}, + + {"nano.pid.0", "nano/pid\\.0", "0xABCD", compliancelevel.Permissive, assert.False}, + {"nano.pid.0", "nano/pid\\.0", "0xABCD", compliancelevel.Specification, assert.False}, + {"nano.pid.0", "nano/pid\\.0", "0xABCD", compliancelevel.Strict, assert.False}, + {"nano.pid.0", "nano/pid\\.0", "foo", compliancelevel.Permissive, assert.True}, + {"nano.pid.0", "nano/pid\\.0", "foo", compliancelevel.Specification, assert.True}, + {"nano.pid.0", "nano/pid\\.0", "foo", compliancelevel.Strict, assert.True}, + } + + for _, testTable := range testTables { + boardsTxt, err := properties.LoadFromBytes(validBoardsTxtRaw) + require.Nil(t, err) + boardsTxt.Set(testTable.propertyName, testTable.propertyValue) + + validationResult := boardstxt.Validate(boardsTxt) + + t.Run(fmt.Sprintf("%s: %s (%s)", testTable.propertyName, testTable.propertyValue, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.PropertyPatternMismatch(testTable.validationErrorPropertyName, validationResult[testTable.complianceLevel])) + }) + } +} + +func TestPropertyNames(t *testing.T) { + testTables := []struct { + propertyName string + validationErrorPropertyName string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + {"nano.compiler.c.extra_flags", "nano/compiler\\.c\\.extra_flags", compliancelevel.Permissive, assert.False}, + {"nano.compiler.c.extra_flags", "nano/compiler\\.c\\.extra_flags", compliancelevel.Specification, assert.False}, + {"nano.compiler.c.extra_flags", "nano/compiler\\.c\\.extra_flags", compliancelevel.Strict, assert.True}, + + {"nano.compiler.c.elf.extra_flags", "nano/compiler\\.c\\.elf\\.extra_flags", compliancelevel.Permissive, assert.False}, + {"nano.compiler.c.elf.extra_flags", "nano/compiler\\.c\\.elf\\.extra_flags", compliancelevel.Specification, assert.False}, + {"nano.compiler.c.elf.extra_flags", "nano/compiler\\.c\\.elf\\.extra_flags", compliancelevel.Strict, assert.True}, + + {"nano.compiler.S.extra_flags", "nano/compiler\\.S\\.extra_flags", compliancelevel.Permissive, assert.False}, + {"nano.compiler.S.extra_flags", "nano/compiler\\.S\\.extra_flags", compliancelevel.Specification, assert.False}, + {"nano.compiler.S.extra_flags", "nano/compiler\\.S\\.extra_flags", compliancelevel.Strict, assert.True}, + + {"nano.compiler.cpp.extra_flags", "nano/compiler\\.cpp\\.extra_flags", compliancelevel.Permissive, assert.False}, + {"nano.compiler.cpp.extra_flags", "nano/compiler\\.cpp\\.extra_flags", compliancelevel.Specification, assert.False}, + {"nano.compiler.cpp.extra_flags", "nano/compiler\\.cpp\\.extra_flags", compliancelevel.Strict, assert.True}, + + {"nano.compiler.ar.extra_flags", "nano/compiler\\.ar\\.extra_flags", compliancelevel.Permissive, assert.False}, + {"nano.compiler.ar.extra_flags", "nano/compiler\\.ar\\.extra_flags", compliancelevel.Specification, assert.False}, + {"nano.compiler.ar.extra_flags", "nano/compiler\\.ar\\.extra_flags", compliancelevel.Strict, assert.True}, + + {"nano.compiler.objcopy.eep.extra_flags", "nano/compiler\\.objcopy\\.eep\\.extra_flags", compliancelevel.Permissive, assert.False}, + {"nano.compiler.objcopy.eep.extra_flags", "nano/compiler\\.objcopy\\.eep\\.extra_flags", compliancelevel.Specification, assert.False}, + {"nano.compiler.objcopy.eep.extra_flags", "nano/compiler\\.objcopy\\.eep\\.extra_flags", compliancelevel.Strict, assert.True}, + + {"nano.compiler.elf2hex.extra_flags", "nano/compiler\\.elf2hex\\.extra_flags", compliancelevel.Permissive, assert.False}, + {"nano.compiler.elf2hex.extra_flags", "nano/compiler\\.elf2hex\\.extra_flags", compliancelevel.Specification, assert.False}, + {"nano.compiler.elf2hex.extra_flags", "nano/compiler\\.elf2hex\\.extra_flags", compliancelevel.Strict, assert.True}, + } + + for _, testTable := range testTables { + boardsTxt, err := properties.LoadFromBytes(validBoardsTxtRaw) + require.Nil(t, err) + boardsTxt.Set(testTable.propertyName, "foo") + + validationResult := boardstxt.Validate(boardsTxt) + + t.Run(fmt.Sprintf("%s (%s)", testTable.propertyName, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.ValidationErrorMatch("#/"+testTable.validationErrorPropertyName, "/userExtraFlagsProperties/", "", "", validationResult[testTable.complianceLevel])) + }) + } +} diff --git a/internal/project/platform/boardstxt/testdata/valid/boards.txt b/internal/project/platform/boardstxt/testdata/valid/boards.txt new file mode 100644 index 000000000..f2e1a11eb --- /dev/null +++ b/internal/project/platform/boardstxt/testdata/valid/boards.txt @@ -0,0 +1,6 @@ +uno.name=Arduino Uno +uno.build.board=AVR_UNO +uno.build.core=arduino +uno.upload.tool=avrdude +uno.upload.maximum_size=123 +uno.upload.maximum_data_size=123 diff --git a/internal/project/projectdata/platform.go b/internal/project/projectdata/platform.go index abe5b846b..104a14a58 100644 --- a/internal/project/projectdata/platform.go +++ b/internal/project/projectdata/platform.go @@ -18,12 +18,24 @@ package projectdata import ( "github.com/arduino/arduino-lint/internal/project" "github.com/arduino/arduino-lint/internal/project/platform/boardstxt" + "github.com/arduino/arduino-lint/internal/rule/schema" + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" "github.com/arduino/go-properties-orderedmap" + "github.com/sirupsen/logrus" ) // InitializeForPlatform gathers the platform rule data for the specified project. func InitializeForPlatform(project project.Type) { boardsTxt, boardsTxtLoadError = boardstxt.Properties(ProjectPath()) + if boardsTxtLoadError != nil { + logrus.Errorf("Error loading boards.txt from %s: %s", project.Path, boardsTxtLoadError) + boardsTxtSchemaValidationResult = nil + } else { + boardsTxtSchemaValidationResult = boardstxt.Validate(boardsTxt) + + boardsTxtMenuIds = boardstxt.MenuIDs(boardsTxt) + boardsTxtBoardIds = boardstxt.BoardIDs(boardsTxt) + } } var boardsTxt *properties.Map @@ -39,3 +51,24 @@ var boardsTxtLoadError error func BoardsTxtLoadError() error { return boardsTxtLoadError } + +var boardsTxtSchemaValidationResult map[compliancelevel.Type]schema.ValidationResult + +// BoardsTxtSchemaValidationResult returns the result of validating boards.txt against the JSON schema. +func BoardsTxtSchemaValidationResult() map[compliancelevel.Type]schema.ValidationResult { + return boardsTxtSchemaValidationResult +} + +var boardsTxtMenuIds []string + +// BoardsTxtMenuIds returns the list of menu IDs present in the platform's boards.txt. +func BoardsTxtMenuIds() []string { + return boardsTxtMenuIds +} + +var boardsTxtBoardIds []string + +// BoardsTxtMenuIds returns the list of board IDs present in the platform's boards.txt. +func BoardsTxtBoardIds() []string { + return boardsTxtBoardIds +} diff --git a/internal/project/projectdata/platform_test.go b/internal/project/projectdata/platform_test.go index f83baaec8..77520a4f4 100644 --- a/internal/project/projectdata/platform_test.go +++ b/internal/project/projectdata/platform_test.go @@ -43,7 +43,7 @@ func TestInitializeForPlatform(t *testing.T) { }{ {"Valid", "valid-boards.txt", assert.NotNil, assert.Nil}, {"Invalid", "invalid-boards.txt", assert.Nil, assert.NotNil}, - {"Missing", "missing-boards.txt", assert.NotNil, assert.Nil}, + {"Missing", "missing-boards.txt", assert.Nil, assert.NotNil}, } for _, testTable := range testTables { diff --git a/internal/rule/ruleconfiguration/ruleconfiguration.go b/internal/rule/ruleconfiguration/ruleconfiguration.go index 675dfe2d5..d15767e6d 100644 --- a/internal/rule/ruleconfiguration/ruleconfiguration.go +++ b/internal/rule/ruleconfiguration/ruleconfiguration.go @@ -1465,6 +1465,374 @@ var configurations = []Type{ ErrorModes: []rulemode.Type{rulemode.Default}, RuleFunction: rulefunction.BoardsTxtFormat, }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF003", + Brief: "missing boardID.name", + Description: "", + MessageTemplate: "Missing name property for board ID(s) {{.}}. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDNameMissing, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF004", + Brief: "boardID.name < min length", + Description: "", + MessageTemplate: "name value for board ID(s) {{.}} is less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDNameLTMinLength, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF005", + Brief: "missing build.board", + Description: "", + MessageTemplate: "Missing build.board property for board ID(s) {{.}}. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: []rulemode.Type{rulemode.Default}, + ErrorModes: []rulemode.Type{rulemode.Strict}, + RuleFunction: rulefunction.BoardsTxtBoardIDBuildBoardMissing, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF006", + Brief: "build.board < min length", + Description: "", + MessageTemplate: "build.board value for board ID(s) {{.}} is less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDBuildBoardLTMinLength, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF007", + Brief: "missing build.core", + Description: "", + MessageTemplate: "Missing build.core property for board ID(s) {{.}}. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#cores", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDBuildCoreMissing, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF008", + Brief: "build.core < min length", + Description: "", + MessageTemplate: "build.core value for board ID(s) {{.}} is less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#cores", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDBuildCoreLTMinLength, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF009", + Brief: "use of compiler.x.extra_flags", + Description: "", + MessageTemplate: "Board ID(s) {{.}} use compiler.x.extra_flags properties. These are intended to be left for use by the user.", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: []rulemode.Type{rulemode.Default}, + ErrorModes: []rulemode.Type{rulemode.Strict}, + RuleFunction: rulefunction.BoardsTxtUserExtraFlagsUsage, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF010", + Brief: "debug.tool < min length", + Description: "", + MessageTemplate: "debug.tool value for board ID(s) {{.}} is less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#sketch-debugging-configuration", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDDebugToolLTMinLength, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF011", + Brief: "non-empty hide value", + Description: "", + MessageTemplate: "hide value for board ID(s) {{.}} is not empty. The value of this property is ignored. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#hiding-boards", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: []rulemode.Type{rulemode.Default}, + ErrorModes: []rulemode.Type{rulemode.Strict}, + RuleFunction: rulefunction.BoardsTxtBoardIDHideInvalid, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF012", + Brief: "menu title < min length", + Description: "", + MessageTemplate: "title for menu ID(s) {{.}} are less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#custom-board-options", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtMenuMenuIDLTMinLength, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF013", + Brief: "board option < min length", + Description: "", + MessageTemplate: "Custom board option value for board ID(s) {{.}} is less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#custom-board-options", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDMenuMenuIDOptionIDLTMinLength, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF014", + Brief: "serial.disableDTR value invalid", + Description: "", + MessageTemplate: "serial.disableDTR value for board ID(s) {{.}} is invalid. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#serial-monitor-control-signal-configuration", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDSerialDisableDTRInvalid, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF015", + Brief: "serial.disableRTS value invalid", + Description: "", + MessageTemplate: "serial.disableRTS value for board ID(s) {{.}} is invalid. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#serial-monitor-control-signal-configuration", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDSerialDisableRTSInvalid, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF016", + Brief: "missing upload.tool", + Description: "", + MessageTemplate: "Missing upload.tool property for board ID(s) {{.}}. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#sketch-upload-configuration", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDUploadToolMissing, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF017", + Brief: "upload.tool < min length", + Description: "", + MessageTemplate: "upload.tool value for board ID(s) {{.}} is less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#sketch-upload-configuration", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDUploadToolLTMinLength, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF018", + Brief: "missing upload.maximum_size", + Description: "Although not required, this provides the build system and the user with very useful information so should be provided.", + MessageTemplate: "Missing upload.maximum_size property for board ID(s) {{.}}. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#recipes-to-compute-binary-sketch-size", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: []rulemode.Type{rulemode.Default}, + ErrorModes: []rulemode.Type{rulemode.Strict}, + RuleFunction: rulefunction.BoardsTxtBoardIDUploadMaximumSizeMissing, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF019", + Brief: "upload.maximum_size not a number", + Description: "", + MessageTemplate: "upload.maximum_size value for board ID(s) {{.}} is not a number. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#recipes-to-compute-binary-sketch-size", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDUploadMaximumSizeInvalid, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF020", + Brief: "missing upload.maximum_data_size", + Description: "Although not required, this provides the build system and the user with very useful information so should be provided.", + MessageTemplate: "Missing upload.maximum_data_size property for board ID(s) {{.}}. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#recipes-to-compute-binary-sketch-size", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: []rulemode.Type{rulemode.Default}, + ErrorModes: []rulemode.Type{rulemode.Strict}, + RuleFunction: rulefunction.BoardsTxtBoardIDUploadMaximumDataSizeMissing, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF021", + Brief: "upload.maximum_data_size not a number", + Description: "", + MessageTemplate: "upload.maximum_data_size value for board ID(s) {{.}} is not a number. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#recipes-to-compute-binary-sketch-size", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDUploadMaximumDataSizeInvalid, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF022", + Brief: "upload.use_1200bps_touch value invalid", + Description: "", + MessageTemplate: "upload.use_1200bps_touch value for board ID(s) {{.}} is invalid. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#1200-bps-bootloader-reset", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDUploadUse1200bpsTouchInvalid, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF023", + Brief: "upload.wait_for_upload_port value invalid", + Description: "", + MessageTemplate: "upload.wait_for_upload_port value for board ID(s) {{.}} is invalid. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#1200-bps-bootloader-reset", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDUploadWaitForUploadPortInvalid, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF024", + Brief: "vid.n value invalid format", + Description: "", + MessageTemplate: "vid.n value for board ID(s) {{.}} has invalid format. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#board-vidpid", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDVidNInvalid, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "boards.txt", + ID: "PF025", + Brief: "pid.n value invalid format", + Description: "", + MessageTemplate: "pid.n value for board ID(s) {{.}} has invalid format. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#board-vidpid", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.BoardsTxtBoardIDPidNInvalid, + }, { ProjectType: projecttype.Platform, SuperprojectType: projecttype.All, diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index d517dfd7a..8a06b608f 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -16,8 +16,12 @@ package rulefunction import ( + "strings" + "github.com/arduino/arduino-lint/internal/project/projectdata" "github.com/arduino/arduino-lint/internal/rule/ruleresult" + "github.com/arduino/arduino-lint/internal/rule/schema" + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" ) // The rule functions for platforms. @@ -49,3 +53,498 @@ func BoardsTxtFormat() (result ruleresult.Type, output string) { return ruleresult.Fail, projectdata.BoardsTxtLoadError().Error() } + +// BoardsTxtBoardIDNameMissing checks if any of the boards are missing name properties. +func BoardsTxtBoardIDNameMissing() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDMissingRequiredProperty("name", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDNameLTMinLength checks if any of the board names are less than the minimum length. +func BoardsTxtBoardIDNameLTMinLength() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueLTMinLength("name", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDBuildBoardMissing checks if any of the boards are missing build.board properties. +func BoardsTxtBoardIDBuildBoardMissing() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDMissingRequiredProperty("build\\.board", compliancelevel.Strict) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDBuildBoardLTMinLength checks if any of the board build.board values are less than the minimum length. +func BoardsTxtBoardIDBuildBoardLTMinLength() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueLTMinLength("build\\.board", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDBuildCoreMissing checks if any of the boards are missing build.core properties. +func BoardsTxtBoardIDBuildCoreMissing() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDMissingRequiredProperty("build\\.core", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDBuildCoreLTMinLength checks if any of the board build.core values are less than the minimum length. +func BoardsTxtBoardIDBuildCoreLTMinLength() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueLTMinLength("build\\.core", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtUserExtraFlagsUsage checks if the user's compiler.x.extra_flags properties are used in boards.txt. +func BoardsTxtUserExtraFlagsUsage() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := []string{} + for _, boardID := range projectdata.BoardsTxtBoardIds() { + if schema.ValidationErrorMatch("#/"+boardID, "/userExtraFlagsProperties/", "", "", projectdata.BoardsTxtSchemaValidationResult()[compliancelevel.Strict]) { + nonCompliantBoardIDs = append(nonCompliantBoardIDs, boardID) + } + } + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDDebugToolLTMinLength checks if any of the board debug.tool values are less than the minimum length. +func BoardsTxtBoardIDDebugToolLTMinLength() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueLTMinLength("debug\\.tool", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDHideInvalid checks if any of the board hide values are less than the minimum length. +func BoardsTxtBoardIDHideInvalid() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueEnumMismatch("hide", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtMenuMenuIDLTMinLength checks if any of the menu titles are less than the minimum length. +func BoardsTxtMenuMenuIDLTMinLength() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtMenuIds()) == 0 { + return ruleresult.Skip, "boards.txt has no menus" + } + + nonCompliantMenuIDs := []string{} + for _, menuID := range projectdata.BoardsTxtMenuIds() { + if schema.PropertyLessThanMinLength("menu/"+menuID, projectdata.BoardsTxtSchemaValidationResult()[compliancelevel.Specification]) { + nonCompliantMenuIDs = append(nonCompliantMenuIDs, menuID) + } + } + + if len(nonCompliantMenuIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantMenuIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDMenuMenuIDOptionIDLTMinLength checks if any of the board menu.MENU_ID.OPTION_ID values are less than the minimum length. +func BoardsTxtBoardIDMenuMenuIDOptionIDLTMinLength() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueLTMinLength("menu\\..+\\..+", compliancelevel.Strict) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDSerialDisableDTRInvalid checks if any of the board serial.disableDTR values are invalid. +func BoardsTxtBoardIDSerialDisableDTRInvalid() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueEnumMismatch("serial\\.disableDTR", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDSerialDisableRTSInvalid checks if any of the board serial.disableRTS values are invalid. +func BoardsTxtBoardIDSerialDisableRTSInvalid() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueEnumMismatch("serial\\.disableRTS", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDUploadToolMissing checks if any of the boards are missing upload.tool properties. +func BoardsTxtBoardIDUploadToolMissing() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDMissingRequiredProperty("upload\\.tool", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDUploadToolLTMinLength checks if any of the board upload.tool values are less than the minimum length. +func BoardsTxtBoardIDUploadToolLTMinLength() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueLTMinLength("upload\\.tool", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDUploadMaximumSizeMissing checks if any of the boards are missing upload.maximum_size properties. +func BoardsTxtBoardIDUploadMaximumSizeMissing() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDMissingRequiredProperty("upload\\.maximum_size", compliancelevel.Strict) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDUploadMaximumSizeInvalid checks if any of the board upload.maximum_size values have an invalid format. +func BoardsTxtBoardIDUploadMaximumSizeInvalid() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValuePatternMismatch("upload\\.maximum_size", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDUploadMaximumDataSizeMissing checks if any of the boards are missing upload.maximum_data_size properties. +func BoardsTxtBoardIDUploadMaximumDataSizeMissing() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDMissingRequiredProperty("upload\\.maximum_data_size", compliancelevel.Strict) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDUploadMaximumDataSizeInvalid checks if any of the board upload.maximum_data_size values have an invalid format. +func BoardsTxtBoardIDUploadMaximumDataSizeInvalid() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValuePatternMismatch("upload\\.maximum_data_size", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDUploadUse1200bpsTouchInvalid checks if any of the board upload.use_1200bps_touch values are invalid. +func BoardsTxtBoardIDUploadUse1200bpsTouchInvalid() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueEnumMismatch("upload\\.use_1200bps_touch", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDUploadWaitForUploadPortInvalid checks if any of the board upload.wait_for_upload_port values are invalid. +func BoardsTxtBoardIDUploadWaitForUploadPortInvalid() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValueEnumMismatch("upload\\.wait_for_upload_port", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDVidNInvalid checks if any of the board vid.n values have an invalid format. +func BoardsTxtBoardIDVidNInvalid() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValuePatternMismatch("vid\\.[0-9]+", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// BoardsTxtBoardIDPidNInvalid checks if any of the board pid.n values have an invalid format. +func BoardsTxtBoardIDPidNInvalid() (result ruleresult.Type, output string) { + if projectdata.BoardsTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load boards.txt" + } + + if len(projectdata.BoardsTxtBoardIds()) == 0 { + return ruleresult.Skip, "boards.txt has no boards" + } + + nonCompliantBoardIDs := boardIDValuePatternMismatch("pid\\.[0-9]+", compliancelevel.Specification) + + if len(nonCompliantBoardIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// boardIDMissingRequiredProperty returns the list of board IDs missing the given required property. +func boardIDMissingRequiredProperty(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + nonCompliantBoardIDs := []string{} + for _, boardID := range projectdata.BoardsTxtBoardIds() { + if schema.RequiredPropertyMissing(boardID+"/"+propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) { + nonCompliantBoardIDs = append(nonCompliantBoardIDs, boardID) + } + } + + return nonCompliantBoardIDs +} + +// boardIDValueLTMinLength returns the list of board IDs with value of the given property less than the minimum length. +func boardIDValueLTMinLength(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + nonCompliantBoardIDs := []string{} + for _, boardID := range projectdata.BoardsTxtBoardIds() { + if schema.PropertyLessThanMinLength(boardID+"/"+propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) { + nonCompliantBoardIDs = append(nonCompliantBoardIDs, boardID) + } + } + + return nonCompliantBoardIDs +} + +// boardIDValueEnumMismatch returns the list of board IDs with value of the given property not matching the JSON schema enum. +func boardIDValueEnumMismatch(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + nonCompliantBoardIDs := []string{} + for _, boardID := range projectdata.BoardsTxtBoardIds() { + if schema.PropertyEnumMismatch(boardID+"/"+propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) { + nonCompliantBoardIDs = append(nonCompliantBoardIDs, boardID) + } + } + + return nonCompliantBoardIDs +} + +// boardIDValueEnumMismatch returns the list of board IDs with value of the given property not matching the JSON schema pattern. +func boardIDValuePatternMismatch(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + nonCompliantBoardIDs := []string{} + for _, boardID := range projectdata.BoardsTxtBoardIds() { + if schema.PropertyPatternMismatch(boardID+"/"+propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) { + nonCompliantBoardIDs = append(nonCompliantBoardIDs, boardID) + } + } + + return nonCompliantBoardIDs +} diff --git a/internal/rule/rulefunction/platform_test.go b/internal/rule/rulefunction/platform_test.go index b06b75452..1b3bf5246 100644 --- a/internal/rule/rulefunction/platform_test.go +++ b/internal/rule/rulefunction/platform_test.go @@ -80,3 +80,279 @@ func TestBoardsTxtFormat(t *testing.T) { checkPlatformRuleFunction(BoardsTxtFormat, testTables, t) } + +func TestBoardsTxtBoardIDNameMissing(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property missing", "boardID-name-missing-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDNameMissing, testTables, t) +} + +func TestBoardsTxtBoardIDNameLTMinLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property LT min", "boardID-name-LT-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDNameLTMinLength, testTables, t) +} + +func TestBoardsTxtBoardIDBuildBoardMissing(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property missing", "boardID-build-board-missing-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDBuildBoardMissing, testTables, t) +} + +func TestBoardsTxtBoardIDBuildBoardLTMinLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property LT min", "boardID-build-board-LT-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDBuildBoardLTMinLength, testTables, t) +} + +func TestBoardsTxtBoardIDBuildCoreMissing(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property missing", "boardID-build-core-missing-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDBuildCoreMissing, testTables, t) +} + +func TestBoardsTxtBoardIDBuildCoreLTMinLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property LT min", "boardID-build-core-LT-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDBuildCoreLTMinLength, testTables, t) +} + +func TestBoardsTxtUserExtraFlagsUsage(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Use of user extra flags", "boardID-compiler-x-extra_flags-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtUserExtraFlagsUsage, testTables, t) +} + +func TestBoardsTxtBoardIDDebugToolLTMinLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property LT min", "boardID-debug-tool-LT-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDDebugToolLTMinLength, testTables, t) +} + +func TestBoardsTxtBoardIDHideInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property invalid", "boardID-hide-invalid-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDHideInvalid, testTables, t) +} + +func TestBoardsTxtMenuMenuIDLTMinLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No menus", "no-menus-boards.txt", ruleresult.Skip, ""}, + {"Menu title too short", "menu-menuID-LT-boards.txt", ruleresult.Fail, "foo, baz"}, + {"Menu title valid", "menu-menuID-valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtMenuMenuIDLTMinLength, testTables, t) +} + +func TestBoardsTxtBoardIDMenuMenuIDOptionIDLTMinLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property LT min", "boardID-menu-menuID-LT-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDMenuMenuIDOptionIDLTMinLength, testTables, t) +} + +func TestBoardsTxtBoardIDSerialDisableDTRInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property invalid", "boardID-serial-disableDTR-invalid-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDSerialDisableDTRInvalid, testTables, t) +} + +func TestBoardsTxtBoardIDSerialDisableRTSInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property invalid", "boardID-serial-disableRTS-invalid-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDSerialDisableRTSInvalid, testTables, t) +} + +func TestBoardsTxtBoardIDUploadToolMissing(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property missing", "boardID-upload-tool-missing-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDUploadToolMissing, testTables, t) +} + +func TestBoardsTxtBoardIDUploadToolLTMinLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property LT min", "boardID-upload-tool-LT-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDUploadToolLTMinLength, testTables, t) +} + +func TestBoardsTxtBoardIDUploadMaximumSizeMissing(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property missing", "boardID-upload-maximum_size-missing-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDUploadMaximumSizeMissing, testTables, t) +} + +func TestBoardsTxtBoardIDUploadMaximumSizeInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property invalid", "boardID-upload-maximum_size-invalid-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDUploadMaximumSizeInvalid, testTables, t) +} + +func TestBoardsTxtBoardIDUploadMaximumDataSizeMissing(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property missing", "boardID-upload-maximum_data_size-missing-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDUploadMaximumDataSizeMissing, testTables, t) +} + +func TestBoardsTxtBoardIDUploadMaximumDataSizeInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property invalid", "boardID-upload-maximum_data_size-invalid-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDUploadMaximumDataSizeInvalid, testTables, t) +} + +func TestBoardsTxtBoardIDUploadUse1200bpsTouchInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property invalid", "boardID-upload-use_1200bps_touch-invalid-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDUploadUse1200bpsTouchInvalid, testTables, t) +} + +func TestBoardsTxtBoardIDUploadWaitForUploadPortInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property invalid", "boardID-upload-wait_for_upload_port-invalid-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDUploadWaitForUploadPortInvalid, testTables, t) +} + +func TestBoardsTxtBoardIDVidNInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property invalid", "boardID-vid-n-invalid-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDVidNInvalid, testTables, t) +} + +func TestBoardsTxtBoardIDPidNInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, + {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, + {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, + {"Property invalid", "boardID-pid-n-invalid-boards.txt", ruleresult.Fail, "buno, funo"}, + {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(BoardsTxtBoardIDPidNInvalid, testTables, t) +} diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-build-board-LT-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-build-board-LT-boards.txt/boards.txt new file mode 100644 index 000000000..f0cd6bd1a --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-build-board-LT-boards.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board= +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board= +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-build-board-missing-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-build-board-missing-boards.txt/boards.txt new file mode 100644 index 000000000..9da39ca00 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-build-board-missing-boards.txt/boards.txt @@ -0,0 +1,21 @@ +buno.name=Buno +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-build-core-LT-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-build-core-LT-boards.txt/boards.txt new file mode 100644 index 000000000..02ec763f3 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-build-core-LT-boards.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core= +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core= +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-build-core-missing-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-build-core-missing-boards.txt/boards.txt new file mode 100644 index 000000000..96a3e3e7b --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-build-core-missing-boards.txt/boards.txt @@ -0,0 +1,21 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-compiler-x-extra_flags-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-compiler-x-extra_flags-boards.txt/boards.txt new file mode 100644 index 000000000..e3399fa0e --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-compiler-x-extra_flags-boards.txt/boards.txt @@ -0,0 +1,25 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.compiler.c.extra_flags=bar +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.compiler.objcopy.eep.extra_flags=foo +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-debug-tool-LT-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-debug-tool-LT-boards.txt/boards.txt new file mode 100644 index 000000000..3e97e0859 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-debug-tool-LT-boards.txt/boards.txt @@ -0,0 +1,26 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.debug.tool= +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.debug.tool=asdf +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.debug.tool= +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-hide-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-hide-invalid-boards.txt/boards.txt new file mode 100644 index 000000000..9a28652c6 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-hide-invalid-boards.txt/boards.txt @@ -0,0 +1,26 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.hide=true +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.hide= +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.hide=false +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-menu-menuID-LT-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-menu-menuID-LT-boards.txt/boards.txt new file mode 100644 index 000000000..113e609a1 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-menu-menuID-LT-boards.txt/boards.txt @@ -0,0 +1,26 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.menu.bar.asdf= +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.menu.baz.zxcv=qwer +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.menu.foo.tyui= +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-name-LT-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-name-LT-boards.txt/boards.txt new file mode 100644 index 000000000..6e26f4271 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-name-LT-boards.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name= +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name= +funo.build.board=BUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-name-missing-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-name-missing-boards.txt/boards.txt new file mode 100644 index 000000000..5287f67e0 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-name-missing-boards.txt/boards.txt @@ -0,0 +1,21 @@ +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-pid-n-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-pid-n-invalid-boards.txt/boards.txt new file mode 100644 index 000000000..5896bfb53 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-pid-n-invalid-boards.txt/boards.txt @@ -0,0 +1,29 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 +buno.vid.0=0xABCD +buno.pid.0=bar + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 +uno.vid.0=0xABCD +uno.pid.0=0x1234 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 +funo.vid.0=0xABCD +funo.pid.0=foo diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-serial-disableDTR-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-serial-disableDTR-invalid-boards.txt/boards.txt new file mode 100644 index 000000000..ec066e92b --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-serial-disableDTR-invalid-boards.txt/boards.txt @@ -0,0 +1,26 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.serial.disableDTR=bar +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.serial.disableDTR=true +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.serial.disableDTR=foo +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-serial-disableRTS-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-serial-disableRTS-invalid-boards.txt/boards.txt new file mode 100644 index 000000000..b6aca5d08 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-serial-disableRTS-invalid-boards.txt/boards.txt @@ -0,0 +1,26 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.serial.disableRTS=bar +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.serial.disableRTS=true +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.serial.disableRTS=foo +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_data_size-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_data_size-invalid-boards.txt/boards.txt new file mode 100644 index 000000000..8670c7cfe --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_data_size-invalid-boards.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=bar + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=fpp diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_data_size-missing-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_data_size-missing-boards.txt/boards.txt new file mode 100644 index 000000000..737e0e819 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_data_size-missing-boards.txt/boards.txt @@ -0,0 +1,21 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_size-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_size-invalid-boards.txt/boards.txt new file mode 100644 index 000000000..3dd41ae07 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_size-invalid-boards.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=bar +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=foo +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_size-missing-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_size-missing-boards.txt/boards.txt new file mode 100644 index 000000000..43cb6eb9b --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-maximum_size-missing-boards.txt/boards.txt @@ -0,0 +1,21 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-LT-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-LT-boards.txt/boards.txt new file mode 100644 index 000000000..432282836 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-LT-boards.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool= +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool= +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-missing-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-missing-boards.txt/boards.txt new file mode 100644 index 000000000..8ac786b2a --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-missing-boards.txt/boards.txt @@ -0,0 +1,21 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-use_1200bps_touch-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-use_1200bps_touch-invalid-boards.txt/boards.txt new file mode 100644 index 000000000..ab8afe811 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-use_1200bps_touch-invalid-boards.txt/boards.txt @@ -0,0 +1,26 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 +buno.upload.use_1200bps_touch=bar + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 +uno.upload.use_1200bps_touch=true + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 +funo.upload.use_1200bps_touch=foo diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-wait_for_upload_port-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-wait_for_upload_port-invalid-boards.txt/boards.txt new file mode 100644 index 000000000..fc75822b5 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-wait_for_upload_port-invalid-boards.txt/boards.txt @@ -0,0 +1,26 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 +buno.upload.wait_for_upload_port=bar + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 +uno.upload.wait_for_upload_port=false + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 +funo.upload.wait_for_upload_port=foo diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-vid-n-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-vid-n-invalid-boards.txt/boards.txt new file mode 100644 index 000000000..0bf439586 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/boardID-vid-n-invalid-boards.txt/boards.txt @@ -0,0 +1,29 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 +buno.vid.0=bar +buno.pid.0=0xABCD + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 +uno.vid.0=0xABCD +uno.pid.0=0xABCD + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 +funo.vid.0=foo +funo.pid.0=0xABCD diff --git a/internal/rule/rulefunction/testdata/platforms/menu-menuID-LT-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/menu-menuID-LT-boards.txt/boards.txt new file mode 100644 index 000000000..527da5c83 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/menu-menuID-LT-boards.txt/boards.txt @@ -0,0 +1,11 @@ +menu.foo= +menu.bar=asdf +menu.baz= + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/menu-menuID-valid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/menu-menuID-valid-boards.txt/boards.txt new file mode 100644 index 000000000..71216dbd7 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/menu-menuID-valid-boards.txt/boards.txt @@ -0,0 +1,10 @@ +menu.foo=asdf +menu.bar=zxcv + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/no-boards-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/no-boards-boards.txt/boards.txt new file mode 100644 index 000000000..e69de29bb diff --git a/internal/rule/rulefunction/testdata/platforms/no-menus-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/no-menus-boards.txt/boards.txt new file mode 100644 index 000000000..075d9bc34 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/no-menus-boards.txt/boards.txt @@ -0,0 +1,7 @@ +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/valid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/valid-boards.txt/boards.txt index 28dddb078..1866d3c44 100644 --- a/internal/rule/rulefunction/testdata/platforms/valid-boards.txt/boards.txt +++ b/internal/rule/rulefunction/testdata/platforms/valid-boards.txt/boards.txt @@ -1,8 +1,23 @@ -uno.name=Arduino Uno +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 +uno.name=Arduino Uno +uno.build.board=UNO uno.build.core=arduino uno.build.variant=standard - uno.upload.tool=avrdude uno.upload.maximum_size=32256 uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index 34814bcd7..f57629b99 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -1,5 +1,9 @@ // Package schemadata Code generated by go-bindata. (@generated) DO NOT EDIT. // sources: +// etc/schemas/arduino-boards-txt-definitions-schema.json +// etc/schemas/arduino-boards-txt-permissive-schema.json +// etc/schemas/arduino-boards-txt-schema.json +// etc/schemas/arduino-boards-txt-strict-schema.json // etc/schemas/arduino-library-properties-definitions-schema.json // etc/schemas/arduino-library-properties-permissive-schema.json // etc/schemas/arduino-library-properties-schema.json @@ -58,6 +62,1159 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } +var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-boards-txt-definitions-schema.json", + "title": "Shared definitions for the Arduino boards.txt schemas", + "definitions": { + "propertiesObjects": { + "menu": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menu/base/object" + }, + { + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/propertiesObjects/menuMenuID/permissive/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menu/base/object" + }, + { + "patternProperties": { + "^.+$": { + "$ref": "#/definitions/propertiesObjects/menuMenuID/specification/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menu/base/object" + }, + { + "patternProperties": { + ".+": { + "$ref": "#/definitions/propertiesObjects/menuMenuID/permissive/object" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "menuMenuID": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menuMenuID/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menuMenuID/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/menuMenuID/base/object" + } + ] + } + } + }, + "boardID": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardIDName/permissive/object" + }, + "build.board": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/permissive/object" + }, + "build.core": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/permissive/object" + }, + "debug.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/permissive/object" + }, + "hide": { + "$ref": "#/definitions/propertiesObjects/boardIDHide/permissive/object" + }, + "serial.disableDTR": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/permissive/object" + }, + "serial.disableRTS": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/permissive/object" + }, + "upload.maximum_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/permissive/object" + }, + "upload.maximum_data_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/permissive/object" + }, + "upload.protocol": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" + }, + "upload.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" + }, + "upload.use_1200bps_touch": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" + }, + "upload.wait_for_upload_port": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/permissive/object" + } + } + }, + { + "patternProperties": { + "menu\\..+\\..+$": { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/permissive/object" + }, + "^[vp]id\\.[0-9]+$": { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" + } + } + }, + { + "$ref": "#/definitions/propertyNamesObjects/permissive/object" + }, + { + "$ref": "#/definitions/requiredObjects/boardID/permissive/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardIDName/specification/object" + }, + "build.board": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/specification/object" + }, + "build.core": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/specification/object" + }, + "debug.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/specification/object" + }, + "hide": { + "$ref": "#/definitions/propertiesObjects/boardIDHide/specification/object" + }, + "serial.disableDTR": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/specification/object" + }, + "serial.disableRTS": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/specification/object" + }, + "upload.maximum_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/specification/object" + }, + "upload.maximum_data_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/specification/object" + }, + "upload.protocol": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" + }, + "upload.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" + }, + "upload.use_1200bps_touch": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" + }, + "upload.wait_for_upload_port": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/specification/object" + } + } + }, + { + "patternProperties": { + "menu\\..+\\..+$": { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/specification/object" + }, + "^[vp]id\\.[0-9]+$": { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" + } + } + }, + { + "$ref": "#/definitions/propertyNamesObjects/specification/object" + }, + { + "$ref": "#/definitions/requiredObjects/boardID/specification/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardIDName/strict/object" + }, + "build.board": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/strict/object" + }, + "build.core": { + "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/strict/object" + }, + "debug.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/strict/object" + }, + "hide": { + "$ref": "#/definitions/propertiesObjects/boardIDHide/strict/object" + }, + "serial.disableDTR": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/strict/object" + }, + "serial.disableRTS": { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/strict/object" + }, + "upload.maximum_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/strict/object" + }, + "upload.maximum_data_size": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/strict/object" + }, + "upload.protocol": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" + }, + "upload.tool": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" + }, + "upload.use_1200bps_touch": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" + }, + "upload.wait_for_upload_port": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/strict/object" + } + } + }, + { + "patternProperties": { + "menu\\..+\\..+$": { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/strict/object" + }, + "^[vp]id\\.[0-9]+$": { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object" + } + } + }, + { + "$ref": "#/definitions/propertyNamesObjects/strict/object" + }, + { + "$ref": "#/definitions/requiredObjects/boardID/strict/object" + } + ] + } + } + }, + "boardIDName": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDName/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDName/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDName/base/object" + } + ] + } + } + }, + "boardIDBuildBoard": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + } + }, + "boardIDBuildCore": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDBuildBoard/base/object" + } + ] + } + } + }, + "boardIDDebugTool": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" + } + ] + } + } + }, + "boardIDHide": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDHide/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDHide/base/object" + }, + { + "enum": [""] + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDHide/specification/object" + } + ] + } + } + }, + "boardIDMenuMenuIDOptionID": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDMenuMenuIDOptionID/base/object" + } + ] + } + } + }, + "boardIDSerialDisableDTR": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableDTR/base/object" + } + ] + } + } + }, + "boardIDSerialDisableRTS": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/base/object" + } + ] + } + } + }, + "boardIDUploadMaximumSize": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "pattern": "^[0-9]+$" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/base/object" + } + ] + } + } + }, + "boardIDUploadMaximumDataSize": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "pattern": "^[0-9]+$" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/base/object" + } + ] + } + } + }, + "boardIDUploadProtocol": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/base/object" + } + ] + } + } + }, + "boardIDUploadTool": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/base/object" + } + ] + } + } + }, + "boardIDUploadUse1200bpsTouch": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/base/object" + } + ] + } + } + }, + "boardIDUploadWaitForUploadPort": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/base/object" + } + ] + } + } + }, + "boardIDXidN": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "pattern": "^0[xX][0-9a-fA-F]{4}$" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boardIDXidN/base/object" + } + ] + } + } + } + }, + "propertyNamesObjects": { + "base": { + "object": {} + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertyNamesObjects/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertyNamesObjects/base/object" + } + ] + } + }, + "strict": { + "definitions": { + "userExtraFlagsProperties": { + "propertyNames": { + "not": { + "pattern": "^compiler\\.((c)|(c\\.elf)|(S)|(cpp)|(ar)|(objcopy.eep)|(elf2hex))\\.extra_flags$" + } + } + } + }, + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertyNamesObjects/base/object" + }, + { + "$ref": "#/definitions/propertyNamesObjects/strict/definitions/userExtraFlagsProperties" + } + ] + } + } + }, + "requiredObjects": { + "boardID": { + "base": { + "object": { + "allOf": [ + { + "required": ["name", "build.core", "upload.tool"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/boardID/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/boardID/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/boardID/base/object" + }, + { + "required": ["build.board", "upload.maximum_size", "upload.maximum_data_size"] + } + ] + } + } + } + }, + "enumObjects": { + "booleanString": { + "enum": ["true", "false"] + } + } + } +} +`) + +func arduinoBoardsTxtDefinitionsSchemaJsonBytes() ([]byte, error) { + return _arduinoBoardsTxtDefinitionsSchemaJson, nil +} + +func arduinoBoardsTxtDefinitionsSchemaJson() (*asset, error) { + bytes, err := arduinoBoardsTxtDefinitionsSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-boards-txt-definitions-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoBoardsTxtPermissiveSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-boards-txt-permissive-schema.json", + "title": "Arduino boards.txt JSON permissive schema", + "description": "boards.txt contains the boards definitions of Arduino platforms. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + "$comment": "For information on the boards.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "properties": { + "menu": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/menu/permissive/object" + } + }, + "patternProperties": { + "^([^m].*|m([^e].*)?|me([^n].*)?|men([^u].*)?|menu.+)$": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/boardID/permissive/object" + } + } +} +`) + +func arduinoBoardsTxtPermissiveSchemaJsonBytes() ([]byte, error) { + return _arduinoBoardsTxtPermissiveSchemaJson, nil +} + +func arduinoBoardsTxtPermissiveSchemaJson() (*asset, error) { + bytes, err := arduinoBoardsTxtPermissiveSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-boards-txt-permissive-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoBoardsTxtSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-boards-txt-schema.json", + "title": "Arduino boards.txt JSON schema", + "description": "boards.txt contains the boards definitions of Arduino platforms. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + "$comment": "For information on the boards.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "properties": { + "menu": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/menu/specification/object" + } + }, + "patternProperties": { + "^([^m].*|m([^e].*)?|me([^n].*)?|men([^u].*)?|menu.+)$": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/boardID/specification/object" + } + } +} +`) + +func arduinoBoardsTxtSchemaJsonBytes() ([]byte, error) { + return _arduinoBoardsTxtSchemaJson, nil +} + +func arduinoBoardsTxtSchemaJson() (*asset, error) { + bytes, err := arduinoBoardsTxtSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-boards-txt-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoBoardsTxtStrictSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-boards-txt-strict-schema.json", + "title": "Arduino boards.txt JSON strict schema", + "description": "boards.txt contains the boards definitions of Arduino platforms. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boardstxt", + "$comment": "For information on the boards.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "properties": { + "menu": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/menu/strict/object" + } + }, + "patternProperties": { + "^([^m].*|m([^e].*)?|me([^n].*)?|men([^u].*)?|menu.+)$": { + "$ref": "arduino-boards-txt-definitions-schema.json#/definitions/propertiesObjects/boardID/strict/object" + } + } +} +`) + +func arduinoBoardsTxtStrictSchemaJsonBytes() ([]byte, error) { + return _arduinoBoardsTxtStrictSchemaJson, nil +} + +func arduinoBoardsTxtStrictSchemaJson() (*asset, error) { + bytes, err := arduinoBoardsTxtStrictSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-boards-txt-strict-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + var _arduinoLibraryPropertiesDefinitionsSchemaJson = []byte(`{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-library-properties-definitions-schema.json", @@ -1268,6 +2425,10 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ + "arduino-boards-txt-definitions-schema.json": arduinoBoardsTxtDefinitionsSchemaJson, + "arduino-boards-txt-permissive-schema.json": arduinoBoardsTxtPermissiveSchemaJson, + "arduino-boards-txt-schema.json": arduinoBoardsTxtSchemaJson, + "arduino-boards-txt-strict-schema.json": arduinoBoardsTxtStrictSchemaJson, "arduino-library-properties-definitions-schema.json": arduinoLibraryPropertiesDefinitionsSchemaJson, "arduino-library-properties-permissive-schema.json": arduinoLibraryPropertiesPermissiveSchemaJson, "arduino-library-properties-schema.json": arduinoLibraryPropertiesSchemaJson, @@ -1316,6 +2477,10 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ + "arduino-boards-txt-definitions-schema.json": &bintree{arduinoBoardsTxtDefinitionsSchemaJson, map[string]*bintree{}}, + "arduino-boards-txt-permissive-schema.json": &bintree{arduinoBoardsTxtPermissiveSchemaJson, map[string]*bintree{}}, + "arduino-boards-txt-schema.json": &bintree{arduinoBoardsTxtSchemaJson, map[string]*bintree{}}, + "arduino-boards-txt-strict-schema.json": &bintree{arduinoBoardsTxtStrictSchemaJson, map[string]*bintree{}}, "arduino-library-properties-definitions-schema.json": &bintree{arduinoLibraryPropertiesDefinitionsSchemaJson, map[string]*bintree{}}, "arduino-library-properties-permissive-schema.json": &bintree{arduinoLibraryPropertiesPermissiveSchemaJson, map[string]*bintree{}}, "arduino-library-properties-schema.json": &bintree{arduinoLibraryPropertiesSchemaJson, map[string]*bintree{}}, From 648291eab4172c85b2360e97c0c4f914017224b7 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 3 Jan 2021 18:17:31 -0800 Subject: [PATCH 5/6] Add JSON schema for programmers.txt This schema defines the required data structure of the programmers.txt configuration file of Arduino boards platforms. --- ...no-programmers-txt-definitions-schema.json | 208 +++++++++++ ...ino-programmers-txt-permissive-schema.json | 13 + .../arduino-programmers-txt-schema.json | 13 + ...arduino-programmers-txt-strict-schema.json | 13 + internal/project/general/general.go | 44 +++ internal/project/general/general_test.go | 47 +++ .../project/platform/boardstxt/boardstxt.go | 22 +- .../platform/programmerstxt/programmerstxt.go | 65 ++++ .../programmerstxt/programmerstxt_test.go | 74 ++++ .../programmerstxtschema_test.go | 128 +++++++ .../testdata/valid/programmers.txt | 6 + internal/project/projectdata/platform.go | 48 +++ .../ruleconfiguration/ruleconfiguration.go | 80 +++++ internal/rule/rulefunction/platform.go | 197 +++++++++-- internal/rule/rulefunction/platform_test.go | 48 +++ .../invalid-programmers.txt/boards.txt | 7 + .../invalid-programmers.txt/programmers.txt | 1 + .../missing-programmers.txt/boards.txt | 7 + .../no-programmers-programmers.txt/boards.txt | 7 + .../programmers.txt | 0 .../boards.txt | 7 + .../programmers.txt | 12 + .../boards.txt | 7 + .../programmers.txt | 10 + .../boards.txt | 7 + .../programmers.txt | 12 + .../boards.txt | 7 + .../programmers.txt | 10 + .../valid-programmers.txt/boards.txt | 7 + .../valid-programmers.txt/programmers.txt | 12 + internal/rule/schema/schemadata/bindata.go | 327 ++++++++++++++++++ 31 files changed, 1401 insertions(+), 45 deletions(-) create mode 100644 etc/schemas/arduino-programmers-txt-definitions-schema.json create mode 100644 etc/schemas/arduino-programmers-txt-permissive-schema.json create mode 100644 etc/schemas/arduino-programmers-txt-schema.json create mode 100644 etc/schemas/arduino-programmers-txt-strict-schema.json create mode 100644 internal/project/general/general.go create mode 100644 internal/project/general/general_test.go create mode 100644 internal/project/platform/programmerstxt/programmerstxt.go create mode 100644 internal/project/platform/programmerstxt/programmerstxt_test.go create mode 100644 internal/project/platform/programmerstxt/programmerstxtschema_test.go create mode 100644 internal/project/platform/programmerstxt/testdata/valid/programmers.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/invalid-programmers.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/invalid-programmers.txt/programmers.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/missing-programmers.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/no-programmers-programmers.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/no-programmers-programmers.txt/programmers.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/programmerID-name-LT-programmers.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/programmerID-name-LT-programmers.txt/programmers.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/programmerID-name-missing-programmers.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/programmerID-name-missing-programmers.txt/programmers.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-LT-programmers.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-LT-programmers.txt/programmers.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-missing-programmers.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-missing-programmers.txt/programmers.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/valid-programmers.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/valid-programmers.txt/programmers.txt diff --git a/etc/schemas/arduino-programmers-txt-definitions-schema.json b/etc/schemas/arduino-programmers-txt-definitions-schema.json new file mode 100644 index 000000000..d59967dc0 --- /dev/null +++ b/etc/schemas/arduino-programmers-txt-definitions-schema.json @@ -0,0 +1,208 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-programmers-txt-definitions-schema.json", + "title": "Shared definitions for the Arduino programmers.txt schemas", + "definitions": { + "propertiesObjects": { + "programmerID": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/programmerIDName/permissive/object" + }, + "program.tool": { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/permissive/object" + } + } + }, + { + "$ref": "#/definitions/requiredObjects/programmerID/permissive/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/programmerIDName/specification/object" + }, + "program.tool": { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/specification/object" + } + } + }, + { + "$ref": "#/definitions/requiredObjects/programmerID/specification/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/programmerIDName/strict/object" + }, + "program.tool": { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/strict/object" + } + } + }, + { + "$ref": "#/definitions/requiredObjects/programmerID/strict/object" + } + ] + } + } + }, + "programmerIDName": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDName/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDName/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDName/base/object" + } + ] + } + } + }, + "programmerIDProgramTool": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/base/object" + } + ] + } + } + } + }, + "requiredObjects": { + "programmerID": { + "base": { + "object": { + "allOf": [ + { + "required": ["name", "program.tool"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/programmerID/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/programmerID/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/programmerID/base/object" + } + ] + } + } + } + } + } +} diff --git a/etc/schemas/arduino-programmers-txt-permissive-schema.json b/etc/schemas/arduino-programmers-txt-permissive-schema.json new file mode 100644 index 000000000..7a1cd4468 --- /dev/null +++ b/etc/schemas/arduino-programmers-txt-permissive-schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-programmers-txt-permissive-schema.json", + "title": "Arduino programmers.txt JSON permissive schema", + "description": "programmers.txt contains the definitions of Arduino hardware programmers. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + "$comment": "For information on the programmers.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "patternProperties": { + ".+": { + "$ref": "arduino-programmers-txt-definitions-schema.json#/definitions/propertiesObjects/programmerID/permissive/object" + } + } +} diff --git a/etc/schemas/arduino-programmers-txt-schema.json b/etc/schemas/arduino-programmers-txt-schema.json new file mode 100644 index 000000000..fd101f23d --- /dev/null +++ b/etc/schemas/arduino-programmers-txt-schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-programmers-txt-schema.json", + "title": "Arduino programmers.txt JSON schema", + "description": "programmers.txt contains the definitions of Arduino hardware programmers. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + "$comment": "For information on the programmers.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "patternProperties": { + ".+": { + "$ref": "arduino-programmers-txt-definitions-schema.json#/definitions/propertiesObjects/programmerID/specification/object" + } + } +} diff --git a/etc/schemas/arduino-programmers-txt-strict-schema.json b/etc/schemas/arduino-programmers-txt-strict-schema.json new file mode 100644 index 000000000..6f8c45daa --- /dev/null +++ b/etc/schemas/arduino-programmers-txt-strict-schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-programmers-txt-strict-schema.json", + "title": "Arduino programmers.txt JSON strict schema", + "description": "programmers.txt contains the definitions of Arduino hardware programmers. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + "$comment": "For information on the programmers.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "patternProperties": { + ".+": { + "$ref": "arduino-programmers-txt-definitions-schema.json#/definitions/propertiesObjects/programmerID/strict/object" + } + } +} diff --git a/internal/project/general/general.go b/internal/project/general/general.go new file mode 100644 index 000000000..7f730aefd --- /dev/null +++ b/internal/project/general/general.go @@ -0,0 +1,44 @@ +// This file is part of arduino-lint. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-lint. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +// Package general provides functions that apply to multiple project types. +package general + +import ( + "github.com/arduino/go-properties-orderedmap" +) + +/* +PropertiesToFirstLevelExpandedMap converts properties.Map data structures to map[string]interface that maps between . +Even though boards/properties.txt have a multi-level nested data structure, the format has the odd characteristic of +allowing a key to be both an object and a string simultaneously, which is not compatible with Golang maps or JSON. So +the data structure used is a map of the first level keys (necessary to accommodate the board/prograrmmer IDs) to the +full remainder of the keys (rather than recursing through each key level individually), to string values. +*/ +func PropertiesToFirstLevelExpandedMap(flatProperties *properties.Map) map[string]interface{} { + propertiesInterface := make(map[string]interface{}) + keys := flatProperties.FirstLevelKeys() + for _, key := range keys { + subtreeMap := flatProperties.SubTree(key).AsMap() + // This level also must be converted to map[string]interface{}. + subtreeInterface := make(map[string]interface{}) + for subtreeKey, subtreeValue := range subtreeMap { + subtreeInterface[subtreeKey] = subtreeValue + } + propertiesInterface[key] = subtreeInterface + } + + return propertiesInterface +} diff --git a/internal/project/general/general_test.go b/internal/project/general/general_test.go new file mode 100644 index 000000000..a8699c3e0 --- /dev/null +++ b/internal/project/general/general_test.go @@ -0,0 +1,47 @@ +// This file is part of arduino-lint. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-lint. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package general + +import ( + "reflect" + "testing" + + "github.com/arduino/go-properties-orderedmap" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPropertiesToFirstLevelExpandedMap(t *testing.T) { + rawProperties := []byte(` + foo.bar=asdf + foo.baz=zxcv + bar.bat.bam=123 + `) + propertiesInput, err := properties.LoadFromBytes(rawProperties) + require.Nil(t, err) + + expectedMapOutput := map[string]interface{}{ + "foo": map[string]interface{}{ + "bar": "asdf", + "baz": "zxcv", + }, + "bar": map[string]interface{}{ + "bat.bam": "123", + }, + } + + assert.True(t, reflect.DeepEqual(expectedMapOutput, PropertiesToFirstLevelExpandedMap(propertiesInput))) +} diff --git a/internal/project/platform/boardstxt/boardstxt.go b/internal/project/platform/boardstxt/boardstxt.go index 39b6d8a8e..860b8f60f 100644 --- a/internal/project/platform/boardstxt/boardstxt.go +++ b/internal/project/platform/boardstxt/boardstxt.go @@ -20,6 +20,7 @@ See: https://arduino.github.io/arduino-cli/latest/platform-specification/#boards package boardstxt import ( + "github.com/arduino/arduino-lint/internal/project/general" "github.com/arduino/arduino-lint/internal/rule/schema" "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" "github.com/arduino/arduino-lint/internal/rule/schema/schemadata" @@ -49,25 +50,8 @@ func Validate(boardsTxt *properties.Map) map[compliancelevel.Type]schema.Validat schemaObject[compliancelevel.Strict] = schema.Compile("arduino-boards-txt-strict-schema.json", referencedSchemaFilenames, schemadata.Asset) } - /* - Convert the boards.txt data from the native properties.Map type to the interface type required by the schema - validation package. - Even though boards.txt has a multi-level nested data structure, the format has the odd characteristic of allowing a - key to be both an object and a string simultaneously, which is not compatible with Golang maps or JSON. So the data - structure used is a map of the first level keys (necessary to accommodate the board IDs) to the full remainder of - the keys (rather than recursing through the key levels individually), to string values. - */ - boardsTxtInterface := make(map[string]interface{}) - keys := boardsTxt.FirstLevelKeys() - for _, key := range keys { - subtreeMap := boardsTxt.SubTree(key).AsMap() - // This level also must be converted to map[string]interface{}. - subtreeInterface := make(map[string]interface{}) - for subtreeKey, subtreeValue := range subtreeMap { - subtreeInterface[subtreeKey] = subtreeValue - } - boardsTxtInterface[key] = subtreeInterface - } + //Convert the boards.txt data from the native properties.Map type to the interface type required by the schema validation package. + boardsTxtInterface := general.PropertiesToFirstLevelExpandedMap(boardsTxt) validationResults[compliancelevel.Permissive] = schema.Validate(boardsTxtInterface, schemaObject[compliancelevel.Permissive]) validationResults[compliancelevel.Specification] = schema.Validate(boardsTxtInterface, schemaObject[compliancelevel.Specification]) diff --git a/internal/project/platform/programmerstxt/programmerstxt.go b/internal/project/platform/programmerstxt/programmerstxt.go new file mode 100644 index 000000000..a3ae6d905 --- /dev/null +++ b/internal/project/platform/programmerstxt/programmerstxt.go @@ -0,0 +1,65 @@ +// This file is part of arduino-lint. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-lint. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +/* +Package programmerstxt provides functions specific to linting the programmers.txt configuration files of Arduino programmers platforms. +See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt +*/ +package programmerstxt + +import ( + "github.com/arduino/arduino-lint/internal/project/general" + "github.com/arduino/arduino-lint/internal/rule/schema" + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" + "github.com/arduino/arduino-lint/internal/rule/schema/schemadata" + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" +) + +// Properties parses the programmers.txt from the given path and returns the data. +func Properties(platformPath *paths.Path) (*properties.Map, error) { + return properties.LoadFromPath(platformPath.Join("programmers.txt")) +} + +var schemaObject = make(map[compliancelevel.Type]schema.Schema) + +// Validate validates programmers.txt data against the JSON schema and returns the result. +func Validate(programmersTxt *properties.Map) map[compliancelevel.Type]schema.ValidationResult { + referencedSchemaFilenames := []string{ + "arduino-programmers-txt-definitions-schema.json", + } + + var validationResults = make(map[compliancelevel.Type]schema.ValidationResult) + + if schemaObject[compliancelevel.Permissive].Compiled == nil { // Only compile the schemas once. + schemaObject[compliancelevel.Permissive] = schema.Compile("arduino-programmers-txt-permissive-schema.json", referencedSchemaFilenames, schemadata.Asset) + schemaObject[compliancelevel.Specification] = schema.Compile("arduino-programmers-txt-schema.json", referencedSchemaFilenames, schemadata.Asset) + schemaObject[compliancelevel.Strict] = schema.Compile("arduino-programmers-txt-strict-schema.json", referencedSchemaFilenames, schemadata.Asset) + } + + //Convert the programmers.txt data from the native properties.Map type to the interface type required by the schema validation package. + programmersTxtInterface := general.PropertiesToFirstLevelExpandedMap(programmersTxt) + + validationResults[compliancelevel.Permissive] = schema.Validate(programmersTxtInterface, schemaObject[compliancelevel.Permissive]) + validationResults[compliancelevel.Specification] = schema.Validate(programmersTxtInterface, schemaObject[compliancelevel.Specification]) + validationResults[compliancelevel.Strict] = schema.Validate(programmersTxtInterface, schemaObject[compliancelevel.Strict]) + + return validationResults +} + +// ProgrammerIDs returns the list of programmer IDs from the given programmers.txt properties. +func ProgrammerIDs(programmersTxt *properties.Map) []string { + return programmersTxt.FirstLevelKeys() +} diff --git a/internal/project/platform/programmerstxt/programmerstxt_test.go b/internal/project/platform/programmerstxt/programmerstxt_test.go new file mode 100644 index 000000000..886ada27a --- /dev/null +++ b/internal/project/platform/programmerstxt/programmerstxt_test.go @@ -0,0 +1,74 @@ +// This file is part of arduino-lint. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-lint. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package programmerstxt + +import ( + "testing" + + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var testDataPath *paths.Path + +var validProgrammersTxtMap map[string]string + +func init() { + workingDirectory, err := paths.Getwd() + if err != nil { + panic(err) + } + testDataPath = workingDirectory.Join("testdata") + + validProgrammersTxtMap = map[string]string{ + "usbasp.name": "USBasp", + "usbasp.program.tool": "avrdude", + "usbasp.program.extra_params": "-Pusb", + "arduinoasisp.name": "Arduino as ISP", + "arduinoasisp.program.tool": "avrdude", + } +} + +func TestProperties(t *testing.T) { + propertiesOutput, err := Properties(testDataPath.Join("valid")) + require.Nil(t, err) + + assert.True(t, properties.NewFromHashmap(validProgrammersTxtMap).Equals(propertiesOutput)) +} + +func TestValidate(t *testing.T) { + programmersTxt := properties.NewFromHashmap(validProgrammersTxtMap) + validationResult := Validate(programmersTxt) + + assert.Nil(t, validationResult[compliancelevel.Permissive].Result) + assert.Nil(t, validationResult[compliancelevel.Specification].Result) + assert.Nil(t, validationResult[compliancelevel.Strict].Result) + + programmersTxt.Remove("usbasp.name") // Remove required property. + validationResult = Validate(programmersTxt) + assert.NotNil(t, validationResult[compliancelevel.Permissive].Result) + assert.NotNil(t, validationResult[compliancelevel.Specification].Result) + assert.NotNil(t, validationResult[compliancelevel.Strict].Result) +} + +func TestProgrammerIDs(t *testing.T) { + programmersTxt := properties.NewFromHashmap(validProgrammersTxtMap) + + assert.ElementsMatch(t, []string{"usbasp", "arduinoasisp"}, ProgrammerIDs(programmersTxt)) +} diff --git a/internal/project/platform/programmerstxt/programmerstxtschema_test.go b/internal/project/platform/programmerstxt/programmerstxtschema_test.go new file mode 100644 index 000000000..b989d3f49 --- /dev/null +++ b/internal/project/platform/programmerstxt/programmerstxtschema_test.go @@ -0,0 +1,128 @@ +// This file is part of arduino-lint. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-lint. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +// This file contains tests for the programmers.txt JSON schema. +package programmerstxt_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/arduino/arduino-lint/internal/project/platform/programmerstxt" + "github.com/arduino/arduino-lint/internal/rule/schema" + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" + "github.com/arduino/go-properties-orderedmap" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var validProgrammersTxtRaw = []byte(` + usbasp.name=USBasp + usbasp.protocol=usbasp + usbasp.program.tool=avrdude +`) + +func TestSchemaValid(t *testing.T) { + validProgrammersTxtProperties, err := properties.LoadFromBytes(validProgrammersTxtRaw) + require.Nil(t, err) + + validationResult := programmerstxt.Validate(validProgrammersTxtProperties) + + assert.Nil(t, validationResult[compliancelevel.Permissive].Result) + assert.Nil(t, validationResult[compliancelevel.Specification].Result) + assert.Nil(t, validationResult[compliancelevel.Strict].Result) +} + +func TestMinLength(t *testing.T) { + testTables := []struct { + propertyName string + validationErrorPropertyName string + minLength int + complianceLevel compliancelevel.Type + }{ + {"foo.name", "foo/name", 1, compliancelevel.Permissive}, + {"foo.name", "foo/name", 1, compliancelevel.Specification}, + {"foo.name", "foo/name", 1, compliancelevel.Strict}, + + {"foo.program.tool", "foo/program\\.tool", 1, compliancelevel.Permissive}, + {"foo.program.tool", "foo/program\\.tool", 1, compliancelevel.Specification}, + {"foo.program.tool", "foo/program\\.tool", 1, compliancelevel.Strict}, + } + + // Test schema validation results with value length < minimum. + for _, testTable := range testTables { + programmersTxt, err := properties.LoadFromBytes(validProgrammersTxtRaw) + require.Nil(t, err) + programmersTxt.Set(testTable.propertyName, strings.Repeat("a", testTable.minLength-1)) + + t.Run(fmt.Sprintf("%s less than minimum length of %d (%s)", testTable.propertyName, testTable.minLength, testTable.complianceLevel), func(t *testing.T) { + assert.True(t, schema.PropertyLessThanMinLength(testTable.propertyName, programmerstxt.Validate(programmersTxt)[testTable.complianceLevel])) + }) + + // Test schema validation results with minimum value length. + programmersTxt, err = properties.LoadFromBytes(validProgrammersTxtRaw) + require.Nil(t, err) + programmersTxt.Set(testTable.propertyName, strings.Repeat("a", testTable.minLength)) + + t.Run(fmt.Sprintf("%s at minimum length of %d (%s)", testTable.propertyName, testTable.minLength, testTable.complianceLevel), func(t *testing.T) { + assert.False(t, schema.PropertyLessThanMinLength(testTable.validationErrorPropertyName, programmerstxt.Validate(programmersTxt)[testTable.complianceLevel])) + }) + } +} + +func TestEmpty(t *testing.T) { + // None of the root properties are required, so an empty programmers.txt is valid. + programmersTxt, err := properties.LoadFromBytes([]byte{}) + require.Nil(t, err) + + validationResult := programmerstxt.Validate(programmersTxt) + + assert.Nil(t, validationResult[compliancelevel.Permissive].Result) + assert.Nil(t, validationResult[compliancelevel.Specification].Result) + assert.Nil(t, validationResult[compliancelevel.Strict].Result) +} + +func TestRequired(t *testing.T) { + testTables := []struct { + propertyName string + validationErrorPropertyName string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + {"usbasp.name", "usbasp/name", compliancelevel.Permissive, assert.True}, + {"usbasp.name", "usbasp/name", compliancelevel.Specification, assert.True}, + {"usbasp.name", "usbasp/name", compliancelevel.Strict, assert.True}, + + {"usbasp.program.tool", "usbasp/program\\.tool", compliancelevel.Permissive, assert.True}, + {"usbasp.program.tool", "usbasp/program\\.tool", compliancelevel.Specification, assert.True}, + {"usbasp.program.tool", "usbasp/program\\.tool", compliancelevel.Strict, assert.True}, + + {"usbasp.foo.bar", "usbasp/foo\\.bar", compliancelevel.Permissive, assert.False}, + {"usbasp.foo.bar", "usbasp/foo\\.bar", compliancelevel.Specification, assert.False}, + {"usbasp.foo.bar", "usbasp/foo\\.bar", compliancelevel.Strict, assert.False}, + } + + for _, testTable := range testTables { + programmersTxt, err := properties.LoadFromBytes(validProgrammersTxtRaw) + require.Nil(t, err) + programmersTxt.Remove(testTable.propertyName) + + validationResult := programmerstxt.Validate(programmersTxt) + t.Run(fmt.Sprintf("%s (%s)", testTable.propertyName, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.RequiredPropertyMissing(testTable.validationErrorPropertyName, validationResult[testTable.complianceLevel])) + }) + } +} diff --git a/internal/project/platform/programmerstxt/testdata/valid/programmers.txt b/internal/project/platform/programmerstxt/testdata/valid/programmers.txt new file mode 100644 index 000000000..482646020 --- /dev/null +++ b/internal/project/platform/programmerstxt/testdata/valid/programmers.txt @@ -0,0 +1,6 @@ +usbasp.name=USBasp +usbasp.program.tool=avrdude +usbasp.program.extra_params=-Pusb + +arduinoasisp.name=Arduino as ISP +arduinoasisp.program.tool=avrdude diff --git a/internal/project/projectdata/platform.go b/internal/project/projectdata/platform.go index 104a14a58..943df121d 100644 --- a/internal/project/projectdata/platform.go +++ b/internal/project/projectdata/platform.go @@ -18,6 +18,7 @@ package projectdata import ( "github.com/arduino/arduino-lint/internal/project" "github.com/arduino/arduino-lint/internal/project/platform/boardstxt" + "github.com/arduino/arduino-lint/internal/project/platform/programmerstxt" "github.com/arduino/arduino-lint/internal/rule/schema" "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" "github.com/arduino/go-properties-orderedmap" @@ -36,6 +37,18 @@ func InitializeForPlatform(project project.Type) { boardsTxtMenuIds = boardstxt.MenuIDs(boardsTxt) boardsTxtBoardIds = boardstxt.BoardIDs(boardsTxt) } + + programmersTxtExists = ProjectPath().Join("programmers.txt").Exist() + + programmersTxt, programmersTxtLoadError = programmerstxt.Properties(ProjectPath()) + if programmersTxtLoadError != nil { + logrus.Errorf("Error loading programmers.txt from %s: %s", project.Path, programmersTxtLoadError) + programmersTxtSchemaValidationResult = nil + } else { + programmersTxtSchemaValidationResult = programmerstxt.Validate(programmersTxt) + + programmersTxtProgrammerIds = programmerstxt.ProgrammerIDs(programmersTxt) + } } var boardsTxt *properties.Map @@ -72,3 +85,38 @@ var boardsTxtBoardIds []string func BoardsTxtBoardIds() []string { return boardsTxtBoardIds } + +var programmersTxtExists bool + +// ProgrammersTxtExists returns whether the platform contains a programmer.txt file. +func ProgrammersTxtExists() bool { + return programmersTxtExists +} + +var programmersTxt *properties.Map + +// ProgrammersTxt returns the data from the programmers.txt configuration file. +func ProgrammersTxt() *properties.Map { + return programmersTxt +} + +var programmersTxtLoadError error + +// ProgrammersTxtLoadError returns the error output from loading the programmers.txt configuration file. +func ProgrammersTxtLoadError() error { + return programmersTxtLoadError +} + +var programmersTxtSchemaValidationResult map[compliancelevel.Type]schema.ValidationResult + +// ProgrammersTxtSchemaValidationResult returns the result of validating programmers.txt against the JSON schema. +func ProgrammersTxtSchemaValidationResult() map[compliancelevel.Type]schema.ValidationResult { + return programmersTxtSchemaValidationResult +} + +var programmersTxtProgrammerIds []string + +// ProgrammersTxtProgrammerIds returns the list of board IDs present in the platform's programmers.txt. +func ProgrammersTxtProgrammerIds() []string { + return programmersTxtProgrammerIds +} diff --git a/internal/rule/ruleconfiguration/ruleconfiguration.go b/internal/rule/ruleconfiguration/ruleconfiguration.go index d15767e6d..538f7d281 100644 --- a/internal/rule/ruleconfiguration/ruleconfiguration.go +++ b/internal/rule/ruleconfiguration/ruleconfiguration.go @@ -1833,6 +1833,86 @@ var configurations = []Type{ ErrorModes: []rulemode.Type{rulemode.Default}, RuleFunction: rulefunction.BoardsTxtBoardIDPidNInvalid, }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "programmers.txt", + ID: "PF026", + Brief: "invalid programmers.txt", + Description: "", + MessageTemplate: "programmers.txt has an invalid format: {{.}}. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.ProgrammersTxtFormat, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "programmers.txt", + ID: "PF027", + Brief: "missing programmerID.name", + Description: "", + MessageTemplate: "Missing name property for programmer ID(s) {{.}}. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.ProgrammersTxtProgrammerIDNameMissing, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "programmers.txt", + ID: "PF028", + Brief: "programmerID.name < min length", + Description: "", + MessageTemplate: "name value for programmer ID(s) {{.}} is less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.ProgrammersTxtProgrammerIDNameLTMinLength, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "programmers.txt", + ID: "PF029", + Brief: "missing programmerID.program.tool", + Description: "", + MessageTemplate: "Missing program.tool property for programmer ID(s) {{.}}. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.ProgrammersTxtProgrammerIDProgramToolMissing, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "programmers.txt", + ID: "PF030", + Brief: "programmerID.program.tool < min length", + Description: "", + MessageTemplate: "program.tool value for programmer ID(s) {{.}} is less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.ProgrammersTxtProgrammerIDProgramToolLTMinLength, + }, { ProjectType: projecttype.Platform, SuperprojectType: projecttype.All, diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index 8a06b608f..fab9ee356 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -501,50 +501,195 @@ func BoardsTxtBoardIDPidNInvalid() (result ruleresult.Type, output string) { return ruleresult.Pass, "" } +// ProgrammersTxtFormat checks for invalid programmers.txt format. +func ProgrammersTxtFormat() (result ruleresult.Type, output string) { + if !projectdata.ProgrammersTxtExists() { + return ruleresult.Skip, "Platform has no programmers.txt" + } + + if projectdata.ProgrammersTxtLoadError() == nil { + return ruleresult.Pass, "" + } + + return ruleresult.Fail, projectdata.ProgrammersTxtLoadError().Error() +} + +// ProgrammersTxtProgrammerIDNameMissing checks if any of the programmers are missing name properties. +func ProgrammersTxtProgrammerIDNameMissing() (result ruleresult.Type, output string) { + if !projectdata.ProgrammersTxtExists() { + return ruleresult.Skip, "Platform has no programmers.txt" + } + + if projectdata.ProgrammersTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load programmers.txt" + } + + if len(projectdata.ProgrammersTxtProgrammerIds()) == 0 { + return ruleresult.Skip, "programmers.txt has no programmers" + } + + nonCompliantProgrammerIDs := programmerIDMissingRequiredProperty("name", compliancelevel.Specification) + + if len(nonCompliantProgrammerIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantProgrammerIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// ProgrammersTxtProgrammerIDNameLTMinLength checks if any of the programmer names are less than the minimum length. +func ProgrammersTxtProgrammerIDNameLTMinLength() (result ruleresult.Type, output string) { + if !projectdata.ProgrammersTxtExists() { + return ruleresult.Skip, "Platform has no programmers.txt" + } + + if projectdata.ProgrammersTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load programmers.txt" + } + + if len(projectdata.ProgrammersTxtProgrammerIds()) == 0 { + return ruleresult.Skip, "programmers.txt has no programmers" + } + + nonCompliantProgrammerIDs := programmerIDValueLTMinLength("name", compliancelevel.Specification) + + if len(nonCompliantProgrammerIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantProgrammerIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// ProgrammersTxtProgrammerIDProgramToolMissing checks if any of the programmers are missing program.tool properties. +func ProgrammersTxtProgrammerIDProgramToolMissing() (result ruleresult.Type, output string) { + if !projectdata.ProgrammersTxtExists() { + return ruleresult.Skip, "Platform has no programmers.txt" + } + + if projectdata.ProgrammersTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load programmers.txt" + } + + if len(projectdata.ProgrammersTxtProgrammerIds()) == 0 { + return ruleresult.Skip, "programmers.txt has no programmers" + } + + nonCompliantProgrammerIDs := programmerIDMissingRequiredProperty("program\\.tool", compliancelevel.Specification) + + if len(nonCompliantProgrammerIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantProgrammerIDs, ", ") + } + + return ruleresult.Pass, "" +} + +// ProgrammersTxtProgrammerIDProgramToolLTMinLength checks if any of the programmer program.tool properties are less than the minimum length. +func ProgrammersTxtProgrammerIDProgramToolLTMinLength() (result ruleresult.Type, output string) { + if !projectdata.ProgrammersTxtExists() { + return ruleresult.Skip, "Platform has no programmers.txt" + } + + if projectdata.ProgrammersTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load programmers.txt" + } + + if len(projectdata.ProgrammersTxtProgrammerIds()) == 0 { + return ruleresult.Skip, "programmers.txt has no programmers" + } + + nonCompliantProgrammerIDs := programmerIDValueLTMinLength("program\\.tool", compliancelevel.Specification) + + if len(nonCompliantProgrammerIDs) > 0 { + return ruleresult.Fail, strings.Join(nonCompliantProgrammerIDs, ", ") + } + + return ruleresult.Pass, "" +} + // boardIDMissingRequiredProperty returns the list of board IDs missing the given required property. func boardIDMissingRequiredProperty(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { - nonCompliantBoardIDs := []string{} - for _, boardID := range projectdata.BoardsTxtBoardIds() { - if schema.RequiredPropertyMissing(boardID+"/"+propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) { - nonCompliantBoardIDs = append(nonCompliantBoardIDs, boardID) + return iDMissingRequiredProperty(projectdata.BoardsTxtBoardIds(), propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) +} + +// boardIDValueLTMinLength returns the list of board IDs with value of the given property less than the minimum length. +func boardIDValueLTMinLength(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + return iDValueLTMinLength(projectdata.BoardsTxtBoardIds(), propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) +} + +// boardIDValueEnumMismatch returns the list of board IDs with value of the given property not matching the JSON schema enum. +func boardIDValueEnumMismatch(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + return iDValueEnumMismatch(projectdata.BoardsTxtBoardIds(), propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) +} + +// boardIDValueEnumMismatch returns the list of board IDs with value of the given property not matching the JSON schema pattern. +func boardIDValuePatternMismatch(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + return iDValuePatternMismatch(projectdata.BoardsTxtBoardIds(), propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) +} + +// programmerIDMissingRequiredProperty returns the list of programmer IDs missing the given required property. +func programmerIDMissingRequiredProperty(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + return iDMissingRequiredProperty(projectdata.ProgrammersTxtProgrammerIds(), propertyNameQuery, projectdata.ProgrammersTxtSchemaValidationResult()[complianceLevel]) +} + +// programmerIDValueLTMinLength returns the list of programmer IDs with value of the given property less than the minimum length. +func programmerIDValueLTMinLength(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + return iDValueLTMinLength(projectdata.ProgrammersTxtProgrammerIds(), propertyNameQuery, projectdata.ProgrammersTxtSchemaValidationResult()[complianceLevel]) +} + +// programmerIDValueEnumMismatch returns the list of programmer IDs with value of the given property not matching the JSON schema enum. +func programmerIDValueEnumMismatch(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + return iDValueEnumMismatch(projectdata.ProgrammersTxtProgrammerIds(), propertyNameQuery, projectdata.ProgrammersTxtSchemaValidationResult()[complianceLevel]) +} + +// programmerIDValueEnumMismatch returns the list of programmer IDs with value of the given property not matching the JSON schema pattern. +func programmerIDValuePatternMismatch(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { + return iDValuePatternMismatch(projectdata.ProgrammersTxtProgrammerIds(), propertyNameQuery, projectdata.ProgrammersTxtSchemaValidationResult()[complianceLevel]) +} + +// iDMissingRequiredProperty returns the list of first level keys missing the given required property. +func iDMissingRequiredProperty(iDs []string, propertyNameQuery string, validationResult schema.ValidationResult) []string { + nonCompliantIDs := []string{} + for _, iD := range iDs { + if schema.RequiredPropertyMissing(iD+"/"+propertyNameQuery, validationResult) { + nonCompliantIDs = append(nonCompliantIDs, iD) } } - return nonCompliantBoardIDs + return nonCompliantIDs } -// boardIDValueLTMinLength returns the list of board IDs with value of the given property less than the minimum length. -func boardIDValueLTMinLength(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { - nonCompliantBoardIDs := []string{} - for _, boardID := range projectdata.BoardsTxtBoardIds() { - if schema.PropertyLessThanMinLength(boardID+"/"+propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) { - nonCompliantBoardIDs = append(nonCompliantBoardIDs, boardID) +// iDValueLTMinLength returns the list of first level keys with value of the given property less than the minimum length. +func iDValueLTMinLength(iDs []string, propertyNameQuery string, validationResult schema.ValidationResult) []string { + nonCompliantIDs := []string{} + for _, iD := range iDs { + if schema.PropertyLessThanMinLength(iD+"/"+propertyNameQuery, validationResult) { + nonCompliantIDs = append(nonCompliantIDs, iD) } } - return nonCompliantBoardIDs + return nonCompliantIDs } -// boardIDValueEnumMismatch returns the list of board IDs with value of the given property not matching the JSON schema enum. -func boardIDValueEnumMismatch(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { - nonCompliantBoardIDs := []string{} - for _, boardID := range projectdata.BoardsTxtBoardIds() { - if schema.PropertyEnumMismatch(boardID+"/"+propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) { - nonCompliantBoardIDs = append(nonCompliantBoardIDs, boardID) +// iDValueEnumMismatch returns the list of first level keys with value of the given property not matching the JSON schema enum. +func iDValueEnumMismatch(iDs []string, propertyNameQuery string, validationResult schema.ValidationResult) []string { + nonCompliantIDs := []string{} + for _, iD := range iDs { + if schema.PropertyEnumMismatch(iD+"/"+propertyNameQuery, validationResult) { + nonCompliantIDs = append(nonCompliantIDs, iD) } } - return nonCompliantBoardIDs + return nonCompliantIDs } -// boardIDValueEnumMismatch returns the list of board IDs with value of the given property not matching the JSON schema pattern. -func boardIDValuePatternMismatch(propertyNameQuery string, complianceLevel compliancelevel.Type) []string { - nonCompliantBoardIDs := []string{} - for _, boardID := range projectdata.BoardsTxtBoardIds() { - if schema.PropertyPatternMismatch(boardID+"/"+propertyNameQuery, projectdata.BoardsTxtSchemaValidationResult()[complianceLevel]) { - nonCompliantBoardIDs = append(nonCompliantBoardIDs, boardID) +// iDValueEnumMismatch returns the list of first level keys with value of the given property not matching the JSON schema pattern. +func iDValuePatternMismatch(iDs []string, propertyNameQuery string, validationResult schema.ValidationResult) []string { + nonCompliantIDs := []string{} + for _, iD := range iDs { + if schema.PropertyPatternMismatch(iD+"/"+propertyNameQuery, validationResult) { + nonCompliantIDs = append(nonCompliantIDs, iD) } } - return nonCompliantBoardIDs + return nonCompliantIDs } diff --git a/internal/rule/rulefunction/platform_test.go b/internal/rule/rulefunction/platform_test.go index 1b3bf5246..45b2cd828 100644 --- a/internal/rule/rulefunction/platform_test.go +++ b/internal/rule/rulefunction/platform_test.go @@ -356,3 +356,51 @@ func TestBoardsTxtBoardIDPidNInvalid(t *testing.T) { checkPlatformRuleFunction(BoardsTxtBoardIDPidNInvalid, testTables, t) } + +func TestProgrammersTxtProgrammerIDNameMissing(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-programmers.txt", ruleresult.Skip, ""}, + {"Invalid", "invalid-programmers.txt", ruleresult.NotRun, ""}, + {"No programmers", "no-programmers-programmers.txt", ruleresult.Skip, ""}, + {"Property missing", "programmerID-name-missing-programmers.txt", ruleresult.Fail, "foo, bar"}, + {"Valid", "valid-programmers.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(ProgrammersTxtProgrammerIDNameMissing, testTables, t) +} + +func TestProgrammersTxtProgrammerIDNameLTMinLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-programmers.txt", ruleresult.Skip, ""}, + {"Invalid", "invalid-programmers.txt", ruleresult.NotRun, ""}, + {"No programmers", "no-programmers-programmers.txt", ruleresult.Skip, ""}, + {"Property LT min", "programmerID-name-LT-programmers.txt", ruleresult.Fail, "foo, bar"}, + {"Valid", "valid-programmers.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(ProgrammersTxtProgrammerIDNameLTMinLength, testTables, t) +} + +func TestProgrammersTxtProgrammerIDProgramToolMissing(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-programmers.txt", ruleresult.Skip, ""}, + {"Invalid", "invalid-programmers.txt", ruleresult.NotRun, ""}, + {"No programmers", "no-programmers-programmers.txt", ruleresult.Skip, ""}, + {"Property missing", "programmerID-program-tool-missing-programmers.txt", ruleresult.Fail, "foo, bar"}, + {"Valid", "valid-programmers.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(ProgrammersTxtProgrammerIDProgramToolMissing, testTables, t) +} + +func TestProgrammersTxtProgrammerIDProgramToolLTMinLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-programmers.txt", ruleresult.Skip, ""}, + {"Invalid", "invalid-programmers.txt", ruleresult.NotRun, ""}, + {"No programmers", "no-programmers-programmers.txt", ruleresult.Skip, ""}, + {"Property LT min", "programmerID-program-tool-LT-programmers.txt", ruleresult.Fail, "foo, bar"}, + {"Valid", "valid-programmers.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(ProgrammersTxtProgrammerIDProgramToolLTMinLength, testTables, t) +} diff --git a/internal/rule/rulefunction/testdata/platforms/invalid-programmers.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/invalid-programmers.txt/boards.txt new file mode 100644 index 000000000..075d9bc34 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/invalid-programmers.txt/boards.txt @@ -0,0 +1,7 @@ +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/invalid-programmers.txt/programmers.txt b/internal/rule/rulefunction/testdata/platforms/invalid-programmers.txt/programmers.txt new file mode 100644 index 000000000..126cc32ba --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/invalid-programmers.txt/programmers.txt @@ -0,0 +1 @@ +This makes the format invalid diff --git a/internal/rule/rulefunction/testdata/platforms/missing-programmers.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/missing-programmers.txt/boards.txt new file mode 100644 index 000000000..075d9bc34 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/missing-programmers.txt/boards.txt @@ -0,0 +1,7 @@ +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/no-programmers-programmers.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/no-programmers-programmers.txt/boards.txt new file mode 100644 index 000000000..075d9bc34 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/no-programmers-programmers.txt/boards.txt @@ -0,0 +1,7 @@ +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/no-programmers-programmers.txt/programmers.txt b/internal/rule/rulefunction/testdata/platforms/no-programmers-programmers.txt/programmers.txt new file mode 100644 index 000000000..e69de29bb diff --git a/internal/rule/rulefunction/testdata/platforms/programmerID-name-LT-programmers.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/programmerID-name-LT-programmers.txt/boards.txt new file mode 100644 index 000000000..075d9bc34 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/programmerID-name-LT-programmers.txt/boards.txt @@ -0,0 +1,7 @@ +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/programmerID-name-LT-programmers.txt/programmers.txt b/internal/rule/rulefunction/testdata/platforms/programmerID-name-LT-programmers.txt/programmers.txt new file mode 100644 index 000000000..7ccf017e0 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/programmerID-name-LT-programmers.txt/programmers.txt @@ -0,0 +1,12 @@ +foo.name= +foo.program.tool=avrdude + +usbasp.name=USBasp +usbasp.communication=usb +usbasp.protocol=usbasp +usbasp.program.protocol=usbasp +usbasp.program.tool=avrdude +usbasp.program.extra_params=-Pusb + +bar.name= +bar.program.tool=bartool diff --git a/internal/rule/rulefunction/testdata/platforms/programmerID-name-missing-programmers.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/programmerID-name-missing-programmers.txt/boards.txt new file mode 100644 index 000000000..075d9bc34 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/programmerID-name-missing-programmers.txt/boards.txt @@ -0,0 +1,7 @@ +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/programmerID-name-missing-programmers.txt/programmers.txt b/internal/rule/rulefunction/testdata/platforms/programmerID-name-missing-programmers.txt/programmers.txt new file mode 100644 index 000000000..1bd6285be --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/programmerID-name-missing-programmers.txt/programmers.txt @@ -0,0 +1,10 @@ +foo.program.tool=avrdude + +usbasp.name=USBasp +usbasp.communication=usb +usbasp.protocol=usbasp +usbasp.program.protocol=usbasp +usbasp.program.tool=avrdude +usbasp.program.extra_params=-Pusb + +bar.program.tool=bartool diff --git a/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-LT-programmers.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-LT-programmers.txt/boards.txt new file mode 100644 index 000000000..075d9bc34 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-LT-programmers.txt/boards.txt @@ -0,0 +1,7 @@ +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-LT-programmers.txt/programmers.txt b/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-LT-programmers.txt/programmers.txt new file mode 100644 index 000000000..6c2a8ad8d --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-LT-programmers.txt/programmers.txt @@ -0,0 +1,12 @@ +foo.name=Foo +foo.program.tool= + +usbasp.name=USBasp +usbasp.communication=usb +usbasp.protocol=usbasp +usbasp.program.protocol=usbasp +usbasp.program.tool=avrdude +usbasp.program.extra_params=-Pusb + +bar.name=Bar +bar.program.tool= diff --git a/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-missing-programmers.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-missing-programmers.txt/boards.txt new file mode 100644 index 000000000..075d9bc34 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-missing-programmers.txt/boards.txt @@ -0,0 +1,7 @@ +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-missing-programmers.txt/programmers.txt b/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-missing-programmers.txt/programmers.txt new file mode 100644 index 000000000..43750bca9 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/programmerID-program-tool-missing-programmers.txt/programmers.txt @@ -0,0 +1,10 @@ +foo.name=Foo + +usbasp.name=USBasp +usbasp.communication=usb +usbasp.protocol=usbasp +usbasp.program.protocol=usbasp +usbasp.program.tool=avrdude +usbasp.program.extra_params=-Pusb + +bar.name=Bar diff --git a/internal/rule/rulefunction/testdata/platforms/valid-programmers.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/valid-programmers.txt/boards.txt new file mode 100644 index 000000000..075d9bc34 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/valid-programmers.txt/boards.txt @@ -0,0 +1,7 @@ +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/valid-programmers.txt/programmers.txt b/internal/rule/rulefunction/testdata/platforms/valid-programmers.txt/programmers.txt new file mode 100644 index 000000000..b3b383141 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/valid-programmers.txt/programmers.txt @@ -0,0 +1,12 @@ +foo.name=Foo +foo.program.tool=avrdude + +usbasp.name=USBasp +usbasp.communication=usb +usbasp.protocol=usbasp +usbasp.program.protocol=usbasp +usbasp.program.tool=avrdude +usbasp.program.extra_params=-Pusb + +bar.name=Bar +bar.program.tool=bartool diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index f57629b99..5aca30c29 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -8,6 +8,10 @@ // etc/schemas/arduino-library-properties-permissive-schema.json // etc/schemas/arduino-library-properties-schema.json // etc/schemas/arduino-library-properties-strict-schema.json +// etc/schemas/arduino-programmers-txt-definitions-schema.json +// etc/schemas/arduino-programmers-txt-permissive-schema.json +// etc/schemas/arduino-programmers-txt-schema.json +// etc/schemas/arduino-programmers-txt-strict-schema.json // etc/schemas/general-definitions-schema.json package schemadata @@ -2337,6 +2341,321 @@ func arduinoLibraryPropertiesStrictSchemaJson() (*asset, error) { return a, nil } +var _arduinoProgrammersTxtDefinitionsSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-programmers-txt-definitions-schema.json", + "title": "Shared definitions for the Arduino programmers.txt schemas", + "definitions": { + "propertiesObjects": { + "programmerID": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/programmerIDName/permissive/object" + }, + "program.tool": { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/permissive/object" + } + } + }, + { + "$ref": "#/definitions/requiredObjects/programmerID/permissive/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/programmerIDName/specification/object" + }, + "program.tool": { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/specification/object" + } + } + }, + { + "$ref": "#/definitions/requiredObjects/programmerID/specification/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerID/base/object" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/programmerIDName/strict/object" + }, + "program.tool": { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/strict/object" + } + } + }, + { + "$ref": "#/definitions/requiredObjects/programmerID/strict/object" + } + ] + } + } + }, + "programmerIDName": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDName/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDName/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDName/base/object" + } + ] + } + } + }, + "programmerIDProgramTool": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/programmerIDProgramTool/base/object" + } + ] + } + } + } + }, + "requiredObjects": { + "programmerID": { + "base": { + "object": { + "allOf": [ + { + "required": ["name", "program.tool"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/programmerID/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/programmerID/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/programmerID/base/object" + } + ] + } + } + } + } + } +} +`) + +func arduinoProgrammersTxtDefinitionsSchemaJsonBytes() ([]byte, error) { + return _arduinoProgrammersTxtDefinitionsSchemaJson, nil +} + +func arduinoProgrammersTxtDefinitionsSchemaJson() (*asset, error) { + bytes, err := arduinoProgrammersTxtDefinitionsSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-programmers-txt-definitions-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoProgrammersTxtPermissiveSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-programmers-txt-permissive-schema.json", + "title": "Arduino programmers.txt JSON permissive schema", + "description": "programmers.txt contains the definitions of Arduino hardware programmers. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + "$comment": "For information on the programmers.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "patternProperties": { + ".+": { + "$ref": "arduino-programmers-txt-definitions-schema.json#/definitions/propertiesObjects/programmerID/permissive/object" + } + } +} +`) + +func arduinoProgrammersTxtPermissiveSchemaJsonBytes() ([]byte, error) { + return _arduinoProgrammersTxtPermissiveSchemaJson, nil +} + +func arduinoProgrammersTxtPermissiveSchemaJson() (*asset, error) { + bytes, err := arduinoProgrammersTxtPermissiveSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-programmers-txt-permissive-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoProgrammersTxtSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-programmers-txt-schema.json", + "title": "Arduino programmers.txt JSON schema", + "description": "programmers.txt contains the definitions of Arduino hardware programmers. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + "$comment": "For information on the programmers.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "patternProperties": { + ".+": { + "$ref": "arduino-programmers-txt-definitions-schema.json#/definitions/propertiesObjects/programmerID/specification/object" + } + } +} +`) + +func arduinoProgrammersTxtSchemaJsonBytes() ([]byte, error) { + return _arduinoProgrammersTxtSchemaJson, nil +} + +func arduinoProgrammersTxtSchemaJson() (*asset, error) { + bytes, err := arduinoProgrammersTxtSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-programmers-txt-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoProgrammersTxtStrictSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-programmers-txt-strict-schema.json", + "title": "Arduino programmers.txt JSON strict schema", + "description": "programmers.txt contains the definitions of Arduino hardware programmers. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#programmerstxt", + "$comment": "For information on the programmers.txt format, see https://godoc.org/github.com/arduino/go-properties-orderedmap", + "type": "object", + "patternProperties": { + ".+": { + "$ref": "arduino-programmers-txt-definitions-schema.json#/definitions/propertiesObjects/programmerID/strict/object" + } + } +} +`) + +func arduinoProgrammersTxtStrictSchemaJsonBytes() ([]byte, error) { + return _arduinoProgrammersTxtStrictSchemaJson, nil +} + +func arduinoProgrammersTxtStrictSchemaJson() (*asset, error) { + bytes, err := arduinoProgrammersTxtStrictSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-programmers-txt-strict-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + var _generalDefinitionsSchemaJson = []byte(`{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/general-definitions-schema.json", @@ -2433,6 +2752,10 @@ var _bindata = map[string]func() (*asset, error){ "arduino-library-properties-permissive-schema.json": arduinoLibraryPropertiesPermissiveSchemaJson, "arduino-library-properties-schema.json": arduinoLibraryPropertiesSchemaJson, "arduino-library-properties-strict-schema.json": arduinoLibraryPropertiesStrictSchemaJson, + "arduino-programmers-txt-definitions-schema.json": arduinoProgrammersTxtDefinitionsSchemaJson, + "arduino-programmers-txt-permissive-schema.json": arduinoProgrammersTxtPermissiveSchemaJson, + "arduino-programmers-txt-schema.json": arduinoProgrammersTxtSchemaJson, + "arduino-programmers-txt-strict-schema.json": arduinoProgrammersTxtStrictSchemaJson, "general-definitions-schema.json": generalDefinitionsSchemaJson, } @@ -2485,6 +2808,10 @@ var _bintree = &bintree{nil, map[string]*bintree{ "arduino-library-properties-permissive-schema.json": &bintree{arduinoLibraryPropertiesPermissiveSchemaJson, map[string]*bintree{}}, "arduino-library-properties-schema.json": &bintree{arduinoLibraryPropertiesSchemaJson, map[string]*bintree{}}, "arduino-library-properties-strict-schema.json": &bintree{arduinoLibraryPropertiesStrictSchemaJson, map[string]*bintree{}}, + "arduino-programmers-txt-definitions-schema.json": &bintree{arduinoProgrammersTxtDefinitionsSchemaJson, map[string]*bintree{}}, + "arduino-programmers-txt-permissive-schema.json": &bintree{arduinoProgrammersTxtPermissiveSchemaJson, map[string]*bintree{}}, + "arduino-programmers-txt-schema.json": &bintree{arduinoProgrammersTxtSchemaJson, map[string]*bintree{}}, + "arduino-programmers-txt-strict-schema.json": &bintree{arduinoProgrammersTxtStrictSchemaJson, map[string]*bintree{}}, "general-definitions-schema.json": &bintree{generalDefinitionsSchemaJson, map[string]*bintree{}}, }} From 70add3b3224f5e581045131cea44e84f6b195306 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 4 Jan 2021 04:56:45 -0800 Subject: [PATCH 6/6] Remove boards.txt debug.tool property rules It turns out that the debug configuration system was completely reworked without updating the platform specification. This resulted in the specification-based rules that had been implemented being worthless. --- ...arduino-boards-txt-definitions-schema.json | 50 ------------------- .../boardstxt/boardstxtschema_test.go | 4 -- .../ruleconfiguration/ruleconfiguration.go | 16 ------ internal/rule/rulefunction/platform.go | 19 ------- internal/rule/rulefunction/platform_test.go | 12 ----- internal/rule/schema/schemadata/bindata.go | 50 ------------------- 6 files changed, 151 deletions(-) diff --git a/etc/schemas/arduino-boards-txt-definitions-schema.json b/etc/schemas/arduino-boards-txt-definitions-schema.json index 875736d8a..5f01879e4 100644 --- a/etc/schemas/arduino-boards-txt-definitions-schema.json +++ b/etc/schemas/arduino-boards-txt-definitions-schema.json @@ -134,9 +134,6 @@ "build.core": { "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/permissive/object" }, - "debug.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/permissive/object" - }, "hide": { "$ref": "#/definitions/propertiesObjects/boardIDHide/permissive/object" }, @@ -202,9 +199,6 @@ "build.core": { "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/specification/object" }, - "debug.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/specification/object" - }, "hide": { "$ref": "#/definitions/propertiesObjects/boardIDHide/specification/object" }, @@ -270,9 +264,6 @@ "build.core": { "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/strict/object" }, - "debug.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/strict/object" - }, "hide": { "$ref": "#/definitions/propertiesObjects/boardIDHide/strict/object" }, @@ -445,47 +436,6 @@ } } }, - "boardIDDebugTool": { - "base": { - "object": { - "allOf": [ - { - "type": "string" - }, - { - "minLength": 1 - } - ] - } - }, - "permissive": { - "object": { - "allOf": [ - { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" - } - ] - } - }, - "specification": { - "object": { - "allOf": [ - { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" - } - ] - } - }, - "strict": { - "object": { - "allOf": [ - { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" - } - ] - } - } - }, "boardIDHide": { "base": { "object": { diff --git a/internal/project/platform/boardstxt/boardstxtschema_test.go b/internal/project/platform/boardstxt/boardstxtschema_test.go index c400b7958..ea3ad2b42 100644 --- a/internal/project/platform/boardstxt/boardstxtschema_test.go +++ b/internal/project/platform/boardstxt/boardstxtschema_test.go @@ -75,10 +75,6 @@ func TestMinLength(t *testing.T) { {"foo.build.core", "foo/build\\.core", 1, compliancelevel.Specification}, {"foo.build.core", "foo/build\\.core", 1, compliancelevel.Strict}, - {"foo.debug.tool", "foo/debug\\.tool", 1, compliancelevel.Permissive}, - {"foo.debug.tool", "foo/debug\\.tool", 1, compliancelevel.Specification}, - {"foo.debug.tool", "foo/debug\\.tool", 1, compliancelevel.Strict}, - {"foo.menu.bar.baz", "foo/menu\\.bar\\.baz", 1, compliancelevel.Permissive}, {"foo.menu.bar.baz", "foo/menu\\.bar\\.baz", 1, compliancelevel.Specification}, {"foo.menu.bar.baz", "foo/menu\\.bar\\.baz", 1, compliancelevel.Strict}, diff --git a/internal/rule/ruleconfiguration/ruleconfiguration.go b/internal/rule/ruleconfiguration/ruleconfiguration.go index 538f7d281..9a6dae06e 100644 --- a/internal/rule/ruleconfiguration/ruleconfiguration.go +++ b/internal/rule/ruleconfiguration/ruleconfiguration.go @@ -1577,22 +1577,6 @@ var configurations = []Type{ ErrorModes: []rulemode.Type{rulemode.Strict}, RuleFunction: rulefunction.BoardsTxtUserExtraFlagsUsage, }, - { - ProjectType: projecttype.Platform, - SuperprojectType: projecttype.All, - Category: "configuration files", - Subcategory: "boards.txt", - ID: "PF010", - Brief: "debug.tool < min length", - Description: "", - MessageTemplate: "debug.tool value for board ID(s) {{.}} is less than the minimum length. See: https://arduino.github.io/arduino-cli/latest/platform-specification/#sketch-debugging-configuration", - DisableModes: nil, - EnableModes: []rulemode.Type{rulemode.Default}, - InfoModes: nil, - WarningModes: nil, - ErrorModes: []rulemode.Type{rulemode.Default}, - RuleFunction: rulefunction.BoardsTxtBoardIDDebugToolLTMinLength, - }, { ProjectType: projecttype.Platform, SuperprojectType: projecttype.All, diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index fab9ee356..8d58eccac 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -192,25 +192,6 @@ func BoardsTxtUserExtraFlagsUsage() (result ruleresult.Type, output string) { return ruleresult.Pass, "" } -// BoardsTxtBoardIDDebugToolLTMinLength checks if any of the board debug.tool values are less than the minimum length. -func BoardsTxtBoardIDDebugToolLTMinLength() (result ruleresult.Type, output string) { - if projectdata.BoardsTxtLoadError() != nil { - return ruleresult.NotRun, "Couldn't load boards.txt" - } - - if len(projectdata.BoardsTxtBoardIds()) == 0 { - return ruleresult.Skip, "boards.txt has no boards" - } - - nonCompliantBoardIDs := boardIDValueLTMinLength("debug\\.tool", compliancelevel.Specification) - - if len(nonCompliantBoardIDs) > 0 { - return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") - } - - return ruleresult.Pass, "" -} - // BoardsTxtBoardIDHideInvalid checks if any of the board hide values are less than the minimum length. func BoardsTxtBoardIDHideInvalid() (result ruleresult.Type, output string) { if projectdata.BoardsTxtLoadError() != nil { diff --git a/internal/rule/rulefunction/platform_test.go b/internal/rule/rulefunction/platform_test.go index 45b2cd828..dcf43db45 100644 --- a/internal/rule/rulefunction/platform_test.go +++ b/internal/rule/rulefunction/platform_test.go @@ -165,18 +165,6 @@ func TestBoardsTxtUserExtraFlagsUsage(t *testing.T) { checkPlatformRuleFunction(BoardsTxtUserExtraFlagsUsage, testTables, t) } -func TestBoardsTxtBoardIDDebugToolLTMinLength(t *testing.T) { - testTables := []platformRuleFunctionTestTable{ - {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, - {"Invalid", "invalid-boards.txt", ruleresult.NotRun, ""}, - {"No boards", "no-boards-boards.txt", ruleresult.Skip, ""}, - {"Property LT min", "boardID-debug-tool-LT-boards.txt", ruleresult.Fail, "buno, funo"}, - {"Valid", "valid-boards.txt", ruleresult.Pass, ""}, - } - - checkPlatformRuleFunction(BoardsTxtBoardIDDebugToolLTMinLength, testTables, t) -} - func TestBoardsTxtBoardIDHideInvalid(t *testing.T) { testTables := []platformRuleFunctionTestTable{ {"Missing", "missing-boards.txt", ruleresult.NotRun, ""}, diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index 5aca30c29..ff0443f6f 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -202,9 +202,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "build.core": { "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/permissive/object" }, - "debug.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/permissive/object" - }, "hide": { "$ref": "#/definitions/propertiesObjects/boardIDHide/permissive/object" }, @@ -270,9 +267,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "build.core": { "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/specification/object" }, - "debug.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/specification/object" - }, "hide": { "$ref": "#/definitions/propertiesObjects/boardIDHide/specification/object" }, @@ -338,9 +332,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "build.core": { "$ref": "#/definitions/propertiesObjects/boardIDBuildCore/strict/object" }, - "debug.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/strict/object" - }, "hide": { "$ref": "#/definitions/propertiesObjects/boardIDHide/strict/object" }, @@ -513,47 +504,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ } } }, - "boardIDDebugTool": { - "base": { - "object": { - "allOf": [ - { - "type": "string" - }, - { - "minLength": 1 - } - ] - } - }, - "permissive": { - "object": { - "allOf": [ - { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" - } - ] - } - }, - "specification": { - "object": { - "allOf": [ - { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" - } - ] - } - }, - "strict": { - "object": { - "allOf": [ - { - "$ref": "#/definitions/propertiesObjects/boardIDDebugTool/base/object" - } - ] - } - } - }, "boardIDHide": { "base": { "object": {