From a760b7c7dee1c7ea623763138f34cbdd44290c57 Mon Sep 17 00:00:00 2001 From: Matteo Pologruto Date: Wed, 12 Apr 2023 09:45:44 +0200 Subject: [PATCH 1/2] Improve library examples loading process by filtering out unneeded directories --- arduino/libraries/loader.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/arduino/libraries/loader.go b/arduino/libraries/loader.go index ec3d1dc164a..9af4b4fbf8e 100644 --- a/arduino/libraries/loader.go +++ b/arduino/libraries/loader.go @@ -20,7 +20,6 @@ import ( "strings" "github.com/arduino/arduino-cli/arduino/globals" - "github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" "github.com/pkg/errors" @@ -176,20 +175,11 @@ func addExamples(lib *Library) error { } func addExamplesToPathList(examplesPath *paths.Path, list *paths.PathList) error { - files, err := examplesPath.ReadDir() + files, err := examplesPath.ReadDirRecursiveFiltered(nil, paths.AndFilter(paths.FilterDirectories(), filterExamplesDirs)) if err != nil { return err } - for _, file := range files { - _, err := sketch.New(file) - if err == nil { - list.Add(file) - } else if file.IsDir() { - if err := addExamplesToPathList(file, list); err != nil { - return err - } - } - } + list.AddAll(files) return nil } @@ -208,3 +198,17 @@ func containsHeaderFile(d *paths.Path) (bool, error) { dirContent.FilterSuffix(headerExtensions...) return len(dirContent) > 0, nil } + +// filterExamplesDirs filters out any directory that does not contain a ".ino" or ".pde" file +// with the correct casing +func filterExamplesDirs(dir *paths.Path) bool { + if list, err := dir.ReadDir(); err == nil { + list.FilterOutDirs() + list.FilterPrefix(dir.Base()+".ino", dir.Base()+".pde") + // accept only directories that contain a single correct sketch + if list.Len() == 1 { + return true + } + } + return false +} From a34f57d34710caff44e1f0923202d4da3e86a32e Mon Sep 17 00:00:00 2001 From: Matteo Pologruto Date: Wed, 12 Apr 2023 11:17:42 +0200 Subject: [PATCH 2/2] Add tests for the changes --- arduino/libraries/libraries_test.go | 68 +++++++++++++++++++ .../examples/MultipleFiles/MultipleFiles.ino | 0 .../examples/MultipleFiles/MultipleFiles.pde | 0 .../examples/WrongCasing/wrongCasing.ino | 0 .../examples/simple/simple.ino | 0 .../TestLibExamples/library.properties | 9 +++ .../testdata/TestLibExamples/src/TestLib.h | 0 7 files changed, 77 insertions(+) create mode 100644 arduino/libraries/testdata/TestLibExamples/examples/MultipleFiles/MultipleFiles.ino create mode 100644 arduino/libraries/testdata/TestLibExamples/examples/MultipleFiles/MultipleFiles.pde create mode 100644 arduino/libraries/testdata/TestLibExamples/examples/WrongCasing/wrongCasing.ino create mode 100644 arduino/libraries/testdata/TestLibExamples/examples/simple/simple.ino create mode 100644 arduino/libraries/testdata/TestLibExamples/library.properties create mode 100644 arduino/libraries/testdata/TestLibExamples/src/TestLib.h diff --git a/arduino/libraries/libraries_test.go b/arduino/libraries/libraries_test.go index 35c2a40ddfa..ab9c5f129fa 100644 --- a/arduino/libraries/libraries_test.go +++ b/arduino/libraries/libraries_test.go @@ -17,7 +17,9 @@ package libraries import ( "encoding/json" + "os" "testing" + "time" paths "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" @@ -85,3 +87,69 @@ func TestLibrariesLoader(t *testing.T) { require.True(t, lib.IsLegacy) } } + +func TestSymlinkLoop(t *testing.T) { + // Set up directory structure of test library. + testLib := paths.New("testdata", "TestLib") + examplesPath := testLib.Join("examples") + require.NoError(t, examplesPath.Mkdir()) + defer examplesPath.RemoveAll() + + // It's probably most friendly for contributors using Windows to create the symlinks needed for the test on demand. + err := os.Symlink(examplesPath.Join("..").String(), examplesPath.Join("UpGoer1").String()) + require.NoError(t, err, "This test must be run as administrator on Windows to have symlink creation privilege.") + // It's necessary to have multiple symlinks to a parent directory to create the loop. + err = os.Symlink(examplesPath.Join("..").String(), examplesPath.Join("UpGoer2").String()) + require.NoError(t, err) + + // The failure condition is Load() never returning, testing for which requires setting up a timeout. + done := make(chan bool) + go func() { + _, err = Load(testLib, User) + done <- true + }() + select { + case <-done: + case <-time.After(2 * time.Second): + require.FailNow(t, "Load didn't complete in the allocated time.") + } + require.Error(t, err) +} + +func TestLegacySymlinkLoop(t *testing.T) { + // Set up directory structure of test library. + testLib := paths.New("testdata", "LegacyLib") + examplesPath := testLib.Join("examples") + require.NoError(t, examplesPath.Mkdir()) + defer examplesPath.RemoveAll() + + // It's probably most friendly for contributors using Windows to create the symlinks needed for the test on demand. + err := os.Symlink(examplesPath.Join("..").String(), examplesPath.Join("UpGoer1").String()) + require.NoError(t, err, "This test must be run as administrator on Windows to have symlink creation privilege.") + // It's necessary to have multiple symlinks to a parent directory to create the loop. + err = os.Symlink(examplesPath.Join("..").String(), examplesPath.Join("UpGoer2").String()) + require.NoError(t, err) + + // The failure condition is Load() never returning, testing for which requires setting up a timeout. + done := make(chan bool) + go func() { + _, err = Load(testLib, User) + done <- true + }() + + select { + case <-done: + case <-time.After(2 * time.Second): + require.FailNow(t, "Load didn't complete in the allocated time.") + } + require.Error(t, err) +} + +func TestLoadExamples(t *testing.T) { + example, err := paths.New(".", "testdata", "TestLibExamples", "examples", "simple").Abs() + require.NoError(t, err) + lib, err := Load(paths.New("testdata", "TestLibExamples"), User) + require.NoError(t, err) + require.Len(t, lib.Examples, 1) + require.True(t, lib.Examples.Contains(example)) +} diff --git a/arduino/libraries/testdata/TestLibExamples/examples/MultipleFiles/MultipleFiles.ino b/arduino/libraries/testdata/TestLibExamples/examples/MultipleFiles/MultipleFiles.ino new file mode 100644 index 00000000000..e69de29bb2d diff --git a/arduino/libraries/testdata/TestLibExamples/examples/MultipleFiles/MultipleFiles.pde b/arduino/libraries/testdata/TestLibExamples/examples/MultipleFiles/MultipleFiles.pde new file mode 100644 index 00000000000..e69de29bb2d diff --git a/arduino/libraries/testdata/TestLibExamples/examples/WrongCasing/wrongCasing.ino b/arduino/libraries/testdata/TestLibExamples/examples/WrongCasing/wrongCasing.ino new file mode 100644 index 00000000000..e69de29bb2d diff --git a/arduino/libraries/testdata/TestLibExamples/examples/simple/simple.ino b/arduino/libraries/testdata/TestLibExamples/examples/simple/simple.ino new file mode 100644 index 00000000000..e69de29bb2d diff --git a/arduino/libraries/testdata/TestLibExamples/library.properties b/arduino/libraries/testdata/TestLibExamples/library.properties new file mode 100644 index 00000000000..030d3472039 --- /dev/null +++ b/arduino/libraries/testdata/TestLibExamples/library.properties @@ -0,0 +1,9 @@ +name=TestLib +version=1.0.3 +author=Arduino +maintainer=Arduino +sentence=A test lib +paragraph=very powerful! +category=Device Control +url=http://www.arduino.cc/en/Reference/TestLib +architectures=avr \ No newline at end of file diff --git a/arduino/libraries/testdata/TestLibExamples/src/TestLib.h b/arduino/libraries/testdata/TestLibExamples/src/TestLib.h new file mode 100644 index 00000000000..e69de29bb2d