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..6a7f9b085 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -12,6 +12,16 @@ 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/" + - go get -u golang.org/x/tools/cmd/stringer@v0.0.0-20201211192254-72fbef54948b + - go generate ./... + - task: go:format + go:test-unit: desc: Run unit tests cmds: @@ -76,7 +86,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 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..119387221 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 @@ -54,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 f974dfb8b..842fef1ac 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= @@ -1386,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= 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.", ) }