From e5f7200e23d9f48315484ff1fbff00ab4172a1e8 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sat, 12 Dec 2020 05:25:54 -0800 Subject: [PATCH 1/3] Embed schemas in code Previously, the schemas were provided as external files. This presented some difficulties for testing and was also likely to make distribution and installation more complex. By embedding the schemas as binary data in the Go code, the tool can consist of a single standalone binary. --- .github/workflows/test.yml | 8 + .prettierignore | 2 +- Taskfile.yml | 8 + check/check.go | 2 +- check/checkdata/checkdata.go | 4 +- check/checkdata/library.go | 11 +- check/checkdata/packageindex_test.go | 2 +- check/checkdata/platform_test.go | 2 +- .../checkdata/schema/parsevalidationresult.go | 73 +- check/checkdata/schema/schema.go | 141 +- check/checkdata/schema/schema_test.go | 154 +- check/checkdata/schema/schemadata/bindata.go | 1371 +++++++++++++++++ .../checkdata/schema/schemadata/schemadata.go | 2 + check/checkdata/schema/testdata/bindata.go | 425 +++++ .../testdata/{ => input}/invalid-schema.json | 0 .../{ => input}/referenced-schema-1.json | 0 .../{ => input}/referenced-schema-2.json | 0 .../{ => input}/schema-without-id.json | 0 .../valid-schema-with-references.json | 0 .../testdata/{ => input}/valid-schema.json | 0 check/checkdata/schema/testdata/testdata.go | 2 + check/checkfunctions/checkfunctions_test.go | 2 +- check/checkfunctions/library.go | 69 +- check/checkfunctions/library_test.go | 4 +- check/checkfunctions/packageindex_test.go | 2 +- check/checkfunctions/platform_test.go | 2 +- check/checkfunctions/sketch_test.go | 2 +- configuration/configuration.go | 9 - go.mod | 1 - go.sum | 3 +- .../libraryproperties/libraryproperties.go | 22 +- .../librarypropertiesschemas_test.go | 106 +- 32 files changed, 2094 insertions(+), 335 deletions(-) create mode 100644 check/checkdata/schema/schemadata/bindata.go create mode 100644 check/checkdata/schema/schemadata/schemadata.go create mode 100644 check/checkdata/schema/testdata/bindata.go rename check/checkdata/schema/testdata/{ => input}/invalid-schema.json (100%) rename check/checkdata/schema/testdata/{ => input}/referenced-schema-1.json (100%) rename check/checkdata/schema/testdata/{ => input}/referenced-schema-2.json (100%) rename check/checkdata/schema/testdata/{ => input}/schema-without-id.json (100%) rename check/checkdata/schema/testdata/{ => input}/valid-schema-with-references.json (100%) rename check/checkdata/schema/testdata/{ => input}/valid-schema.json (100%) create mode 100644 check/checkdata/schema/testdata/testdata.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 11445d93b..a447ef77d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,6 +9,7 @@ on: - "go.sum" - "**/*.go" - "**/testdata/**" + - "etc/schemas/**/*.json" pull_request: paths: - ".github/workflows/test.yml" @@ -17,6 +18,7 @@ on: - "go.sum" - "**/*.go" - "**/testdata/**" + - "etc/schemas/**/*.json" jobs: test-go: @@ -44,6 +46,12 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} version: 3.x + - name: Generate code + run: task go:generate + + - name: Check for forgotten code generation + run: git diff --color --exit-code + - name: Build run: task build diff --git a/.prettierignore b/.prettierignore index b26169227..e74956847 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,7 +5,7 @@ .ionide/ # Test files -/check/checkdata/schema/testdata/invalid-schema.json +/check/checkdata/schema/testdata/input/invalid-schema.json /check/checkdata/testdata/packageindexes/invalid-JSON/package_foo_index.json /check/checkfunctions/testdata/packageindexes/invalid-JSON/package_foo_index.json /check/checkfunctions/testdata/sketches/InvalidJSONMetadataFile/sketch.json diff --git a/Taskfile.yml b/Taskfile.yml index a07bcc46e..8c154b38c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -12,6 +12,14 @@ tasks: - task: go:test-unit - task: schema:compile + go:generate: + desc: Generate Go code + cmds: + - go get -u "github.com/go-bindata/go-bindata/...@v3.1.1" + - go-bindata -nocompress -nometadata -o "./check/checkdata/schema/schemadata/bindata.go" --pkg schemadata --prefix "./etc/schemas/" "./etc/schemas/" + - go-bindata -nocompress -nometadata -o "./check/checkdata/schema/testdata/bindata.go" --pkg testdata --prefix "./check/checkdata/schema/testdata/input/" "./check/checkdata/schema/testdata/input/" + - task: go:format + go:test-unit: desc: Run unit tests cmds: diff --git a/check/check.go b/check/check.go index c98a391ef..4827631c2 100644 --- a/check/check.go +++ b/check/check.go @@ -34,7 +34,7 @@ import ( func RunChecks(project project.Type) { feedback.Printf("\nChecking %s in %s\n", project.ProjectType, project.Path) - checkdata.Initialize(project, configuration.SchemasPath()) + checkdata.Initialize(project) for _, checkConfiguration := range checkconfigurations.Configurations() { runCheck, err := shouldRun(checkConfiguration, project) diff --git a/check/checkdata/checkdata.go b/check/checkdata/checkdata.go index 0b101ed4f..6b7790b57 100644 --- a/check/checkdata/checkdata.go +++ b/check/checkdata/checkdata.go @@ -27,7 +27,7 @@ import ( ) // Initialize gathers the check data for the specified project. -func Initialize(project project.Type, schemasPath *paths.Path) { +func Initialize(project project.Type) { superprojectType = project.SuperprojectType projectType = project.ProjectType projectPath = project.Path @@ -35,7 +35,7 @@ func Initialize(project project.Type, schemasPath *paths.Path) { case projecttype.Sketch: InitializeForSketch(project) case projecttype.Library: - InitializeForLibrary(project, schemasPath) + InitializeForLibrary(project) case projecttype.Platform: InitializeForPlatform(project) case projecttype.PackageIndex: diff --git a/check/checkdata/library.go b/check/checkdata/library.go index addb554ee..91881a939 100644 --- a/check/checkdata/library.go +++ b/check/checkdata/library.go @@ -21,20 +21,19 @@ import ( "net/http" "os" + "github.com/arduino/arduino-check/check/checkdata/schema" "github.com/arduino/arduino-check/check/checkdata/schema/compliancelevel" "github.com/arduino/arduino-check/project" "github.com/arduino/arduino-check/project/library/libraryproperties" "github.com/arduino/arduino-check/result/feedback" "github.com/arduino/arduino-cli/arduino/libraries" - "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" "github.com/client9/misspell" - "github.com/ory/jsonschema/v3" "github.com/sirupsen/logrus" ) // Initialize gathers the library check data for the specified project. -func InitializeForLibrary(project project.Type, schemasPath *paths.Path) { +func InitializeForLibrary(project project.Type) { var err error libraryProperties, libraryPropertiesLoadError = libraryproperties.Properties(project.Path) @@ -43,7 +42,7 @@ func InitializeForLibrary(project project.Type, schemasPath *paths.Path) { // TODO: can I even do this? libraryPropertiesSchemaValidationResult = nil } else { - libraryPropertiesSchemaValidationResult = libraryproperties.Validate(libraryProperties, schemasPath) + libraryPropertiesSchemaValidationResult = libraryproperties.Validate(libraryProperties) } loadedLibrary, err = libraries.Load(project.Path, libraries.User) @@ -98,10 +97,10 @@ func LibraryProperties() *properties.Map { return libraryProperties } -var libraryPropertiesSchemaValidationResult map[compliancelevel.Type]*jsonschema.ValidationError +var libraryPropertiesSchemaValidationResult map[compliancelevel.Type]schema.ValidationResult // LibraryPropertiesSchemaValidationResult returns the result of validating library.properties against the JSON schema. -func LibraryPropertiesSchemaValidationResult() map[compliancelevel.Type]*jsonschema.ValidationError { +func LibraryPropertiesSchemaValidationResult() map[compliancelevel.Type]schema.ValidationResult { return libraryPropertiesSchemaValidationResult } diff --git a/check/checkdata/packageindex_test.go b/check/checkdata/packageindex_test.go index dbecb03a8..6648f6237 100644 --- a/check/checkdata/packageindex_test.go +++ b/check/checkdata/packageindex_test.go @@ -53,7 +53,7 @@ func TestInitializeForPackageIndex(t *testing.T) { ProjectType: projecttype.PackageIndex, SuperprojectType: projecttype.PackageIndex, } - Initialize(testProject, nil) + Initialize(testProject) testTable.packageIndexLoadErrorAssertion(t, PackageIndexLoadError(), testTable.testName) if PackageIndexLoadError() == nil { diff --git a/check/checkdata/platform_test.go b/check/checkdata/platform_test.go index 80b60acb3..6a6dab6bf 100644 --- a/check/checkdata/platform_test.go +++ b/check/checkdata/platform_test.go @@ -53,7 +53,7 @@ func TestInitializeForPlatform(t *testing.T) { ProjectType: projecttype.Platform, SuperprojectType: projecttype.Platform, } - Initialize(testProject, nil) + Initialize(testProject) testTable.boardsTxtLoadErrorAssertion(t, BoardsTxtLoadError(), testTable.testName) if BoardsTxtLoadError() == nil { diff --git a/check/checkdata/schema/parsevalidationresult.go b/check/checkdata/schema/parsevalidationresult.go index b1db7af51..fef2a63e2 100644 --- a/check/checkdata/schema/parsevalidationresult.go +++ b/check/checkdata/schema/parsevalidationresult.go @@ -20,39 +20,38 @@ import ( "fmt" "regexp" - "github.com/arduino/go-paths-helper" "github.com/ory/jsonschema/v3" "github.com/sirupsen/logrus" ) // RequiredPropertyMissing returns whether the given required property is missing from the document. -func RequiredPropertyMissing(propertyName string, validationResult *jsonschema.ValidationError, schemasPath *paths.Path) bool { - return ValidationErrorMatch("#", "/required$", "", "^#/"+propertyName+"$", validationResult, schemasPath) +func RequiredPropertyMissing(propertyName string, validationResult ValidationResult) bool { + return ValidationErrorMatch("#", "/required$", "", "^#/"+propertyName+"$", validationResult) } // PropertyPatternMismatch returns whether the given property did not match the regular expression defined in the JSON schema. -func PropertyPatternMismatch(propertyName string, validationResult *jsonschema.ValidationError, schemasPath *paths.Path) bool { - return ValidationErrorMatch("#/"+propertyName, "/pattern$", "", "", validationResult, schemasPath) +func PropertyPatternMismatch(propertyName string, validationResult ValidationResult) bool { + return ValidationErrorMatch("#/"+propertyName, "/pattern$", "", "", validationResult) } // PropertyLessThanMinLength returns whether the given property is less than the minimum length allowed by the schema. -func PropertyLessThanMinLength(propertyName string, validationResult *jsonschema.ValidationError, schemasPath *paths.Path) bool { - return ValidationErrorMatch("^#/"+propertyName+"$", "/minLength$", "", "", validationResult, schemasPath) +func PropertyLessThanMinLength(propertyName string, validationResult ValidationResult) bool { + return ValidationErrorMatch("^#/"+propertyName+"$", "/minLength$", "", "", validationResult) } // PropertyGreaterThanMaxLength returns whether the given property is greater than the maximum length allowed by the schema. -func PropertyGreaterThanMaxLength(propertyName string, validationResult *jsonschema.ValidationError, schemasPath *paths.Path) bool { - return ValidationErrorMatch("^#/"+propertyName+"$", "/maxLength$", "", "", validationResult, schemasPath) +func PropertyGreaterThanMaxLength(propertyName string, validationResult ValidationResult) bool { + return ValidationErrorMatch("^#/"+propertyName+"$", "/maxLength$", "", "", validationResult) } // PropertyEnumMismatch returns whether the given property does not match any of the items in the enum array. -func PropertyEnumMismatch(propertyName string, validationResult *jsonschema.ValidationError, schemasPath *paths.Path) bool { - return ValidationErrorMatch("#/"+propertyName, "/enum$", "", "", validationResult, schemasPath) +func PropertyEnumMismatch(propertyName string, validationResult ValidationResult) bool { + return ValidationErrorMatch("#/"+propertyName, "/enum$", "", "", validationResult) } // MisspelledOptionalPropertyFound returns whether a misspelled optional property was found. -func MisspelledOptionalPropertyFound(validationResult *jsonschema.ValidationError, schemasPath *paths.Path) bool { - return ValidationErrorMatch("#/", "/misspelledOptionalProperties/", "", "", validationResult, schemasPath) +func MisspelledOptionalPropertyFound(validationResult ValidationResult) bool { + return ValidationErrorMatch("#/", "/misspelledOptionalProperties/", "", "", validationResult) } // ValidationErrorMatch returns whether the given query matches against the JSON schema validation error. @@ -62,10 +61,9 @@ func ValidationErrorMatch( schemaPointerQuery, schemaPointerValueQuery, failureContextQuery string, - validationResult *jsonschema.ValidationError, - schemasPath *paths.Path, + validationResult ValidationResult, ) bool { - if validationResult == nil { + if validationResult.Result == nil { // No error, so nothing to match. logrus.Trace("Schema validation passed. No match is possible.") return false @@ -82,7 +80,7 @@ func ValidationErrorMatch( schemaPointerValueRegexp, failureContextRegexp, validationResult, - schemasPath) + ) } func validationErrorMatch( @@ -90,20 +88,19 @@ func validationErrorMatch( schemaPointerRegexp, schemaPointerValueRegexp, failureContextRegexp *regexp.Regexp, - validationError *jsonschema.ValidationError, - schemasPath *paths.Path, + validationError ValidationResult, ) bool { logrus.Trace("--------Checking schema validation failure match--------") - logrus.Tracef("Checking instance pointer: %s match with regexp: %s", validationError.InstancePtr, instancePointerRegexp) - if instancePointerRegexp.MatchString(validationError.InstancePtr) { + logrus.Tracef("Checking instance pointer: %s match with regexp: %s", validationError.Result.InstancePtr, instancePointerRegexp) + if instancePointerRegexp.MatchString(validationError.Result.InstancePtr) { logrus.Tracef("Matched!") - matchedSchemaPointer := validationErrorSchemaPointerMatch(schemaPointerRegexp, validationError, schemasPath) + matchedSchemaPointer := validationErrorSchemaPointerMatch(schemaPointerRegexp, validationError) if matchedSchemaPointer != "" { logrus.Tracef("Matched!") - if validationErrorSchemaPointerValueMatch(schemaPointerValueRegexp, validationError.SchemaURL, matchedSchemaPointer, schemasPath) { + if validationErrorSchemaPointerValueMatch(schemaPointerValueRegexp, validationError, matchedSchemaPointer) { logrus.Tracef("Matched!") - logrus.Tracef("Checking failure context: %v match with regexp: %s", validationError.Context, failureContextRegexp) - if validationErrorContextMatch(failureContextRegexp, validationError) { + logrus.Tracef("Checking failure context: %v match with regexp: %s", validationError.Result.Context, failureContextRegexp) + if validationErrorContextMatch(failureContextRegexp, validationError.Result) { logrus.Tracef("Matched!") return true } @@ -112,14 +109,16 @@ func validationErrorMatch( } // Recursively check all causes for a match. - for _, validationErrorCause := range validationError.Causes { + for _, validationErrorCause := range validationError.Result.Causes { if validationErrorMatch( instancePointerRegexp, schemaPointerRegexp, schemaPointerValueRegexp, failureContextRegexp, - validationErrorCause, - schemasPath, + ValidationResult{ + Result: validationErrorCause, + dataLoader: validationError.dataLoader, + }, ) { return true } @@ -131,18 +130,17 @@ func validationErrorMatch( // validationErrorSchemaPointerMatch matches the JSON schema pointer related to the validation failure against a regular expression. func validationErrorSchemaPointerMatch( schemaPointerRegexp *regexp.Regexp, - validationError *jsonschema.ValidationError, - schemasPath *paths.Path, + validationError ValidationResult, ) string { - logrus.Tracef("Checking schema pointer: %s match with regexp: %s", validationError.SchemaPtr, schemaPointerRegexp) - if schemaPointerRegexp.MatchString(validationError.SchemaPtr) { - return validationError.SchemaPtr + logrus.Tracef("Checking schema pointer: %s match with regexp: %s", validationError.Result.SchemaPtr, schemaPointerRegexp) + if schemaPointerRegexp.MatchString(validationError.Result.SchemaPtr) { + return validationError.Result.SchemaPtr } // The schema validator does not provide full pointer past logic inversion keywords to the lowest level keywords related to the validation error cause. // Therefore, the sub-keywords must be checked for matches in order to be able to interpret the exact cause of the failure. - if regexp.MustCompile("(/not)|(/oneOf)$").MatchString(validationError.SchemaPtr) { - return validationErrorSchemaSubPointerMatch(schemaPointerRegexp, validationError.SchemaPtr, validationErrorSchemaPointerValue(validationError, schemasPath)) + if regexp.MustCompile("(/not)|(/oneOf)$").MatchString(validationError.Result.SchemaPtr) { + return validationErrorSchemaSubPointerMatch(schemaPointerRegexp, validationError.Result.SchemaPtr, validationErrorSchemaPointerValue(validationError)) } return "" @@ -184,11 +182,10 @@ func validationErrorSchemaSubPointerMatch(schemaPointerRegexp *regexp.Regexp, pa // it matches against the given regular expression. func validationErrorSchemaPointerValueMatch( schemaPointerValueRegexp *regexp.Regexp, - schemaURL, + validationError ValidationResult, schemaPointer string, - schemasPath *paths.Path, ) bool { - marshalledSchemaPointerValue, err := json.Marshal(schemaPointerValue(schemaURL, schemaPointer, schemasPath)) + marshalledSchemaPointerValue, err := json.Marshal(schemaPointerValue(validationError.Result.SchemaURL, schemaPointer, validationError.dataLoader)) if err != nil { panic(err) } diff --git a/check/checkdata/schema/schema.go b/check/checkdata/schema/schema.go index e665dff1a..f8c4889cb 100644 --- a/check/checkdata/schema/schema.go +++ b/check/checkdata/schema/schema.go @@ -17,44 +17,70 @@ package schema import ( + "bytes" "encoding/json" "fmt" - "net/url" + "io" + "io/ioutil" "path" - "path/filepath" - "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" "github.com/ory/jsonschema/v3" "github.com/sirupsen/logrus" "github.com/xeipuuv/gojsonreference" ) +// dataLoaderType is the signature of the function that returns the byte encoded data associated with the given file name. +type dataLoaderType func(filename string) ([]byte, error) + +// Schema is the type of the compiled JSON schema object. +type Schema struct { + Compiled *jsonschema.Schema + dataLoader dataLoaderType // Function to load the schema data. +} + +// ValidationResult is the type of the result of the validation of the instance document against the JSON schema. +type ValidationResult struct { + Result *jsonschema.ValidationError + dataLoader dataLoaderType // Function used to load the JSON schema data. +} + // Compile compiles the schema files specified by the filename arguments and returns the compiled schema. -func Compile(schemaFilename string, referencedSchemaFilenames []string, schemasPath *paths.Path) *jsonschema.Schema { +func Compile(schemaFilename string, referencedSchemaFilenames []string, dataLoader dataLoaderType) Schema { compiler := jsonschema.NewCompiler() + // Define a custom schema loader for the binary encoded schema. + compiler.LoadURL = func(schemaFilename string) (io.ReadCloser, error) { + schemaData, err := dataLoader(schemaFilename) + if err != nil { + return nil, err + } + + return ioutil.NopCloser(bytes.NewReader(schemaData)), nil + } + // Load the referenced schemas. for _, referencedSchemaFilename := range referencedSchemaFilenames { - if err := loadReferencedSchema(compiler, referencedSchemaFilename, schemasPath); err != nil { + if err := loadReferencedSchema(compiler, referencedSchemaFilename, dataLoader); err != nil { panic(err) } } // Compile the schema. - schemaPath := schemasPath.Join(schemaFilename) - schemaURI := pathURI(schemaPath) - compiledSchema, err := compiler.Compile(schemaURI) + compiledSchema, err := compiler.Compile(schemaFilename) if err != nil { panic(err) } - return compiledSchema + return Schema{ + Compiled: compiledSchema, + dataLoader: dataLoader, + } } // 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 *jsonschema.Schema, schemasPath *paths.Path) *jsonschema.ValidationError { +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() @@ -63,119 +89,104 @@ func Validate(instanceObject *properties.Map, schemaObject *jsonschema.Schema, s instanceInterface[k] = v } - validationError := schemaObject.ValidateInterface(instanceInterface) + validationError := schemaObject.Compiled.ValidateInterface(instanceInterface) result, _ := validationError.(*jsonschema.ValidationError) - if result == nil { + validationResult := ValidationResult{ + Result: result, + dataLoader: schemaObject.dataLoader, + } + if validationResult.Result == nil { logrus.Debug("Schema validation of instance document passed") - } else { logrus.Debug("Schema validation of instance document failed:") - logValidationError(result, schemasPath) + logValidationError(validationResult) logrus.Trace("-----------------------------------------------") } - return result + return validationResult } // loadReferencedSchema adds a schema that is referenced by the parent schema to the compiler object. -func loadReferencedSchema(compiler *jsonschema.Compiler, schemaFilename string, schemasPath *paths.Path) error { - schemaPath := schemasPath.Join(schemaFilename) - schemaFile, err := schemaPath.Open() +func loadReferencedSchema(compiler *jsonschema.Compiler, schemaFilename string, dataLoader dataLoaderType) error { + // Get the $id value from the schema to use as the `url` argument for the `compiler.AddResource()` call. + id, err := schemaID(schemaFilename, dataLoader) if err != nil { return err } - defer schemaFile.Close() - // Get the $id value from the schema to use as the `url` argument for the `compiler.AddResource()` call. - id, err := schemaID(schemaFilename, schemasPath) + schemaData, err := dataLoader(schemaFilename) if err != nil { return err } - return compiler.AddResource(id, schemaFile) + return compiler.AddResource(id, bytes.NewReader(schemaData)) } // schemaID returns the value of the schema's $id key. -func schemaID(schemaFilename string, schemasPath *paths.Path) (string, error) { - schemaPath := schemasPath.Join(schemaFilename) - schemaInterface := unmarshalJSONFile(schemaPath) +func schemaID(schemaFilename string, dataLoader dataLoaderType) (string, error) { + schemaInterface := unmarshalJSONFile(schemaFilename, dataLoader) id, ok := schemaInterface.(map[string]interface{})["$id"].(string) if !ok { - return "", fmt.Errorf("Schema %s is missing an $id keyword", schemaPath) + return "", fmt.Errorf("Schema %s is missing an $id keyword", schemaFilename) } return id, nil } // unmarshalJSONFile returns the data from a JSON file. -func unmarshalJSONFile(filePath *paths.Path) interface{} { - fileBuffer, err := filePath.ReadFile() +func unmarshalJSONFile(filename string, dataLoader dataLoaderType) interface{} { + data, err := dataLoader(filename) if err != nil { panic(err) } var dataInterface interface{} - if err := json.Unmarshal(fileBuffer, &dataInterface); err != nil { + if err := json.Unmarshal(data, &dataInterface); err != nil { panic(err) } return dataInterface } -// pathURI returns the URI representation of the path argument. -func pathURI(path *paths.Path) string { - absolutePath, err := path.Abs() - if err != nil { - panic(err) - } - uriFriendlyPath := filepath.ToSlash(absolutePath.String()) - // In order to be valid, the path in the URI must start with `/`, but Windows paths do not. - if uriFriendlyPath[0] != '/' { - uriFriendlyPath = "/" + uriFriendlyPath - } - pathURI := url.URL{ - Scheme: "file", - Path: uriFriendlyPath, - } - - return pathURI.String() -} - // logValidationError logs the schema validation error data. -func logValidationError(validationError *jsonschema.ValidationError, schemasPath *paths.Path) { +func logValidationError(validationError ValidationResult) { logrus.Trace("--------Schema validation failure cause--------") - logrus.Tracef("Error message: %s", validationError.Error()) - logrus.Tracef("Instance pointer: %v", validationError.InstancePtr) - logrus.Tracef("Schema URL: %s", validationError.SchemaURL) - logrus.Tracef("Schema pointer: %s", validationError.SchemaPtr) - logrus.Tracef("Schema pointer value: %v", validationErrorSchemaPointerValue(validationError, schemasPath)) - logrus.Tracef("Failure context: %v", validationError.Context) - logrus.Tracef("Failure context type: %T", validationError.Context) + logrus.Tracef("Error message: %s", validationError.Result.Error()) + logrus.Tracef("Instance pointer: %v", validationError.Result.InstancePtr) + logrus.Tracef("Schema URL: %s", validationError.Result.SchemaURL) + logrus.Tracef("Schema pointer: %s", validationError.Result.SchemaPtr) + logrus.Tracef("Schema pointer value: %v", validationErrorSchemaPointerValue(validationError)) + logrus.Tracef("Failure context: %v", validationError.Result.Context) + logrus.Tracef("Failure context type: %T", validationError.Result.Context) // Recursively log all causes. - for _, validationErrorCause := range validationError.Causes { - logValidationError(validationErrorCause, schemasPath) + for _, validationErrorCause := range validationError.Result.Causes { + logValidationError( + ValidationResult{ + Result: validationErrorCause, + dataLoader: validationError.dataLoader, + }, + ) } } // validationErrorSchemaPointerValue returns the object identified by the validation error's schema JSON pointer. -func validationErrorSchemaPointerValue(validationError *jsonschema.ValidationError, schemasPath *paths.Path) interface{} { - return schemaPointerValue(validationError.SchemaURL, validationError.SchemaPtr, schemasPath) +func validationErrorSchemaPointerValue(validationError ValidationResult) interface{} { + return schemaPointerValue(validationError.Result.SchemaURL, validationError.Result.SchemaPtr, validationError.dataLoader) } // schemaPointerValue returns the object identified by the given JSON pointer from the schema file. -func schemaPointerValue(schemaURL, schemaPointer string, schemasPath *paths.Path) interface{} { - schemaPath := schemasPath.Join(path.Base(schemaURL)) - return jsonPointerValue(schemaPointer, schemaPath) +func schemaPointerValue(schemaURL, schemaPointer string, dataLoader dataLoaderType) interface{} { + return jsonPointerValue(schemaPointer, path.Base(schemaURL), dataLoader) } // jsonPointerValue returns the object identified by the given JSON pointer from the JSON file. -func jsonPointerValue(jsonPointer string, filePath *paths.Path) interface{} { +func jsonPointerValue(jsonPointer string, fileName string, dataLoader dataLoaderType) interface{} { jsonReference, err := gojsonreference.NewJsonReference(jsonPointer) if err != nil { panic(err) } - jsonInterface := unmarshalJSONFile(filePath) + jsonInterface := unmarshalJSONFile(fileName, dataLoader) jsonPointerValue, _, err := jsonReference.GetPointer().Get(jsonInterface) if err != nil { panic(err) diff --git a/check/checkdata/schema/schema_test.go b/check/checkdata/schema/schema_test.go index 7a49b465e..d6ccd1a7e 100644 --- a/check/checkdata/schema/schema_test.go +++ b/check/checkdata/schema/schema_test.go @@ -16,19 +16,15 @@ package schema import ( - "os" "regexp" - "runtime" "testing" - "github.com/arduino/go-paths-helper" + "github.com/arduino/arduino-check/check/checkdata/schema/testdata" "github.com/arduino/go-properties-orderedmap" "github.com/ory/jsonschema/v3" "github.com/stretchr/testify/require" ) -var schemasPath *paths.Path - var validMap = map[string]string{ "property1": "foo", "property2": "bar", @@ -37,41 +33,38 @@ var validMap = map[string]string{ var validPropertiesMap = properties.NewFromHashmap(validMap) -var validSchemaWithReferences *jsonschema.Schema +var validSchemaWithReferences Schema func init() { - workingPath, _ := os.Getwd() - schemasPath = paths.New(workingPath).Join("testdata") - validSchemaWithReferences = Compile( "valid-schema-with-references.json", []string{ "referenced-schema-1.json", "referenced-schema-2.json", }, - schemasPath, + testdata.Asset, ) } func TestCompile(t *testing.T) { require.Panics(t, func() { - Compile("valid-schema-with-references.json", []string{"nonexistent.json"}, schemasPath) + Compile("valid-schema-with-references.json", []string{"nonexistent.json"}, testdata.Asset) }) require.Panics(t, func() { - Compile("valid-schema-with-references.json", []string{"schema-without-id.json"}, schemasPath) + Compile("valid-schema-with-references.json", []string{"schema-without-id.json"}, testdata.Asset) }) require.Panics(t, func() { - Compile("invalid-schema.json", []string{}, schemasPath) + Compile("invalid-schema.json", []string{}, testdata.Asset) }) require.Panics(t, func() { - Compile("valid-schema-with-references.json", []string{}, schemasPath) + Compile("valid-schema-with-references.json", []string{}, testdata.Asset) }) require.NotPanics(t, func() { - Compile("valid-schema.json", []string{}, schemasPath) + Compile("valid-schema.json", []string{}, testdata.Asset) }) require.NotPanics(t, func() { @@ -81,146 +74,145 @@ func TestCompile(t *testing.T) { "referenced-schema-1.json", "referenced-schema-2.json", }, - schemasPath, + testdata.Asset, ) }) } func TestValidate(t *testing.T) { - schemaObject := Compile("valid-schema.json", []string{}, schemasPath) + schemaObject := Compile("valid-schema.json", []string{}, testdata.Asset) propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, schemaObject, schemasPath) - require.Nil(t, validationResult) + validationResult := Validate(propertiesMap, schemaObject) + require.Nil(t, validationResult.Result) - validationResult = Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.Nil(t, validationResult) + validationResult = Validate(propertiesMap, validSchemaWithReferences) + require.Nil(t, validationResult.Result) propertiesMap.Set("property1", "a") - validationResult = Validate(propertiesMap, schemaObject, schemasPath) - require.Equal(t, "#/property1", validationResult.InstancePtr) - require.Equal(t, "#/properties/property1/minLength", validationResult.SchemaPtr) + validationResult = Validate(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, schemasPath) - require.False(t, RequiredPropertyMissing("property1", validationResult, schemasPath)) + validationResult := Validate(propertiesMap, validSchemaWithReferences) + require.False(t, RequiredPropertyMissing("property1", validationResult)) propertiesMap.Remove("property1") - validationResult = Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.True(t, RequiredPropertyMissing("property1", validationResult, schemasPath)) + validationResult = Validate(propertiesMap, validSchemaWithReferences) + require.True(t, RequiredPropertyMissing("property1", validationResult)) } func TestPropertyPatternMismatch(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.False(t, PropertyPatternMismatch("property2", validationResult, schemasPath)) + validationResult := Validate(propertiesMap, validSchemaWithReferences) + require.False(t, PropertyPatternMismatch("property2", validationResult)) propertiesMap.Set("property2", "fOo") - validationResult = Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.True(t, PropertyPatternMismatch("property2", validationResult, schemasPath)) + validationResult = Validate(propertiesMap, validSchemaWithReferences) + require.True(t, PropertyPatternMismatch("property2", validationResult)) - require.False(t, PropertyPatternMismatch("property1", validationResult, schemasPath)) + require.False(t, PropertyPatternMismatch("property1", validationResult)) } func TestPropertyLessThanMinLength(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.False(t, PropertyLessThanMinLength("property1", validationResult, schemasPath)) + validationResult := Validate(propertiesMap, validSchemaWithReferences) + require.False(t, PropertyLessThanMinLength("property1", validationResult)) propertiesMap.Set("property1", "a") - validationResult = Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.True(t, PropertyLessThanMinLength("property1", validationResult, schemasPath)) + validationResult = Validate(propertiesMap, validSchemaWithReferences) + require.True(t, PropertyLessThanMinLength("property1", validationResult)) } func TestPropertyGreaterThanMaxLength(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.False(t, PropertyGreaterThanMaxLength("property1", validationResult, schemasPath)) + validationResult := Validate(propertiesMap, validSchemaWithReferences) + require.False(t, PropertyGreaterThanMaxLength("property1", validationResult)) propertiesMap.Set("property1", "12345") - validationResult = Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.True(t, PropertyGreaterThanMaxLength("property1", validationResult, schemasPath)) + validationResult = Validate(propertiesMap, validSchemaWithReferences) + require.True(t, PropertyGreaterThanMaxLength("property1", validationResult)) } func TestPropertyEnumMismatch(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.False(t, PropertyEnumMismatch("property3", validationResult, schemasPath)) + validationResult := Validate(propertiesMap, validSchemaWithReferences) + require.False(t, PropertyEnumMismatch("property3", validationResult)) propertiesMap.Set("property3", "invalid") - validationResult = Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.True(t, PropertyEnumMismatch("property3", validationResult, schemasPath)) + validationResult = Validate(propertiesMap, validSchemaWithReferences) + require.True(t, PropertyEnumMismatch("property3", validationResult)) } func TestMisspelledOptionalPropertyFound(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.False(t, MisspelledOptionalPropertyFound(validationResult, schemasPath)) + validationResult := Validate(propertiesMap, validSchemaWithReferences) + require.False(t, MisspelledOptionalPropertyFound(validationResult)) propertiesMap.Set("porperties", "foo") - validationResult = Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.True(t, MisspelledOptionalPropertyFound(validationResult, schemasPath)) + validationResult = Validate(propertiesMap, validSchemaWithReferences) + require.True(t, MisspelledOptionalPropertyFound(validationResult)) } func TestValidationErrorMatch(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) - validationResult := Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.False(t, ValidationErrorMatch("", "", "", "", validationResult, schemasPath)) + validationResult := Validate(propertiesMap, validSchemaWithReferences) + require.False(t, ValidationErrorMatch("", "", "", "", validationResult)) propertiesMap.Set("property2", "fOo") - validationResult = Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.False(t, ValidationErrorMatch("nomatch", "nomatch", "nomatch", "nomatch", validationResult, schemasPath)) - require.False(t, ValidationErrorMatch("^#/property2$", "nomatch", "nomatch", "nomatch", validationResult, schemasPath)) - require.False(t, ValidationErrorMatch("^#/property2$", "/pattern$", "nomatch", "nomatch", validationResult, schemasPath)) - require.False(t, ValidationErrorMatch("^#/property2$", "/pattern$", `^\^\[a-z\]\+\$$`, "nomatch", validationResult, schemasPath)) - require.True(t, ValidationErrorMatch("^#/property2$", "/pattern$", `^"\^\[a-z\]\+\$"$`, "", validationResult, schemasPath)) - require.True(t, ValidationErrorMatch("", "", "", "", validationResult, schemasPath)) + validationResult = Validate(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)) + require.False(t, ValidationErrorMatch("^#/property2$", "/pattern$", `^\^\[a-z\]\+\$$`, "nomatch", validationResult)) + require.True(t, ValidationErrorMatch("^#/property2$", "/pattern$", `^"\^\[a-z\]\+\$"$`, "", validationResult)) + require.True(t, ValidationErrorMatch("", "", "", "", validationResult)) propertiesMap.Set("property3", "bAz") - validationResult = Validate(propertiesMap, validSchemaWithReferences, schemasPath) - require.True(t, ValidationErrorMatch("^#/property3$", "/pattern$", "", "", validationResult, schemasPath), "Match pointer below logic inversion keyword") + validationResult = Validate(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, schemasPath) - require.False(t, ValidationErrorMatch("nomatch", "nomatch", "nomatch", "nomatch", validationResult, schemasPath)) - require.True(t, ValidationErrorMatch("", "", "", "^#/property1$", validationResult, schemasPath)) + validationResult = Validate(propertiesMap, validSchemaWithReferences) + require.False(t, ValidationErrorMatch("nomatch", "nomatch", "nomatch", "nomatch", validationResult)) + require.True(t, ValidationErrorMatch("", "", "", "^#/property1$", validationResult)) } func Test_loadReferencedSchema(t *testing.T) { compiler := jsonschema.NewCompiler() - require.Error(t, loadReferencedSchema(compiler, "nonexistent.json", schemasPath)) - require.Error(t, loadReferencedSchema(compiler, "schema-without-id.json", schemasPath)) - require.Nil(t, loadReferencedSchema(compiler, "referenced-schema-2.json", schemasPath)) + require.Panics( + t, + func() { + loadReferencedSchema(compiler, "nonexistent.json", testdata.Asset) + }, + ) + require.Error(t, loadReferencedSchema(compiler, "schema-without-id.json", testdata.Asset)) + require.Nil(t, loadReferencedSchema(compiler, "referenced-schema-2.json", testdata.Asset)) } func Test_schemaID(t *testing.T) { - _, err := schemaID("schema-without-id.json", schemasPath) + _, err := schemaID("schema-without-id.json", testdata.Asset) require.NotNil(t, err) - id, err := schemaID("valid-schema.json", schemasPath) + id, err := schemaID("valid-schema.json", testdata.Asset) require.Equal(t, "https://raw.githubusercontent.com/arduino/arduino-check/main/check/checkdata/schema/testdata/schema-with-references.json", id) require.Nil(t, err) } -func Test_pathURI(t *testing.T) { - switch runtime.GOOS { - case "windows": - require.Equal(t, "file:///c:/foo%20bar", pathURI(paths.New("c:/foo bar"))) - default: - require.Equal(t, "file:///foo%20bar", pathURI(paths.New("/foo bar"))) - } -} - func Test_validationErrorSchemaPointerValue(t *testing.T) { - validationError := jsonschema.ValidationError{ - SchemaURL: "https://raw.githubusercontent.com/arduino/arduino-check/main/check/checkdata/schema/testdata/referenced-schema-1.json", - SchemaPtr: "#/definitions/patternObject/pattern", + validationError := ValidationResult{ + Result: &jsonschema.ValidationError{ + SchemaURL: "https://raw.githubusercontent.com/arduino/arduino-check/main/check/checkdata/schema/testdata/referenced-schema-1.json", + SchemaPtr: "#/definitions/patternObject/pattern", + }, + dataLoader: testdata.Asset, } - schemaPointerValueInterface := validationErrorSchemaPointerValue(&validationError, schemasPath) + schemaPointerValueInterface := validationErrorSchemaPointerValue(validationError) schemaPointerValue, ok := schemaPointerValueInterface.(string) require.True(t, ok) require.Equal(t, "^[a-z]+$", schemaPointerValue) diff --git a/check/checkdata/schema/schemadata/bindata.go b/check/checkdata/schema/schemadata/bindata.go new file mode 100644 index 000000000..da0860067 --- /dev/null +++ b/check/checkdata/schema/schemadata/bindata.go @@ -0,0 +1,1371 @@ +// Package schemadata Code generated by go-bindata. (@generated) DO NOT EDIT. +// sources: +// etc/schemas/arduino-library-properties-definitions-schema.json +// 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/general-definitions-schema.json +package schemadata + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +// Name return file name +func (fi bindataFileInfo) Name() string { + return fi.name +} + +// Size return file size +func (fi bindataFileInfo) Size() int64 { + return fi.size +} + +// Mode return file mode +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} + +// Mode return file modify time +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} + +// IsDir return file whether a directory +func (fi bindataFileInfo) IsDir() bool { + return fi.mode&os.ModeDir != 0 +} + +// Sys return file is sys mode +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _arduinoLibraryPropertiesDefinitionsSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-check/main/etc/schema/arduino-library-properties-definitions-schema.json", + "title": "Shared definitions for the Arduino library.properties schemas", + "type": "object", + "definitions": { + "general": { + "patternObjects": { + "notStartsWithArduino": { + "not": { + "pattern": "^[aA][rR][dD][uU][iI][nN][oO].*$" + } + } + } + }, + "propertiesObjects": { + "name": { + "base": { + "definitions": { + "patternObjects": { + "allowedCharacters": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/depends/base/definitions/patternObject" + }, + { + "not": { + "$comment": "The depends property is a comma separated list of names, so a valid name pattern is a valid depends pattern with the comma excluded", + "pattern": "^.*,.*$" + } + } + ] + } + } + }, + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + }, + { + "$ref": "#/definitions/propertiesObjects/name/base/definitions/patternObjects/allowedCharacters" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/name/base/object" + }, + { + "maxLength": 63 + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/name/base/object" + }, + { + "maxLength": 63 + }, + { + "$comment": "Only official Arduino libraries are allowed to have names starting with \"Arduino\"", + "$ref": "#/definitions/general/patternObjects/notStartsWithArduino" + } + ] + } + }, + "strict": { + "definitions": { + "patternObjects": { + "notContainsSpaces": { + "not": { + "pattern": "^.* .*$" + } + }, + "notContainsArduino": { + "not": { + "pattern": "^.+[aA][rR][dD][uU][iI][nN][oO].*$" + } + }, + "notContainsSuperfluousTerms": { + "not": { + "pattern": "^.*[lL][iI][bB][rR][aA][rR][yY].*$" + } + } + } + }, + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/name/specification/object" + }, + { + "maxLength": 16 + }, + { + "$ref": "#/definitions/propertiesObjects/name/strict/definitions/patternObjects/notContainsSpaces" + }, + { + "$ref": "#/definitions/propertiesObjects/name/strict/definitions/patternObjects/notContainsArduino" + }, + { + "$ref": "#/definitions/propertiesObjects/name/strict/definitions/patternObjects/notContainsSuperfluousTerms" + } + ] + } + } + }, + "version": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/version/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/version/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/version/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/semver" + } + ] + } + } + }, + "author": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/author/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/author/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/author/specification/object" + } + ] + } + } + }, + "maintainer": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/maintainer/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/maintainer/base/object" + }, + { + "$comment": "Only official Arduino libraries are allowed to have maintainer field starting with \"Arduino\"", + "$ref": "#/definitions/general/patternObjects/notStartsWithArduino" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/maintainer/specification/object" + } + ] + } + } + }, + "email": { + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/maintainer/permissive/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/maintainer/specification/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/maintainer/strict/object" + } + ] + } + } + }, + "sentence": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/sentence/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/sentence/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/sentence/specification/object" + } + ] + } + } + }, + "paragraph": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/paragraph/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/paragraph/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/paragraph/specification/object" + } + ] + } + } + }, + "category": { + "base": { + "definitions": { + "enumObject": { + "enum": [ + "Display", + "Communication", + "Signal Input/Output", + "Sensors", + "Device Control", + "Timing", + "Data Storage", + "Data Processing", + "Other" + ] + } + }, + "object": {} + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/category/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/category/base/object" + }, + { + "$ref": "#/definitions/propertiesObjects/category/base/definitions/enumObject" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/category/specification/object" + } + ] + } + } + }, + "url": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/url/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/url/base/object" + }, + { + "format": "uri" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/url/specification/object" + } + ] + } + } + }, + "architectures": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/architectures/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/architectures/base/object" + }, + { + "minLength": 1 + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/architectures/specification/object" + } + ] + } + } + }, + "depends": { + "base": { + "definitions": { + "patternObject": { + "pattern": "^(([a-zA-Z][a-zA-Z0-9 _\\.\\-,]*)|([0-9][a-zA-Z0-9 _\\.\\-]*[a-zA-Z][a-zA-Z0-9 _\\.\\-,]*))*$" + } + }, + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/propertiesObjects/depends/base/definitions/patternObject" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/depends/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/depends/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/depends/specification/object" + } + ] + } + } + }, + "dot_a_linkage": { + "base": { + "object": { + "allOf": [ + { + "enum": ["true", "false"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/dot_a_linkage/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/dot_a_linkage/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/dot_a_linkage/specification/object" + } + ] + } + } + }, + "includes": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "minLength": 1 + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/sentence/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/sentence/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/sentence/specification/object" + } + ] + } + } + }, + "precompiled": { + "base": { + "object": { + "allOf": [ + { + "enum": ["true", "full", "false"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/precompiled/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/precompiled/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/precompiled/specification/object" + } + ] + } + } + }, + "ldflags": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/ldflags/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/ldflags/base/object" + }, + { + "$comment": "Minimum length for a valid -l option", + "minLength": 3 + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/ldflags/specification/object" + } + ] + } + } + } + }, + "propertyNamesObjects": { + "base": { + "object": {} + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertyNamesObjects/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertyNamesObjects/base/object" + } + ] + } + }, + "strict": { + "definitions": { + "propertyNamesObjects": { + "misspelledOptionalProperties": { + "propertyNames": { + "$comment": "Misspelled optional property names", + "allOf": [ + { + "not": { + "pattern": "^[dD][eE][pP][eE][nN][dD]$" + } + }, + { + "not": { + "pattern": "^D[eE][pP][eE][nN][dD][sS]$" + } + }, + { + "not": { + "pattern": "^[dD][oO][tT][_-]?[aA][_-]?[lL][iI][nN][kK][aA][gG][eE][sS]$" + } + }, + { + "not": { + "pattern": "^[dD][oO][tT]-?[aA]-?[lL][iI][nN][kK][aA][gG][eE]$" + } + }, + { + "not": { + "pattern": "^D[oO][tT]_[aA]_[lL][iI][nN][kK][aA][gG][eE]$" + } + }, + { + "not": { + "pattern": "^[iI][nN][cC][lL][uU][dD][eE]$" + } + }, + { + "not": { + "pattern": "^I[nN][cC][lL][uU][dD][eE][sS]$" + } + }, + { + "not": { + "pattern": "^[pP][rR][eE][-_]?[cC][oO][mM][pP][iI][lL][eE]$" + } + }, + { + "not": { + "pattern": "^[pP][rR][eE][-_][cC][oO][mM][pP][iI][lL][eE][dD]$" + } + }, + { + "not": { + "pattern": "^P[rR][eE][-_]?[cC][oO][mM][pP][iI][lL][eE][dD]$" + } + }, + { + "not": { + "pattern": "^[lL][dD][-_]?[fF][lL][aA][gG]$" + } + }, + { + "not": { + "pattern": "^[lL][dD][-_][fF][lL][aA][gG][sS]$" + } + }, + { + "not": { + "pattern": "^L[dD][-_]?[fF][lL][aA][gG][sS]$" + } + } + ] + } + } + } + }, + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertyNamesObjects/strict/definitions/propertyNamesObjects/misspelledOptionalProperties" + }, + { + "$ref": "#/definitions/propertyNamesObjects/specification/object" + } + ] + } + } + }, + "requiredObjects": { + "base": { + "object": { + "allOf": [ + { + "required": ["name", "version", "author", "sentence", "paragraph", "url"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/base/object" + }, + { + "$comment": "The original draft of the library specification had an \"email\" field. This was later changed to \"maintainer\" and \"email\" is now deprecated", + "anyOf": [ + { + "required": ["maintainer"] + }, + { + "required": ["email"] + } + ] + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/base/object" + }, + { + "required": ["maintainer"] + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/specification/object" + }, + { + "required": ["category", "architectures"] + } + ] + } + } + } + } +} +`) + +func arduinoLibraryPropertiesDefinitionsSchemaJsonBytes() ([]byte, error) { + return _arduinoLibraryPropertiesDefinitionsSchemaJson, nil +} + +func arduinoLibraryPropertiesDefinitionsSchemaJson() (*asset, error) { + bytes, err := arduinoLibraryPropertiesDefinitionsSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-library-properties-definitions-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoLibraryPropertiesPermissiveSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-check/main/etc/schema/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", + "type": "object", + "properties": { + "name": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/name/permissive/object" + }, + "version": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/version/permissive/object" + }, + "author": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/author/permissive/object" + }, + "maintainer": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/maintainer/permissive/object" + }, + "email": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/email/permissive/object" + }, + "sentence": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/sentence/permissive/object" + }, + "paragraph": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/paragraph/permissive/object" + }, + "category": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/category/permissive/object" + }, + "url": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/url/permissive/object" + }, + "architectures": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/architectures/permissive/object" + }, + "dot_a_linkage": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/dot_a_linkage/permissive/object" + }, + "depends": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/depends/permissive/object" + }, + "includes": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/includes/permissive/object" + }, + "precompiled": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/precompiled/permissive/object" + }, + "ldflags": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/ldflags/permissive/object" + } + }, + "allOf": [ + { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertyNamesObjects/permissive/object" + }, + { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/requiredObjects/permissive/object" + } + ] +} +`) + +func arduinoLibraryPropertiesPermissiveSchemaJsonBytes() ([]byte, error) { + return _arduinoLibraryPropertiesPermissiveSchemaJson, nil +} + +func arduinoLibraryPropertiesPermissiveSchemaJson() (*asset, error) { + bytes, err := arduinoLibraryPropertiesPermissiveSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-library-properties-permissive-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoLibraryPropertiesSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-check/main/etc/schema/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", + "type": "object", + "properties": { + "name": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/name/specification/object" + }, + "version": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/version/specification/object" + }, + "author": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/author/specification/object" + }, + "maintainer": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/maintainer/specification/object" + }, + "email": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/email/specification/object" + }, + "sentence": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/sentence/specification/object" + }, + "paragraph": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/paragraph/specification/object" + }, + "category": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/category/specification/object" + }, + "url": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/url/specification/object" + }, + "architectures": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/architectures/specification/object" + }, + "dot_a_linkage": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/dot_a_linkage/specification/object" + }, + "depends": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/depends/specification/object" + }, + "includes": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/includes/specification/object" + }, + "precompiled": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/precompiled/specification/object" + }, + "ldflags": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/ldflags/specification/object" + } + }, + "allOf": [ + { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertyNamesObjects/specification/object" + }, + { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/requiredObjects/specification/object" + } + ] +} +`) + +func arduinoLibraryPropertiesSchemaJsonBytes() ([]byte, error) { + return _arduinoLibraryPropertiesSchemaJson, nil +} + +func arduinoLibraryPropertiesSchemaJson() (*asset, error) { + bytes, err := arduinoLibraryPropertiesSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-library-properties-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoLibraryPropertiesStrictSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-check/main/etc/schema/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", + "type": "object", + "properties": { + "name": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/name/strict/object" + }, + "version": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/version/strict/object" + }, + "author": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/author/strict/object" + }, + "maintainer": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/maintainer/strict/object" + }, + "email": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/email/strict/object" + }, + "sentence": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/sentence/strict/object" + }, + "paragraph": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/paragraph/strict/object" + }, + "category": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/category/strict/object" + }, + "url": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/url/strict/object" + }, + "architectures": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/architectures/strict/object" + }, + "dot_a_linkage": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/dot_a_linkage/strict/object" + }, + "depends": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/depends/strict/object" + }, + "includes": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/includes/strict/object" + }, + "precompiled": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/precompiled/strict/object" + }, + "ldflags": { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertiesObjects/ldflags/strict/object" + } + }, + "allOf": [ + { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/propertyNamesObjects/strict/object" + }, + { + "$ref": "arduino-library-properties-definitions-schema.json#/definitions/requiredObjects/strict/object" + } + ] +} +`) + +func arduinoLibraryPropertiesStrictSchemaJsonBytes() ([]byte, error) { + return _arduinoLibraryPropertiesStrictSchemaJson, nil +} + +func arduinoLibraryPropertiesStrictSchemaJson() (*asset, error) { + bytes, err := arduinoLibraryPropertiesStrictSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-library-properties-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-check/main/etc/schema/general-definitions-schema.json", + "title": "Shared definitions", + "description": "Definitions for use in schemas.", + "type": "object", + "definitions": { + "patternObjects": { + "semver": { + "$comment": "https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string with unused non-capturing group syntax removed.", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-((0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\\+([0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*))?$" + }, + "relaxedSemver": { + "$comment": "https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string adjusted to also allow MAJOR.MINOR, MAJOR, and with unused non-capturing group syntax removed. For details, see https://go.bug.st/relaxed-semver", + "pattern": "^(0|[1-9]\\d*)(\\.(0|[1-9]\\d*))?(\\.(0|[1-9]\\d*))?(-((0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\\+([0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*))?$" + } + } + } +} +`) + +func generalDefinitionsSchemaJsonBytes() ([]byte, error) { + return _generalDefinitionsSchemaJson, nil +} + +func generalDefinitionsSchemaJson() (*asset, error) { + bytes, err := generalDefinitionsSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "general-definitions-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "arduino-library-properties-definitions-schema.json": arduinoLibraryPropertiesDefinitionsSchemaJson, + "arduino-library-properties-permissive-schema.json": arduinoLibraryPropertiesPermissiveSchemaJson, + "arduino-library-properties-schema.json": arduinoLibraryPropertiesSchemaJson, + "arduino-library-properties-strict-schema.json": arduinoLibraryPropertiesStrictSchemaJson, + "general-definitions-schema.json": generalDefinitionsSchemaJson, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, 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{}}, + "arduino-library-properties-strict-schema.json": &bintree{arduinoLibraryPropertiesStrictSchemaJson, map[string]*bintree{}}, + "general-definitions-schema.json": &bintree{generalDefinitionsSchemaJson, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/check/checkdata/schema/schemadata/schemadata.go b/check/checkdata/schema/schemadata/schemadata.go new file mode 100644 index 000000000..c77d7481f --- /dev/null +++ b/check/checkdata/schema/schemadata/schemadata.go @@ -0,0 +1,2 @@ +// Package schemadata contains the encoded JSON schemas. +package schemadata diff --git a/check/checkdata/schema/testdata/bindata.go b/check/checkdata/schema/testdata/bindata.go new file mode 100644 index 000000000..ea4d08234 --- /dev/null +++ b/check/checkdata/schema/testdata/bindata.go @@ -0,0 +1,425 @@ +// Package testdata Code generated by go-bindata. (@generated) DO NOT EDIT. +// sources: +// check/checkdata/schema/testdata/input/invalid-schema.json +// check/checkdata/schema/testdata/input/referenced-schema-1.json +// check/checkdata/schema/testdata/input/referenced-schema-2.json +// check/checkdata/schema/testdata/input/schema-without-id.json +// check/checkdata/schema/testdata/input/valid-schema-with-references.json +// check/checkdata/schema/testdata/input/valid-schema.json +package testdata + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +// Name return file name +func (fi bindataFileInfo) Name() string { + return fi.name +} + +// Size return file size +func (fi bindataFileInfo) Size() int64 { + return fi.size +} + +// Mode return file mode +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} + +// Mode return file modify time +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} + +// IsDir return file whether a directory +func (fi bindataFileInfo) IsDir() bool { + return fi.mode&os.ModeDir != 0 +} + +// Sys return file is sys mode +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _invalidSchemaJson = []byte(`{ + "foo": "bar" + "baz": "bat" +} +`) + +func invalidSchemaJsonBytes() ([]byte, error) { + return _invalidSchemaJson, nil +} + +func invalidSchemaJson() (*asset, error) { + bytes, err := invalidSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "invalid-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _referencedSchema1Json = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-check/main/check/checkdata/schema/testdata/referenced-schema-1.json", + "title": "Schema for use in unit tests", + "definitions": { + "patternObject": { + "pattern": "^[a-z]+$" + }, + "requiredObject": { + "required": ["property1"] + } + } +} +`) + +func referencedSchema1JsonBytes() ([]byte, error) { + return _referencedSchema1Json, nil +} + +func referencedSchema1Json() (*asset, error) { + bytes, err := referencedSchema1JsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "referenced-schema-1.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _referencedSchema2Json = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-check/main/check/checkdata/schema/testdata/referenced-schema-2.json", + "title": "Schema for use in unit tests", + "definitions": { + "minLengthObject": { + "minLength": 2 + }, + "maxLengthObject": { + "maxLength": 4 + }, + "enumObject": { + "enum": ["baz"] + }, + "notPatternObject": { + "not": { + "allOf": [ + { + "pattern": "[A-Z]" + } + ] + } + }, + "misspelledOptionalProperties": { + "propertyNames": { + "not": { + "pattern": "porpert([y]|[ies])" + } + } + } + } +} +`) + +func referencedSchema2JsonBytes() ([]byte, error) { + return _referencedSchema2Json, nil +} + +func referencedSchema2Json() (*asset, error) { + bytes, err := referencedSchema2JsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "referenced-schema-2.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _schemaWithoutIdJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Schema without $id keyword for use in unit tests" +} +`) + +func schemaWithoutIdJsonBytes() ([]byte, error) { + return _schemaWithoutIdJson, nil +} + +func schemaWithoutIdJson() (*asset, error) { + bytes, err := schemaWithoutIdJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "schema-without-id.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _validSchemaWithReferencesJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-check/main/check/checkdata/schema/testdata/schema-with-references.json", + "title": "Schema for use in unit tests", + "type": "object", + "properties": { + "property1": { + "allOf": [ + { + "$ref": "referenced-schema-2.json#/definitions/minLengthObject" + }, + { + "$ref": "referenced-schema-2.json#/definitions/maxLengthObject" + } + ] + }, + "property2": { + "allOf": [ + { + "$ref": "referenced-schema-1.json#/definitions/patternObject" + } + ] + }, + "property3": { + "allOf": [ + { + "$ref": "referenced-schema-2.json#/definitions/enumObject" + }, + { + "$ref": "referenced-schema-2.json#/definitions/notPatternObject" + } + ] + } + }, + "allOf": [ + { + "$ref": "referenced-schema-1.json#/definitions/requiredObject" + }, + { + "$ref": "referenced-schema-2.json#/definitions/misspelledOptionalProperties" + } + ] +} +`) + +func validSchemaWithReferencesJsonBytes() ([]byte, error) { + return _validSchemaWithReferencesJson, nil +} + +func validSchemaWithReferencesJson() (*asset, error) { + bytes, err := validSchemaWithReferencesJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "valid-schema-with-references.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _validSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-check/main/check/checkdata/schema/testdata/schema-with-references.json", + "title": "Schema for use in unit tests", + "type": "object", + "properties": { + "property1": { + "minLength": 2 + } + } +} +`) + +func validSchemaJsonBytes() ([]byte, error) { + return _validSchemaJson, nil +} + +func validSchemaJson() (*asset, error) { + bytes, err := validSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "valid-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "invalid-schema.json": invalidSchemaJson, + "referenced-schema-1.json": referencedSchema1Json, + "referenced-schema-2.json": referencedSchema2Json, + "schema-without-id.json": schemaWithoutIdJson, + "valid-schema-with-references.json": validSchemaWithReferencesJson, + "valid-schema.json": validSchemaJson, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "invalid-schema.json": &bintree{invalidSchemaJson, map[string]*bintree{}}, + "referenced-schema-1.json": &bintree{referencedSchema1Json, map[string]*bintree{}}, + "referenced-schema-2.json": &bintree{referencedSchema2Json, map[string]*bintree{}}, + "schema-without-id.json": &bintree{schemaWithoutIdJson, map[string]*bintree{}}, + "valid-schema-with-references.json": &bintree{validSchemaWithReferencesJson, map[string]*bintree{}}, + "valid-schema.json": &bintree{validSchemaJson, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/check/checkdata/schema/testdata/invalid-schema.json b/check/checkdata/schema/testdata/input/invalid-schema.json similarity index 100% rename from check/checkdata/schema/testdata/invalid-schema.json rename to check/checkdata/schema/testdata/input/invalid-schema.json diff --git a/check/checkdata/schema/testdata/referenced-schema-1.json b/check/checkdata/schema/testdata/input/referenced-schema-1.json similarity index 100% rename from check/checkdata/schema/testdata/referenced-schema-1.json rename to check/checkdata/schema/testdata/input/referenced-schema-1.json diff --git a/check/checkdata/schema/testdata/referenced-schema-2.json b/check/checkdata/schema/testdata/input/referenced-schema-2.json similarity index 100% rename from check/checkdata/schema/testdata/referenced-schema-2.json rename to check/checkdata/schema/testdata/input/referenced-schema-2.json diff --git a/check/checkdata/schema/testdata/schema-without-id.json b/check/checkdata/schema/testdata/input/schema-without-id.json similarity index 100% rename from check/checkdata/schema/testdata/schema-without-id.json rename to check/checkdata/schema/testdata/input/schema-without-id.json diff --git a/check/checkdata/schema/testdata/valid-schema-with-references.json b/check/checkdata/schema/testdata/input/valid-schema-with-references.json similarity index 100% rename from check/checkdata/schema/testdata/valid-schema-with-references.json rename to check/checkdata/schema/testdata/input/valid-schema-with-references.json diff --git a/check/checkdata/schema/testdata/valid-schema.json b/check/checkdata/schema/testdata/input/valid-schema.json similarity index 100% rename from check/checkdata/schema/testdata/valid-schema.json rename to check/checkdata/schema/testdata/input/valid-schema.json diff --git a/check/checkdata/schema/testdata/testdata.go b/check/checkdata/schema/testdata/testdata.go new file mode 100644 index 000000000..475907dab --- /dev/null +++ b/check/checkdata/schema/testdata/testdata.go @@ -0,0 +1,2 @@ +// Package testdata contains the encoded JSON schemas for the tests of the schema package. +package testdata diff --git a/check/checkfunctions/checkfunctions_test.go b/check/checkfunctions/checkfunctions_test.go index 7c5b42f89..52e7adb92 100644 --- a/check/checkfunctions/checkfunctions_test.go +++ b/check/checkfunctions/checkfunctions_test.go @@ -54,7 +54,7 @@ func checkCheckFunction(checkFunction Type, testTables []checkFunctionTestTable, SuperprojectType: testTable.superProjectType, } - checkdata.Initialize(testProject, schemasPath) + checkdata.Initialize(testProject) result, output := checkFunction() assert.Equal(t, testTable.expectedCheckResult, result, testTable.testName) diff --git a/check/checkfunctions/library.go b/check/checkfunctions/library.go index 6f551911f..21c6c400a 100644 --- a/check/checkfunctions/library.go +++ b/check/checkfunctions/library.go @@ -28,7 +28,6 @@ import ( "github.com/arduino/arduino-check/check/checkdata/schema" "github.com/arduino/arduino-check/check/checkdata/schema/compliancelevel" "github.com/arduino/arduino-check/check/checkresult" - "github.com/arduino/arduino-check/configuration" "github.com/arduino/arduino-check/project/library" "github.com/arduino/arduino-check/project/sketch" "github.com/arduino/arduino-cli/arduino/libraries" @@ -118,7 +117,7 @@ func LibraryPropertiesNameFieldMissing() (result checkresult.Type, output string return checkresult.Skip, "Library has legacy format" } - if schema.RequiredPropertyMissing("name", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.RequiredPropertyMissing("name", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } return checkresult.Pass, "" @@ -134,7 +133,7 @@ func LibraryPropertiesNameFieldLTMinLength() (result checkresult.Type, output st return checkresult.NotRun, "Field not present" } - if schema.PropertyLessThanMinLength("name", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyLessThanMinLength("name", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } @@ -152,7 +151,7 @@ func LibraryPropertiesNameFieldGTMaxLength() (result checkresult.Type, output st return checkresult.NotRun, "Field not present" } - if schema.PropertyGreaterThanMaxLength("name", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyGreaterThanMaxLength("name", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, name } @@ -170,7 +169,7 @@ func LibraryPropertiesNameFieldGTRecommendedLength() (result checkresult.Type, o return checkresult.NotRun, "Field not present" } - if schema.PropertyGreaterThanMaxLength("name", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict], configuration.SchemasPath()) { + if schema.PropertyGreaterThanMaxLength("name", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict]) { return checkresult.Fail, name } @@ -188,7 +187,7 @@ func LibraryPropertiesNameFieldDisallowedCharacters() (result checkresult.Type, return checkresult.NotRun, "Field not present" } - if schema.ValidationErrorMatch("^#/name$", "/patternObjects/allowedCharacters", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.ValidationErrorMatch("^#/name$", "/patternObjects/allowedCharacters", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, name } @@ -206,7 +205,7 @@ func LibraryPropertiesNameFieldHasSpaces() (result checkresult.Type, output stri return checkresult.NotRun, "Field not present" } - if schema.ValidationErrorMatch("^#/name$", "/patternObjects/notContainsSpaces", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict], configuration.SchemasPath()) { + if schema.ValidationErrorMatch("^#/name$", "/patternObjects/notContainsSpaces", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict]) { return checkresult.Fail, name } @@ -224,7 +223,7 @@ func LibraryPropertiesNameFieldStartsWithArduino() (result checkresult.Type, out return checkresult.NotRun, "Field not present" } - if schema.ValidationErrorMatch("^#/name$", "/patternObjects/notStartsWithArduino", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.ValidationErrorMatch("^#/name$", "/patternObjects/notStartsWithArduino", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, name } @@ -259,7 +258,7 @@ func LibraryPropertiesNameFieldContainsArduino() (result checkresult.Type, outpu return checkresult.NotRun, "Field not present" } - if schema.ValidationErrorMatch("^#/name$", "/patternObjects/notContainsArduino", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict], configuration.SchemasPath()) { + if schema.ValidationErrorMatch("^#/name$", "/patternObjects/notContainsArduino", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict]) { return checkresult.Fail, name } @@ -277,7 +276,7 @@ func LibraryPropertiesNameFieldContainsLibrary() (result checkresult.Type, outpu return checkresult.NotRun, "Field not present" } - if schema.ValidationErrorMatch("^#/name$", "/patternObjects/notContainsSuperfluousTerms", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict], configuration.SchemasPath()) { + if schema.ValidationErrorMatch("^#/name$", "/patternObjects/notContainsSuperfluousTerms", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict]) { return checkresult.Fail, name } @@ -351,7 +350,7 @@ func LibraryPropertiesVersionFieldMissing() (result checkresult.Type, output str return checkresult.Skip, "Library has legacy format" } - if schema.RequiredPropertyMissing("version", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.RequiredPropertyMissing("version", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } return checkresult.Pass, "" @@ -368,7 +367,7 @@ func LibraryPropertiesVersionFieldNonRelaxedSemver() (result checkresult.Type, o return checkresult.NotRun, "Field not present" } - if schema.PropertyPatternMismatch("version", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyPatternMismatch("version", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, version } @@ -386,7 +385,7 @@ func LibraryPropertiesVersionFieldNonSemver() (result checkresult.Type, output s return checkresult.NotRun, "Field not present" } - if schema.PropertyPatternMismatch("version", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict], configuration.SchemasPath()) { + if schema.PropertyPatternMismatch("version", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Strict]) { return checkresult.Fail, version } @@ -496,7 +495,7 @@ func LibraryPropertiesAuthorFieldMissing() (result checkresult.Type, output stri return checkresult.Skip, "Library has legacy format" } - if schema.RequiredPropertyMissing("author", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.RequiredPropertyMissing("author", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } return checkresult.Pass, "" @@ -512,7 +511,7 @@ func LibraryPropertiesAuthorFieldLTMinLength() (result checkresult.Type, output return checkresult.NotRun, "Field not present" } - if schema.PropertyLessThanMinLength("author", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyLessThanMinLength("author", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } @@ -529,7 +528,7 @@ func LibraryPropertiesMaintainerFieldMissing() (result checkresult.Type, output return checkresult.Skip, "Library has legacy format" } - if schema.RequiredPropertyMissing("maintainer", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.RequiredPropertyMissing("maintainer", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } return checkresult.Pass, "" @@ -545,7 +544,7 @@ func LibraryPropertiesMaintainerFieldLTMinLength() (result checkresult.Type, out return checkresult.NotRun, "Field not present" } - if schema.PropertyLessThanMinLength("maintainer", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyLessThanMinLength("maintainer", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } @@ -563,7 +562,7 @@ func LibraryPropertiesMaintainerFieldStartsWithArduino() (result checkresult.Typ return checkresult.NotRun, "Field not present" } - if schema.ValidationErrorMatch("^#/maintainer$", "/patternObjects/notStartsWithArduino", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.ValidationErrorMatch("^#/maintainer$", "/patternObjects/notStartsWithArduino", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, maintainer } @@ -597,7 +596,7 @@ func LibraryPropertiesEmailFieldLTMinLength() (result checkresult.Type, output s return checkresult.Skip, "Field not present" } - if schema.PropertyLessThanMinLength("email", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyLessThanMinLength("email", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } @@ -619,7 +618,7 @@ func LibraryPropertiesEmailFieldStartsWithArduino() (result checkresult.Type, ou return checkresult.Skip, "Field not present" } - if schema.ValidationErrorMatch("^#/email$", "/patternObjects/notStartsWithArduino", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.ValidationErrorMatch("^#/email$", "/patternObjects/notStartsWithArduino", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, email } @@ -636,7 +635,7 @@ func LibraryPropertiesSentenceFieldMissing() (result checkresult.Type, output st return checkresult.Skip, "Library has legacy format" } - if schema.RequiredPropertyMissing("sentence", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.RequiredPropertyMissing("sentence", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } return checkresult.Pass, "" @@ -652,7 +651,7 @@ func LibraryPropertiesSentenceFieldLTMinLength() (result checkresult.Type, outpu return checkresult.NotRun, "Field not present" } - if schema.PropertyLessThanMinLength("sentence", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyLessThanMinLength("sentence", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } @@ -674,7 +673,7 @@ func LibraryPropertiesParagraphFieldMissing() (result checkresult.Type, output s return checkresult.Skip, "Library has legacy format" } - if schema.RequiredPropertyMissing("paragraph", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.RequiredPropertyMissing("paragraph", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } return checkresult.Pass, "" @@ -714,7 +713,7 @@ func LibraryPropertiesCategoryFieldMissing() (result checkresult.Type, output st return checkresult.Skip, "Library has legacy format" } - if schema.RequiredPropertyMissing("category", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.RequiredPropertyMissing("category", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } return checkresult.Pass, "" @@ -731,7 +730,7 @@ func LibraryPropertiesCategoryFieldInvalid() (result checkresult.Type, output st return checkresult.Skip, "Field not present" } - if schema.PropertyEnumMismatch("category", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyEnumMismatch("category", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, category } @@ -766,7 +765,7 @@ func LibraryPropertiesUrlFieldMissing() (result checkresult.Type, output string) return checkresult.Skip, "Library has legacy format" } - if schema.RequiredPropertyMissing("url", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.RequiredPropertyMissing("url", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } return checkresult.Pass, "" @@ -783,7 +782,7 @@ func LibraryPropertiesUrlFieldInvalid() (result checkresult.Type, output string) return checkresult.NotRun, "Field not present" } - if schema.ValidationErrorMatch("^#/url$", "/format$", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.ValidationErrorMatch("^#/url$", "/format$", "", "", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, url } @@ -824,7 +823,7 @@ func LibraryPropertiesArchitecturesFieldMissing() (result checkresult.Type, outp return checkresult.Skip, "Library has legacy format" } - if schema.RequiredPropertyMissing("architectures", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.RequiredPropertyMissing("architectures", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } return checkresult.Pass, "" @@ -840,7 +839,7 @@ func LibraryPropertiesArchitecturesFieldLTMinLength() (result checkresult.Type, return checkresult.Skip, "Field not present" } - if schema.PropertyLessThanMinLength("architectures", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyLessThanMinLength("architectures", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } @@ -858,7 +857,7 @@ func LibraryPropertiesDependsFieldDisallowedCharacters() (result checkresult.Typ return checkresult.Skip, "Field not present" } - if schema.PropertyPatternMismatch("depends", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyPatternMismatch("depends", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, depends } @@ -908,7 +907,7 @@ func LibraryPropertiesDotALinkageFieldInvalid() (result checkresult.Type, output return checkresult.Skip, "Field not present" } - if schema.PropertyEnumMismatch("dot_a_linkage", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyEnumMismatch("dot_a_linkage", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, dotALinkage } @@ -942,7 +941,7 @@ func LibraryPropertiesIncludesFieldLTMinLength() (result checkresult.Type, outpu return checkresult.Skip, "Field not present" } - if schema.PropertyLessThanMinLength("includes", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyLessThanMinLength("includes", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } @@ -1002,7 +1001,7 @@ func LibraryPropertiesPrecompiledFieldInvalid() (result checkresult.Type, output return checkresult.Skip, "Field not present" } - if schema.PropertyEnumMismatch("precompiled", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyEnumMismatch("precompiled", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, precompiled } @@ -1037,7 +1036,7 @@ func LibraryPropertiesLdflagsFieldLTMinLength() (result checkresult.Type, output return checkresult.Skip, "Field not present" } - if schema.PropertyLessThanMinLength("ldflags", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.PropertyLessThanMinLength("ldflags", checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } @@ -1050,7 +1049,7 @@ func LibraryPropertiesMisspelledOptionalField() (result checkresult.Type, output return checkresult.NotRun, "Library not loaded" } - if schema.MisspelledOptionalPropertyFound(checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification], configuration.SchemasPath()) { + if schema.MisspelledOptionalPropertyFound(checkdata.LibraryPropertiesSchemaValidationResult()[compliancelevel.Specification]) { return checkresult.Fail, "" } diff --git a/check/checkfunctions/library_test.go b/check/checkfunctions/library_test.go index 098bd8e0b..40b0aec1f 100644 --- a/check/checkfunctions/library_test.go +++ b/check/checkfunctions/library_test.go @@ -33,12 +33,10 @@ import ( ) var librariesTestDataPath *paths.Path -var schemasPath *paths.Path func init() { workingDirectory, _ := os.Getwd() librariesTestDataPath = paths.New(workingDirectory, "testdata", "libraries") - schemasPath = paths.New(workingDirectory, "..", "..", "etc", "schemas") } type libraryCheckFunctionTestTable struct { @@ -58,7 +56,7 @@ func checkLibraryCheckFunction(checkFunction Type, testTables []libraryCheckFunc SuperprojectType: projecttype.Library, } - checkdata.Initialize(testProject, schemasPath) + checkdata.Initialize(testProject) result, output := checkFunction() assert.Equal(t, testTable.expectedCheckResult, result, testTable.testName) diff --git a/check/checkfunctions/packageindex_test.go b/check/checkfunctions/packageindex_test.go index ea3d4bfdf..129a1df63 100644 --- a/check/checkfunctions/packageindex_test.go +++ b/check/checkfunctions/packageindex_test.go @@ -51,7 +51,7 @@ func checkPackageIndexCheckFunction(checkFunction Type, testTables []packageInde SuperprojectType: projecttype.PackageIndex, } - checkdata.Initialize(testProject, nil) + checkdata.Initialize(testProject) result, output := checkFunction() assert.Equal(t, testTable.expectedCheckResult, result, testTable.testName) diff --git a/check/checkfunctions/platform_test.go b/check/checkfunctions/platform_test.go index 673581615..87d90a445 100644 --- a/check/checkfunctions/platform_test.go +++ b/check/checkfunctions/platform_test.go @@ -54,7 +54,7 @@ func checkPlatformCheckFunction(checkFunction Type, testTables []platformCheckFu SuperprojectType: projecttype.Platform, } - checkdata.Initialize(testProject, nil) + checkdata.Initialize(testProject) result, output := checkFunction() assert.Equal(t, testTable.expectedCheckResult, result, testTable.testName) diff --git a/check/checkfunctions/sketch_test.go b/check/checkfunctions/sketch_test.go index c0cc8fe1e..935d0c141 100644 --- a/check/checkfunctions/sketch_test.go +++ b/check/checkfunctions/sketch_test.go @@ -52,7 +52,7 @@ func checkSketchCheckFunction(checkFunction Type, testTables []sketchCheckFuncti SuperprojectType: projecttype.Sketch, } - checkdata.Initialize(testProject, schemasPath) + checkdata.Initialize(testProject) result, output := checkFunction() assert.Equal(t, testTable.expectedCheckResult, result, testTable.testName) diff --git a/configuration/configuration.go b/configuration/configuration.go index 843c08e70..61924acf8 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -223,15 +223,6 @@ func TargetPaths() paths.PathList { return targetPaths } -// SchemasPath returns the path to the folder containing the JSON schemas. -func SchemasPath() *paths.Path { - executablePath, err := os.Executable() - if err != nil { - panic(err) - } - return paths.New(executablePath).Parent().Join("etc", "schemas") -} - func EnableLogging(enable bool) { if enable { logrus.SetOutput(defaultLogOutput) // Enable log output. diff --git a/go.mod b/go.mod index 6f859e0f3..eac503de1 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,6 @@ require ( github.com/xanzy/ssh-agent v0.3.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 - github.com/xeipuuv/gojsonschema v1.2.0 go.bug.st/relaxed-semver v0.0.0-20190922224835-391e10178d18 go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.14.0 // indirect golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 // indirect diff --git a/go.sum b/go.sum index f974dfb8b..a660a8be7 100644 --- a/go.sum +++ b/go.sum @@ -190,6 +190,7 @@ github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.1 h1:L6VrMUGZaMlNIMN8Hj+CHh4U9yodJE3FAt/rgvfaKvE= github.com/gliderlabs/ssh v0.3.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-bindata/go-bindata v3.1.1+incompatible h1:tR4f0e4VTO7LK6B2YWyAoVEzG9ByG1wrXB4TL9+jiYg= github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= @@ -1079,8 +1080,6 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMc github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= diff --git a/project/library/libraryproperties/libraryproperties.go b/project/library/libraryproperties/libraryproperties.go index ba2837c93..9c4a7b495 100644 --- a/project/library/libraryproperties/libraryproperties.go +++ b/project/library/libraryproperties/libraryproperties.go @@ -19,9 +19,9 @@ package libraryproperties import ( "github.com/arduino/arduino-check/check/checkdata/schema" "github.com/arduino/arduino-check/check/checkdata/schema/compliancelevel" + "github.com/arduino/arduino-check/check/checkdata/schema/schemadata" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" - "github.com/ory/jsonschema/v3" ) // Properties parses the library.properties from the given path and returns the data. @@ -29,26 +29,26 @@ func Properties(libraryPath *paths.Path) (*properties.Map, error) { return properties.SafeLoadFromPath(libraryPath.Join("library.properties")) } -var schemaObject = make(map[compliancelevel.Type]*jsonschema.Schema) +var schemaObject = make(map[compliancelevel.Type]schema.Schema) // Validate validates library.properties data against the JSON schema and returns a map of the result for each compliance level. -func Validate(libraryProperties *properties.Map, schemasPath *paths.Path) map[compliancelevel.Type]*jsonschema.ValidationError { +func Validate(libraryProperties *properties.Map) map[compliancelevel.Type]schema.ValidationResult { referencedSchemaFilenames := []string{ "general-definitions-schema.json", "arduino-library-properties-definitions-schema.json", } - var validationResults = make(map[compliancelevel.Type]*jsonschema.ValidationError) + var validationResults = make(map[compliancelevel.Type]schema.ValidationResult) - if schemaObject[compliancelevel.Permissive] == nil { // Only compile the schemas once. - schemaObject[compliancelevel.Permissive] = schema.Compile("arduino-library-properties-permissive-schema.json", referencedSchemaFilenames, schemasPath) - schemaObject[compliancelevel.Specification] = schema.Compile("arduino-library-properties-schema.json", referencedSchemaFilenames, schemasPath) - schemaObject[compliancelevel.Strict] = schema.Compile("arduino-library-properties-strict-schema.json", referencedSchemaFilenames, schemasPath) + if schemaObject[compliancelevel.Permissive].Compiled == nil { // Only compile the schemas once. + schemaObject[compliancelevel.Permissive] = schema.Compile("arduino-library-properties-permissive-schema.json", referencedSchemaFilenames, schemadata.Asset) + schemaObject[compliancelevel.Specification] = schema.Compile("arduino-library-properties-schema.json", referencedSchemaFilenames, schemadata.Asset) + schemaObject[compliancelevel.Strict] = schema.Compile("arduino-library-properties-strict-schema.json", referencedSchemaFilenames, schemadata.Asset) } - validationResults[compliancelevel.Permissive] = schema.Validate(libraryProperties, schemaObject[compliancelevel.Permissive], schemasPath) - validationResults[compliancelevel.Specification] = schema.Validate(libraryProperties, schemaObject[compliancelevel.Specification], schemasPath) - validationResults[compliancelevel.Strict] = schema.Validate(libraryProperties, schemaObject[compliancelevel.Strict], schemasPath) + 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]) return validationResults } diff --git a/project/library/libraryproperties/librarypropertiesschemas_test.go b/project/library/libraryproperties/librarypropertiesschemas_test.go index 440174555..c4320ba03 100644 --- a/project/library/libraryproperties/librarypropertiesschemas_test.go +++ b/project/library/libraryproperties/librarypropertiesschemas_test.go @@ -18,18 +18,13 @@ package libraryproperties_test import ( "fmt" - "os" "strings" "testing" "github.com/arduino/arduino-check/check/checkdata/schema" "github.com/arduino/arduino-check/check/checkdata/schema/compliancelevel" "github.com/arduino/arduino-check/project/library/libraryproperties" - "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" - "github.com/ory/jsonschema/v3" - "github.com/sirupsen/logrus" - "github.com/xeipuuv/gojsonschema" "github.com/stretchr/testify/assert" ) @@ -51,13 +46,6 @@ var validLibraryPropertiesMap = map[string]string{ "ldflags": "-lm", } -var schemasPath *paths.Path - -func init() { - workingPath, _ := os.Getwd() - schemasPath = paths.New(workingPath).Join("..", "..", "..", "etc", "schemas") -} - type propertyValueTestTable struct { testName string propertyValue string @@ -67,26 +55,26 @@ type propertyValueTestTable struct { func checkPropertyPatternMismatch(propertyName string, testTables []propertyValueTestTable, t *testing.T) { libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) - var validationResult map[compliancelevel.Type]*jsonschema.ValidationError + var validationResult map[compliancelevel.Type]schema.ValidationResult for _, testTable := range testTables { validationResult = changeValueUpdateValidationResult(propertyName, testTable.propertyValue, libraryProperties, validationResult) t.Run(fmt.Sprintf("%s (%s)", testTable.testName, testTable.complianceLevel), func(t *testing.T) { - testTable.assertion(t, schema.PropertyPatternMismatch(propertyName, validationResult[testTable.complianceLevel], schemasPath)) + testTable.assertion(t, schema.PropertyPatternMismatch(propertyName, validationResult[testTable.complianceLevel])) }) } } func checkPropertyEnumMismatch(propertyName string, testTables []propertyValueTestTable, t *testing.T) { libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) - var validationResult map[compliancelevel.Type]*jsonschema.ValidationError + var validationResult map[compliancelevel.Type]schema.ValidationResult for _, testTable := range testTables { validationResult = changeValueUpdateValidationResult(propertyName, testTable.propertyValue, libraryProperties, validationResult) t.Run(fmt.Sprintf("%s (%s)", testTable.testName, testTable.complianceLevel), func(t *testing.T) { - testTable.assertion(t, schema.PropertyEnumMismatch(propertyName, validationResult[testTable.complianceLevel], schemasPath)) + testTable.assertion(t, schema.PropertyEnumMismatch(propertyName, validationResult[testTable.complianceLevel])) }) } } @@ -101,63 +89,33 @@ type validationErrorTestTable struct { func checkValidationErrorMatch(propertyName string, testTables []validationErrorTestTable, t *testing.T) { libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) - var validationResult map[compliancelevel.Type]*jsonschema.ValidationError + var validationResult map[compliancelevel.Type]schema.ValidationResult for _, testTable := range testTables { validationResult = changeValueUpdateValidationResult(propertyName, testTable.propertyValue, libraryProperties, validationResult) t.Run(fmt.Sprintf("%s (%s)", testTable.testName, testTable.complianceLevel), func(t *testing.T) { - testTable.assertion(t, schema.ValidationErrorMatch("#/"+propertyName, testTable.schemaPointerQuery, "", "", validationResult[testTable.complianceLevel], schemasPath)) + testTable.assertion(t, schema.ValidationErrorMatch("#/"+propertyName, testTable.schemaPointerQuery, "", "", validationResult[testTable.complianceLevel])) }) } } -func changeValueUpdateValidationResult(propertyName string, propertyValue string, libraryProperties *properties.Map, validationResult map[compliancelevel.Type]*jsonschema.ValidationError) map[compliancelevel.Type]*jsonschema.ValidationError { +func changeValueUpdateValidationResult(propertyName string, propertyValue string, libraryProperties *properties.Map, validationResult map[compliancelevel.Type]schema.ValidationResult) map[compliancelevel.Type]schema.ValidationResult { if validationResult == nil || libraryProperties.Get(propertyName) != propertyValue { libraryProperties.Set(propertyName, propertyValue) - return libraryproperties.Validate(libraryProperties, schemasPath) + return libraryproperties.Validate(libraryProperties) } // No change to property, return the previous validationResult. return validationResult } -func TestCompile(t *testing.T) { - schemaLoader := gojsonschema.NewSchemaLoader() - schemaLoader.Validate = true // Enable meta-schema validation when schemas are added and compiled - - directoryListing, _ := schemasPath.ReadDir() - directoryListing.FilterOutDirs() - directoryListing.FilterSuffix(".json") - - referencedSchemaFilenames := []string{} - // Generate a list of referenced schemas - logrus.Trace("Discovering definition schemas:") - for _, schemaPath := range directoryListing { - if schemaPath.HasSuffix("definitions-schema.json") { - logrus.Trace(schemaPath) - referencedSchemaFilenames = append(referencedSchemaFilenames, schemaPath.Base()) - } - } - - // Compile the parent schemas - logrus.Trace("Validating schemas:") - for _, schemaPath := range directoryListing { - if !schemaPath.HasSuffix("definitions-schema.json") { - logrus.Trace(schemaPath) - assert.NotPanics(t, func() { - schema.Compile(schemaPath.Base(), referencedSchemaFilenames, schemasPath) - }) - } - } -} - func TestPropertiesValid(t *testing.T) { libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) - validationResult := libraryproperties.Validate(libraryProperties, schemasPath) - assert.Nil(t, validationResult[compliancelevel.Permissive]) - assert.Nil(t, validationResult[compliancelevel.Specification]) - assert.Nil(t, validationResult[compliancelevel.Strict]) + validationResult := libraryproperties.Validate(libraryProperties) + assert.Nil(t, validationResult[compliancelevel.Permissive].Result) + assert.Nil(t, validationResult[compliancelevel.Specification].Result) + assert.Nil(t, validationResult[compliancelevel.Strict].Result) } func TestPropertiesMinLength(t *testing.T) { @@ -200,7 +158,7 @@ func TestPropertiesMinLength(t *testing.T) { } libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) - var validationResult map[compliancelevel.Type]*jsonschema.ValidationError + var validationResult map[compliancelevel.Type]schema.ValidationResult // Test schema validation results with value length < minimum. for _, tt := range tests { @@ -213,12 +171,12 @@ func TestPropertiesMinLength(t *testing.T) { if !propertyExists || len(value) >= tt.minLength { libraryProperties = properties.NewFromHashmap(validLibraryPropertiesMap) libraryProperties.Set(tt.propertyName, strings.Repeat("a", tt.minLength-1)) - validationResult = libraryproperties.Validate(libraryProperties, schemasPath) + validationResult = libraryproperties.Validate(libraryProperties) } } t.Run(fmt.Sprintf("%s less than minimum length of %d (%s)", tt.propertyName, tt.minLength, tt.complianceLevel), func(t *testing.T) { - assertion(t, schema.PropertyLessThanMinLength(tt.propertyName, validationResult[tt.complianceLevel], schemasPath)) + assertion(t, schema.PropertyLessThanMinLength(tt.propertyName, validationResult[tt.complianceLevel])) }) } @@ -227,11 +185,11 @@ func TestPropertiesMinLength(t *testing.T) { if len(libraryProperties.Get(tt.propertyName)) < tt.minLength { libraryProperties = properties.NewFromHashmap(validLibraryPropertiesMap) libraryProperties.Set(tt.propertyName, strings.Repeat("a", tt.minLength)) - validationResult = libraryproperties.Validate(libraryProperties, schemasPath) + validationResult = libraryproperties.Validate(libraryProperties) } t.Run(fmt.Sprintf("%s at minimum length of %d (%s)", tt.propertyName, tt.minLength, tt.complianceLevel), func(t *testing.T) { - assert.False(t, schema.PropertyLessThanMinLength(tt.propertyName, validationResult[tt.complianceLevel], schemasPath)) + assert.False(t, schema.PropertyLessThanMinLength(tt.propertyName, validationResult[tt.complianceLevel])) }) } } @@ -248,18 +206,18 @@ func TestPropertiesMaxLength(t *testing.T) { } libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) - var validationResult map[compliancelevel.Type]*jsonschema.ValidationError + var validationResult map[compliancelevel.Type]schema.ValidationResult // Test schema validation results with value length > maximum. for _, tt := range tests { if len(libraryProperties.Get(tt.propertyName)) <= tt.maxLength { libraryProperties = properties.NewFromHashmap(validLibraryPropertiesMap) libraryProperties.Set(tt.propertyName, strings.Repeat("a", tt.maxLength+1)) - validationResult = libraryproperties.Validate(libraryProperties, schemasPath) + validationResult = libraryproperties.Validate(libraryProperties) } t.Run(fmt.Sprintf("%s greater than maximum length of %d (%s)", tt.propertyName, tt.maxLength, tt.complianceLevel), func(t *testing.T) { - assert.True(t, schema.PropertyGreaterThanMaxLength(tt.propertyName, validationResult[tt.complianceLevel], schemasPath)) + assert.True(t, schema.PropertyGreaterThanMaxLength(tt.propertyName, validationResult[tt.complianceLevel])) }) } @@ -268,11 +226,11 @@ func TestPropertiesMaxLength(t *testing.T) { if len(libraryProperties.Get(tt.propertyName)) > tt.maxLength { libraryProperties = properties.NewFromHashmap(validLibraryPropertiesMap) libraryProperties.Set(tt.propertyName, strings.Repeat("a", tt.maxLength)) - validationResult = libraryproperties.Validate(libraryProperties, schemasPath) + validationResult = libraryproperties.Validate(libraryProperties) } t.Run(fmt.Sprintf("%s at maximum length of %d (%s)", tt.propertyName, tt.maxLength, tt.complianceLevel), func(t *testing.T) { - assert.False(t, schema.PropertyGreaterThanMaxLength(tt.propertyName, validationResult[tt.complianceLevel], schemasPath)) + assert.False(t, schema.PropertyGreaterThanMaxLength(tt.propertyName, validationResult[tt.complianceLevel])) }) } } @@ -462,7 +420,7 @@ func TestPropertyNames(t *testing.T) { } libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) - var validationResult map[compliancelevel.Type]*jsonschema.ValidationError + var validationResult map[compliancelevel.Type]schema.ValidationResult for _, testTable := range testTables { _, removePropertyPresent := libraryProperties.GetOk(testTable.removePropertyName) @@ -471,11 +429,11 @@ func TestPropertyNames(t *testing.T) { libraryProperties = properties.NewFromHashmap(validLibraryPropertiesMap) libraryProperties.Remove(testTable.removePropertyName) libraryProperties.Set(testTable.addPropertyName, "foo") - validationResult = libraryproperties.Validate(libraryProperties, schemasPath) + validationResult = libraryproperties.Validate(libraryProperties) } t.Run(fmt.Sprintf("%s (%s)", testTable.testName, testTable.complianceLevel), func(t *testing.T) { - testTable.assertion(t, schema.MisspelledOptionalPropertyFound(validationResult[testTable.complianceLevel], schemasPath)) + testTable.assertion(t, schema.MisspelledOptionalPropertyFound(validationResult[testTable.complianceLevel])) }) } } @@ -544,18 +502,18 @@ func TestRequired(t *testing.T) { } libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) - var validationResult map[compliancelevel.Type]*jsonschema.ValidationError + var validationResult map[compliancelevel.Type]schema.ValidationResult for _, testTable := range testTables { _, propertyExists := libraryProperties.GetOk(testTable.propertyName) if propertyExists { libraryProperties = properties.NewFromHashmap(validLibraryPropertiesMap) libraryProperties.Remove(testTable.propertyName) - validationResult = libraryproperties.Validate(libraryProperties, schemasPath) + validationResult = libraryproperties.Validate(libraryProperties) } t.Run(fmt.Sprintf("%s (%s)", testTable.propertyName, testTable.complianceLevel), func(t *testing.T) { - testTable.assertion(t, schema.RequiredPropertyMissing(testTable.propertyName, validationResult[testTable.complianceLevel], schemasPath)) + testTable.assertion(t, schema.RequiredPropertyMissing(testTable.propertyName, validationResult[testTable.complianceLevel])) }) } } @@ -564,19 +522,19 @@ func TestPropertiesMaintainerOrEmailRequired(t *testing.T) { libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) libraryProperties.Remove("maintainer") libraryProperties.Set("email", "foo@example.com") - validationResult := libraryproperties.Validate(libraryProperties, schemasPath) + validationResult := libraryproperties.Validate(libraryProperties) assert.False( t, - schema.RequiredPropertyMissing("maintainer", validationResult[compliancelevel.Permissive], schemasPath), + schema.RequiredPropertyMissing("maintainer", validationResult[compliancelevel.Permissive]), "maintainer property is not required when email property is defined.", ) assert.True( t, - schema.RequiredPropertyMissing("maintainer", validationResult[compliancelevel.Specification], schemasPath), + schema.RequiredPropertyMissing("maintainer", validationResult[compliancelevel.Specification]), "maintainer property is unconditionally required.", ) assert.True(t, - schema.RequiredPropertyMissing("maintainer", validationResult[compliancelevel.Strict], schemasPath), + schema.RequiredPropertyMissing("maintainer", validationResult[compliancelevel.Strict]), "maintainer property is unconditionally required.", ) } From f8fa91a1bd900b672feef3028b7bae3437b69701 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 13 Dec 2020 02:05:30 -0800 Subject: [PATCH 2/3] Use equivalent commands for formatting and format check The previous `go fmt` command was not formatting the code under the testdata folder. --- Taskfile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index 8c154b38c..1dbadbd6a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -84,7 +84,7 @@ tasks: go:format: desc: Format Go code cmds: - - go fmt {{ default .DEFAULT_PACKAGES .PACKAGES }} + - gofmt -l -w {{ default .DEFAULT_PATHS .PATHS }} docs:check: desc: Lint and check formatting of documentation files From 3aa02c7642cfb70345a735a0f339651e3fa7920b Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 13 Dec 2020 02:13:51 -0800 Subject: [PATCH 3/3] Add `go generate` command to go:generate task The stringer tool is used to generate stringers for the "enums". --- Taskfile.yml | 2 ++ go.mod | 2 +- go.sum | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index 1dbadbd6a..6a7f9b085 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -18,6 +18,8 @@ tasks: - go get -u "github.com/go-bindata/go-bindata/...@v3.1.1" - go-bindata -nocompress -nometadata -o "./check/checkdata/schema/schemadata/bindata.go" --pkg schemadata --prefix "./etc/schemas/" "./etc/schemas/" - go-bindata -nocompress -nometadata -o "./check/checkdata/schema/testdata/bindata.go" --pkg testdata --prefix "./check/checkdata/schema/testdata/input/" "./check/checkdata/schema/testdata/input/" + - go get -u golang.org/x/tools/cmd/stringer@v0.0.0-20201211192254-72fbef54948b + - go generate ./... - task: go:format go:test-unit: diff --git a/go.mod b/go.mod index eac503de1..119387221 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d // indirect golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect golang.org/x/text v0.3.4 // indirect - golang.org/x/tools v0.0.0-20201210181237-6d345e82f1d8 // indirect + golang.org/x/tools v0.0.0-20201211192254-72fbef54948b // indirect google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc // indirect google.golang.org/grpc v1.34.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index a660a8be7..842fef1ac 100644 --- a/go.sum +++ b/go.sum @@ -1385,6 +1385,10 @@ golang.org/x/tools v0.0.0-20200203215610-ab391d50b528/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20201210181237-6d345e82f1d8 h1:Pg9rlomzMhg26WdvwSujvC1Pr2lxOfFfhnQ+/LQcjLw= golang.org/x/tools v0.0.0-20201210181237-6d345e82f1d8/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 h1:1Bs6RVeBFtLZ8Yi1Hk07DiOqzvwLD/4hln4iahvFlag= +golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201211192254-72fbef54948b h1:8fYBhX5ZQZtb7nVKo58TjndJwMM+cOB1xOnfjgH3uiY= +golang.org/x/tools v0.0.0-20201211192254-72fbef54948b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=