@@ -17,9 +17,12 @@ package libraries
17
17
18
18
import (
19
19
"encoding/json"
20
+ "os"
20
21
"testing"
22
+ "time"
21
23
22
24
paths "github.com/arduino/go-paths-helper"
25
+ "github.com/stretchr/testify/assert"
23
26
"github.com/stretchr/testify/require"
24
27
)
25
28
@@ -85,3 +88,89 @@ func TestLibrariesLoader(t *testing.T) {
85
88
require .True (t , lib .IsLegacy )
86
89
}
87
90
}
91
+
92
+ func TestSymlinkLoop (t * testing.T ) {
93
+ // Set up directory structure of test library.
94
+ libraryPath := paths .New ("testdata" , "TestLib" )
95
+ tempLibPath , err := paths .TempDir ().MkTempDir ("TestSymlinkLoop" )
96
+ require .NoError (t , err )
97
+ defer tempLibPath .RemoveAll () // Clean up after the test.
98
+ testLib := tempLibPath .Join ("TestLib" )
99
+ require .NoError (t , libraryPath .CopyDirTo (testLib ))
100
+ examplesPath := testLib .Join ("examples" )
101
+ require .NoError (t , examplesPath .Mkdir ())
102
+
103
+ // It's probably most friendly for contributors using Windows to create the symlinks needed for the test on demand.
104
+ err = os .Symlink (examplesPath .Join (".." ).String (), examplesPath .Join ("UpGoer1" ).String ())
105
+ require .NoError (t , err , "This test must be run as administrator on Windows to have symlink creation privilege." )
106
+ // It's necessary to have multiple symlinks to a parent directory to create the loop.
107
+ err = os .Symlink (examplesPath .Join (".." ).String (), examplesPath .Join ("UpGoer2" ).String ())
108
+ require .NoError (t , err )
109
+
110
+ // The failure condition is Load() never returning, testing for which requires setting up a timeout.
111
+ done := make (chan bool )
112
+ go func () {
113
+ _ , err = Load (testLib , User )
114
+ done <- true
115
+ }()
116
+
117
+ noHang := assert .Eventually (
118
+ t ,
119
+ func () bool {
120
+ select {
121
+ case <- done :
122
+ return true
123
+ default :
124
+ return false
125
+ }
126
+ },
127
+ 20 * time .Second ,
128
+ 10 * time .Millisecond ,
129
+ "Infinite symlink loop while loading library" ,
130
+ )
131
+ require .True (t , noHang )
132
+ require .Error (t , err )
133
+ }
134
+
135
+ func TestLegacySymlinkLoop (t * testing.T ) {
136
+ // Set up directory structure of test library.
137
+ libraryPath , err := paths .TempDir ().MkTempDir ("TestSymlinkLoop" )
138
+ defer libraryPath .RemoveAll () // Clean up after the test.
139
+ require .NoError (t , err )
140
+ err = libraryPath .Join ("TestSymlinkLoop.h" ).WriteFile ([]byte {})
141
+ require .NoError (t , err )
142
+ examplesPath := libraryPath .Join ("examples" )
143
+ err = examplesPath .Mkdir ()
144
+ require .NoError (t , err )
145
+
146
+ // It's probably most friendly for contributors using Windows to create the symlinks needed for the test on demand.
147
+ err = os .Symlink (examplesPath .Join (".." ).String (), examplesPath .Join ("UpGoer1" ).String ())
148
+ require .NoError (t , err , "This test must be run as administrator on Windows to have symlink creation privilege." )
149
+ // It's necessary to have multiple symlinks to a parent directory to create the loop.
150
+ err = os .Symlink (examplesPath .Join (".." ).String (), examplesPath .Join ("UpGoer2" ).String ())
151
+ require .NoError (t , err )
152
+
153
+ // The failure condition is Load() never returning, testing for which requires setting up a timeout.
154
+ done := make (chan bool )
155
+ go func () {
156
+ _ , err = Load (libraryPath , User )
157
+ done <- true
158
+ }()
159
+
160
+ noHang := assert .Eventually (
161
+ t ,
162
+ func () bool {
163
+ select {
164
+ case <- done :
165
+ return true
166
+ default :
167
+ return false
168
+ }
169
+ },
170
+ 20 * time .Second ,
171
+ 10 * time .Millisecond ,
172
+ "Infinite symlink loop while loading library" ,
173
+ )
174
+ require .True (t , noHang )
175
+ require .Error (t , err )
176
+ }
0 commit comments