Skip to content

Commit 33b7ac0

Browse files
authored
Merge pull request #28 from arduino/per1234/library-index-checks
Add Library Manager index-related checks
2 parents 2c5f5af + 3fdb72d commit 33b7ac0

File tree

18 files changed

+320
-6
lines changed

18 files changed

+320
-6
lines changed

check/check.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import (
3535
func RunChecks(project project.Type) {
3636
fmt.Printf("Checking %s in %s\n", project.ProjectType, project.Path)
3737

38-
checkdata.Initialize(project)
38+
checkdata.Initialize(project, configuration.SchemasPath())
3939

4040
for _, checkConfiguration := range checkconfigurations.Configurations() {
4141
runCheck, err := shouldRun(checkConfiguration, project)

check/checkconfigurations/checkconfigurations.go

+45
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,36 @@ var configurations = []Type{
101101
ErrorModes: []checkmode.Type{checkmode.All},
102102
CheckFunction: checkfunctions.LibraryPropertiesNameFieldDisallowedCharacters,
103103
},
104+
{
105+
ProjectType: projecttype.Library,
106+
Category: "library.properties",
107+
Subcategory: "name field",
108+
ID: "LP005",
109+
Brief: "duplicate name",
110+
Description: "This requirement only applies to the library.properties name value. There is no requirement to change the repository or header file names.",
111+
MessageTemplate: "Library name {{.}} is in use by a library in the Library Manager index. Each library must have a unique name value.",
112+
DisableModes: []checkmode.Type{checkmode.LibraryManagerIndexed},
113+
EnableModes: []checkmode.Type{checkmode.All},
114+
InfoModes: nil,
115+
WarningModes: []checkmode.Type{checkmode.All},
116+
ErrorModes: []checkmode.Type{checkmode.LibraryManagerSubmission},
117+
CheckFunction: checkfunctions.LibraryPropertiesNameFieldDuplicate,
118+
},
119+
{
120+
ProjectType: projecttype.Library,
121+
Category: "library.properties",
122+
Subcategory: "name field",
123+
ID: "LP006",
124+
Brief: "not in LM index",
125+
Description: "The name value is the identifier used to install the library and define dependencies, so it should not be changed.",
126+
MessageTemplate: "Library name {{.}} not found in the Library Manager index. Library names are not allowed to change after being added to the index. See: https://github.com/arduino/Arduino/wiki/Library-Manager-FAQ#how-can-i-change-my-librarys-name",
127+
DisableModes: []checkmode.Type{checkmode.Default},
128+
EnableModes: []checkmode.Type{checkmode.LibraryManagerIndexed},
129+
InfoModes: nil,
130+
WarningModes: nil,
131+
ErrorModes: []checkmode.Type{checkmode.All},
132+
CheckFunction: checkfunctions.LibraryPropertiesNameFieldNotInIndex,
133+
},
104134
{
105135
ProjectType: projecttype.Library,
106136
Category: "library.properties",
@@ -116,6 +146,21 @@ var configurations = []Type{
116146
ErrorModes: []checkmode.Type{checkmode.All},
117147
CheckFunction: checkfunctions.LibraryPropertiesVersionFieldMissing,
118148
},
149+
{
150+
ProjectType: projecttype.Library,
151+
Category: "library.properties",
152+
Subcategory: "depends field",
153+
ID: "LP012",
154+
Brief: "Dependency not in index",
155+
Description: "",
156+
MessageTemplate: "library.properties depends field item(s) {{.}} not found in the Library Manager index.",
157+
DisableModes: nil,
158+
EnableModes: []checkmode.Type{checkmode.All},
159+
InfoModes: nil,
160+
WarningModes: []checkmode.Type{checkmode.All},
161+
ErrorModes: nil,
162+
CheckFunction: checkfunctions.LibraryPropertiesDependsFieldNotInIndex,
163+
},
119164
{
120165
ProjectType: projecttype.Sketch,
121166
Category: "structure",

check/checkdata/checkdata.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ import (
2626
)
2727

2828
// Initialize gathers the check data for the specified project.
29-
func Initialize(project project.Type) {
29+
func Initialize(project project.Type, schemasPath *paths.Path) {
3030
projectType = project.ProjectType
3131
projectPath = project.Path
3232
switch project.ProjectType {
3333
case projecttype.Sketch:
3434
case projecttype.Library:
35-
InitializeForLibrary(project)
35+
InitializeForLibrary(project, schemasPath)
3636
case projecttype.Platform:
3737
case projecttype.PackageIndex:
3838
}

check/checkdata/library.go

+38-3
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,50 @@
1616
package checkdata
1717

1818
import (
19+
"encoding/json"
20+
"io/ioutil"
21+
"net/http"
22+
"os"
23+
1924
"github.com/arduino/arduino-check/check/checkdata/schema/compliancelevel"
20-
"github.com/arduino/arduino-check/configuration"
2125
"github.com/arduino/arduino-check/project"
2226
"github.com/arduino/arduino-check/project/library/libraryproperties"
27+
"github.com/arduino/arduino-check/result/feedback"
28+
"github.com/arduino/go-paths-helper"
2329
"github.com/arduino/go-properties-orderedmap"
2430
"github.com/ory/jsonschema/v3"
31+
"github.com/sirupsen/logrus"
2532
)
2633

2734
// Initialize gathers the library check data for the specified project.
28-
func InitializeForLibrary(project project.Type) {
35+
func InitializeForLibrary(project project.Type, schemasPath *paths.Path) {
2936
libraryProperties, libraryPropertiesLoadError = libraryproperties.Properties(project.Path)
3037
if libraryPropertiesLoadError != nil {
38+
logrus.Errorf("Error loading library.properties from %s: %s", project.Path, libraryPropertiesLoadError)
3139
// TODO: can I even do this?
3240
libraryPropertiesSchemaValidationResult = nil
3341
} else {
34-
libraryPropertiesSchemaValidationResult = libraryproperties.Validate(libraryProperties, configuration.SchemasPath())
42+
libraryPropertiesSchemaValidationResult = libraryproperties.Validate(libraryProperties, schemasPath)
43+
}
44+
45+
if libraryManagerIndex == nil { // Only download the Library Manager index once
46+
url := "http://downloads.arduino.cc/libraries/library_index.json"
47+
httpResponse, err := http.Get(url)
48+
if err != nil {
49+
feedback.Errorf("%s Unable to download Library Manager index from %s", err, url)
50+
os.Exit(1)
51+
}
52+
defer httpResponse.Body.Close()
53+
54+
bytes, err := ioutil.ReadAll(httpResponse.Body)
55+
if err != nil {
56+
panic(err)
57+
}
58+
59+
err = json.Unmarshal(bytes, &libraryManagerIndex)
60+
if err != nil {
61+
panic(err)
62+
}
3563
}
3664
}
3765

@@ -55,3 +83,10 @@ var libraryPropertiesSchemaValidationResult map[compliancelevel.Type]*jsonschema
5583
func LibraryPropertiesSchemaValidationResult() map[compliancelevel.Type]*jsonschema.ValidationError {
5684
return libraryPropertiesSchemaValidationResult
5785
}
86+
87+
var libraryManagerIndex map[string]interface{}
88+
89+
// LibraryManagerIndex returns the Library Manager index data.
90+
func LibraryManagerIndex() map[string]interface{} {
91+
return libraryManagerIndex
92+
}

check/checkfunctions/library.go

+83
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ package checkfunctions
1818
// The check functions for libraries.
1919

2020
import (
21+
"strings"
22+
2123
"github.com/arduino/arduino-check/check/checkdata"
2224
"github.com/arduino/arduino-check/check/checkdata/schema"
2325
"github.com/arduino/arduino-check/check/checkdata/schema/compliancelevel"
2426
"github.com/arduino/arduino-check/check/checkresult"
2527
"github.com/arduino/arduino-check/configuration"
28+
"github.com/arduino/go-properties-orderedmap"
29+
"github.com/sirupsen/logrus"
2630
)
2731

2832
// LibraryPropertiesFormat checks for invalid library.properties format.
@@ -58,6 +62,42 @@ func LibraryPropertiesNameFieldDisallowedCharacters() (result checkresult.Type,
5862
return checkresult.Pass, ""
5963
}
6064

65+
// LibraryPropertiesNameFieldDuplicate checks whether there is an existing entry in the Library Manager index using the the library.properties `name` value.
66+
func LibraryPropertiesNameFieldDuplicate() (result checkresult.Type, output string) {
67+
if checkdata.LibraryPropertiesLoadError() != nil {
68+
return checkresult.NotRun, ""
69+
}
70+
71+
name, hasName := checkdata.LibraryProperties().GetOk("name")
72+
if !hasName {
73+
return checkresult.NotRun, ""
74+
}
75+
76+
if nameInLibraryManagerIndex(name) {
77+
return checkresult.Fail, name
78+
}
79+
80+
return checkresult.Pass, ""
81+
}
82+
83+
// LibraryPropertiesNameFieldNotInIndex checks whether there is no existing entry in the Library Manager index using the the library.properties `name` value.
84+
func LibraryPropertiesNameFieldNotInIndex() (result checkresult.Type, output string) {
85+
if checkdata.LibraryPropertiesLoadError() != nil {
86+
return checkresult.NotRun, ""
87+
}
88+
89+
name, hasName := checkdata.LibraryProperties().GetOk("name")
90+
if !hasName {
91+
return checkresult.NotRun, ""
92+
}
93+
94+
if nameInLibraryManagerIndex(name) {
95+
return checkresult.Pass, ""
96+
}
97+
98+
return checkresult.Fail, name
99+
}
100+
61101
// LibraryPropertiesVersionFieldMissing checks for missing library.properties "version" field.
62102
func LibraryPropertiesVersionFieldMissing() (result checkresult.Type, output string) {
63103
if checkdata.LibraryPropertiesLoadError() != nil {
@@ -69,3 +109,46 @@ func LibraryPropertiesVersionFieldMissing() (result checkresult.Type, output str
69109
}
70110
return checkresult.Pass, ""
71111
}
112+
113+
// LibraryPropertiesDependsFieldNotInIndex checks whether the libraries listed in the library.properties `depends` field are in the Library Manager index.
114+
func LibraryPropertiesDependsFieldNotInIndex() (result checkresult.Type, output string) {
115+
if checkdata.LibraryPropertiesLoadError() != nil {
116+
return checkresult.NotRun, ""
117+
}
118+
119+
depends, hasDepends := checkdata.LibraryProperties().GetOk("depends")
120+
if !hasDepends {
121+
return checkresult.NotRun, ""
122+
}
123+
124+
dependencies, err := properties.SplitQuotedString(depends, "", false)
125+
if err != nil {
126+
panic(err)
127+
}
128+
dependenciesNotInIndex := []string{}
129+
for _, dependency := range dependencies {
130+
logrus.Tracef("Checking if dependency %s is in index.", dependency)
131+
if !nameInLibraryManagerIndex(dependency) {
132+
dependenciesNotInIndex = append(dependenciesNotInIndex, dependency)
133+
}
134+
}
135+
136+
if len(dependenciesNotInIndex) > 0 {
137+
return checkresult.Fail, strings.Join(dependenciesNotInIndex, ", ")
138+
}
139+
140+
return checkresult.Pass, ""
141+
}
142+
143+
// nameInLibraryManagerIndex returns whether there is a library in Library Manager index using the given name.
144+
func nameInLibraryManagerIndex(name string) bool {
145+
libraries := checkdata.LibraryManagerIndex()["libraries"].([]interface{})
146+
for _, libraryInterface := range libraries {
147+
library := libraryInterface.(map[string]interface{})
148+
if library["name"].(string) == name {
149+
return true
150+
}
151+
}
152+
153+
return false
154+
}

check/checkfunctions/library_test.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// This file is part of arduino-check.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-check.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package checkfunctions
17+
18+
import (
19+
"os"
20+
"regexp"
21+
"testing"
22+
23+
"github.com/arduino/arduino-check/check/checkdata"
24+
"github.com/arduino/arduino-check/check/checkresult"
25+
"github.com/arduino/arduino-check/project"
26+
"github.com/arduino/arduino-check/project/projecttype"
27+
"github.com/arduino/go-paths-helper"
28+
"github.com/stretchr/testify/assert"
29+
)
30+
31+
var testDataPath *paths.Path
32+
var schemasPath *paths.Path
33+
34+
func init() {
35+
workingDirectory, _ := os.Getwd()
36+
testDataPath = paths.New(workingDirectory, "testdata", "libraries")
37+
schemasPath = paths.New(workingDirectory, "..", "..", "etc", "schemas")
38+
}
39+
40+
type checkFunctionTestTable struct {
41+
testName string
42+
libraryFolderName string
43+
expectedCheckResult checkresult.Type
44+
expectedOutputQuery string
45+
}
46+
47+
func checkCheckFunction(checkFunction Type, testTables []checkFunctionTestTable, t *testing.T) {
48+
for _, testTable := range testTables {
49+
expectedOutputRegexp := regexp.MustCompile(testTable.expectedOutputQuery)
50+
51+
testProject := project.Type{
52+
Path: testDataPath.Join(testTable.libraryFolderName),
53+
ProjectType: projecttype.Library,
54+
SuperprojectType: projecttype.Library,
55+
}
56+
57+
checkdata.Initialize(testProject, schemasPath)
58+
59+
result, output := checkFunction()
60+
assert.Equal(t, testTable.expectedCheckResult, result, testTable.testName)
61+
assert.True(t, expectedOutputRegexp.MatchString(output), testTable.testName)
62+
}
63+
}
64+
65+
func TestLibraryPropertiesNameFieldDuplicate(t *testing.T) {
66+
testTables := []checkFunctionTestTable{
67+
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
68+
{"Duplicate", "Indexed", checkresult.Fail, "^Servo$"},
69+
{"Not duplicate", "NotIndexed", checkresult.Pass, ""},
70+
}
71+
72+
checkCheckFunction(LibraryPropertiesNameFieldDuplicate, testTables, t)
73+
}
74+
75+
func TestLibraryPropertiesNameFieldNotInIndex(t *testing.T) {
76+
testTables := []checkFunctionTestTable{
77+
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
78+
{"In index", "Indexed", checkresult.Pass, ""},
79+
{"Not in index", "NotIndexed", checkresult.Fail, "^NotIndexed$"},
80+
}
81+
82+
checkCheckFunction(LibraryPropertiesNameFieldNotInIndex, testTables, t)
83+
}
84+
85+
func TestLibraryPropertiesDependsFieldNotInIndex(t *testing.T) {
86+
testTables := []checkFunctionTestTable{
87+
{"Unable to load", "InvalidLibraryProperties", checkresult.NotRun, ""},
88+
{"Dependency not in index", "DependsNotIndexed", checkresult.Fail, "^NotIndexed$"},
89+
{"Dependency in index", "DependsIndexed", checkresult.Pass, ""},
90+
{"No depends", "NoDepends", checkresult.NotRun, ""},
91+
}
92+
93+
checkCheckFunction(LibraryPropertiesDependsFieldNotInIndex, testTables, t)
94+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=DependsIndexed
2+
version=1.0.0
3+
author=Cristian Maglie <[email protected]>, Pippo Pluto <[email protected]>
4+
maintainer=Cristian Maglie <[email protected]>
5+
sentence=A library that makes coding a web server a breeze.
6+
paragraph=Supports HTTP1.1 and you can do GET and POST.
7+
category=Communication
8+
url=http://example.com/
9+
architectures=avr
10+
depends=Servo

check/checkfunctions/testdata/libraries/DependsIndexed/src/DependsIndexed.h

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=DependsNotIndexed
2+
version=1.0.0
3+
author=Cristian Maglie <[email protected]>, Pippo Pluto <[email protected]>
4+
maintainer=Cristian Maglie <[email protected]>
5+
sentence=A library that makes coding a web server a breeze.
6+
paragraph=Supports HTTP1.1 and you can do GET and POST.
7+
category=Communication
8+
url=http://example.com/
9+
architectures=avr
10+
depends=NotIndexed

check/checkfunctions/testdata/libraries/DependsNotIndexed/src/DependsNotIndexed.h

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name=Servo
2+
version=1.0.0
3+
author=Cristian Maglie <[email protected]>, Pippo Pluto <[email protected]>
4+
maintainer=Cristian Maglie <[email protected]>
5+
sentence=A library that makes coding a web server a breeze.
6+
paragraph=Supports HTTP1.1 and you can do GET and POST.
7+
category=Communication
8+
url=http://example.com/
9+
architectures=avr

check/checkfunctions/testdata/libraries/Indexed/src/Indexed.h

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
This makes the format invalid
2+
name=InvalidLibraryProperties
3+
version=1.0.0
4+
author=Cristian Maglie <[email protected]>, Pippo Pluto <[email protected]>
5+
maintainer=Cristian Maglie <[email protected]>
6+
sentence=A library that makes coding a web server a breeze.
7+
paragraph=Supports HTTP1.1 and you can do GET and POST.
8+
category=Communication
9+
url=http://example.com/
10+
architectures=avr

check/checkfunctions/testdata/libraries/InvalidLibraryProperties/src/InvalidLibraryProperties.h

Whitespace-only changes.

0 commit comments

Comments
 (0)