1616package bricks
1717
1818import (
19+ "os"
20+ "path/filepath"
1921 "testing"
2022
2123 "github.com/arduino/go-paths-helper"
@@ -24,6 +26,9 @@ import (
2426
2527 "github.com/arduino/arduino-app-cli/internal/orchestrator/app"
2628 "github.com/arduino/arduino-app-cli/internal/orchestrator/bricksindex"
29+ "github.com/arduino/arduino-app-cli/internal/orchestrator/config"
30+ "github.com/arduino/arduino-app-cli/internal/orchestrator/modelsindex"
31+ "github.com/arduino/arduino-app-cli/internal/store"
2732)
2833
2934func TestBrickCreate (t * testing.T ) {
@@ -190,3 +195,169 @@ func TestGetBrickInstanceVariableDetails(t *testing.T) {
190195 })
191196 }
192197}
198+
199+ func TestBricksDetails (t * testing.T ) {
200+ tmpDir := t .TempDir ()
201+ appsDir := filepath .Join (tmpDir , "ArduinoApps" )
202+ dataDir := filepath .Join (tmpDir , "Data" )
203+ assetsDir := filepath .Join (dataDir , "assets" )
204+
205+ require .NoError (t , os .MkdirAll (appsDir , 0755 ))
206+ require .NoError (t , os .MkdirAll (assetsDir , 0755 ))
207+
208+ t .Setenv ("ARDUINO_APP_CLI__APPS_DIR" , appsDir )
209+ t .Setenv ("ARDUINO_APP_CLI__DATA_DIR" , dataDir )
210+
211+ cfg , err := config .NewFromEnv ()
212+ require .NoError (t , err )
213+
214+ for _ , brick := range []string {"object_detection" , "weather_forecast" , "one_model_brick" } {
215+ createFakeBrickAssets (t , assetsDir , brick )
216+ }
217+ createFakeApp (t , appsDir )
218+
219+ bIndex := & bricksindex.BricksIndex {
220+ Bricks : []bricksindex.Brick {
221+ {
222+ ID : "arduino:object_detection" ,
223+ Name : "Object Detection" ,
224+ Category : "video" ,
225+ ModelName : "yolox-object-detection" , // Default model
226+ Variables : []bricksindex.BrickVariable {
227+ {Name : "EI_OBJ_DETECTION_MODEL" , DefaultValue : "default_path" , Description : "path to the model file" },
228+ {Name : "CUSTOM_MODEL_PATH" , DefaultValue : "/home/arduino/.arduino-bricks/ei-models" , Description : "path to the custom model directory" },
229+ },
230+ },
231+ {
232+ ID : "arduino:weather_forecast" ,
233+ Name : "Weather Forecast" ,
234+ Category : "miscellaneous" ,
235+ ModelName : "" ,
236+ },
237+ {
238+ ID : "arduino:one_model_brick" ,
239+ Name : "one model brick" ,
240+ Category : "video" ,
241+ ModelName : "face-detection" , // Default model
242+ Variables : []bricksindex.BrickVariable {},
243+ },
244+ },
245+ }
246+ mIndex := & modelsindex.ModelsIndex {
247+ Models : []modelsindex.AIModel {
248+
249+ {
250+ ID : "yolox-object-detection" ,
251+ Name : "General purpose object detection - YoloX" ,
252+ ModuleDescription : "General purpose object detection..." ,
253+ Bricks : []string {"arduino:object_detection" , "arduino:video_object_detection" },
254+ },
255+ {
256+ ID : "face-detection" ,
257+ Name : "Lightweight-Face-Detection" ,
258+ Bricks : []string {"arduino:object_detection" , "arduino:video_object_detection" , "arduino:one_model_brick" },
259+ },
260+ }}
261+
262+ svc := & Service {
263+ bricksIndex : bIndex ,
264+ modelsIndex : mIndex ,
265+ staticStore : store .NewStaticStore (assetsDir ),
266+ }
267+ idProvider := app .NewAppIDProvider (cfg )
268+
269+ t .Run ("Brick Not Found" , func (t * testing.T ) {
270+ res , err := svc .BricksDetails ("arduino:non_existing" , idProvider , cfg )
271+ require .Error (t , err )
272+ require .Equal (t , ErrBrickNotFound , err )
273+ require .Empty (t , res .ID )
274+ })
275+
276+ t .Run ("Success - Full Details - multiple models" , func (t * testing.T ) {
277+ res , err := svc .BricksDetails ("arduino:object_detection" , idProvider , cfg )
278+ require .NoError (t , err )
279+
280+ require .Equal (t , "arduino:object_detection" , res .ID )
281+ require .Equal (t , "Object Detection" , res .Name )
282+ require .Equal (t , "Arduino" , res .Author )
283+ require .Equal (t , "installed" , res .Status )
284+ require .Contains (t , res .Variables , "EI_OBJ_DETECTION_MODEL" )
285+ require .Equal (t , "default_path" , res .Variables ["EI_OBJ_DETECTION_MODEL" ].DefaultValue )
286+ require .Equal (t , "# Documentation" , res .Readme )
287+ require .Contains (t , res .ApiDocsPath , filepath .Join ("arduino" , "app_bricks" , "object_detection" , "API.md" ))
288+ require .Len (t , res .CodeExamples , 1 )
289+ require .Contains (t , res .CodeExamples [0 ].Path , "blink.ino" )
290+ require .Len (t , res .UsedByApps , 1 )
291+ require .Equal (t , "My App" , res .UsedByApps [0 ].Name )
292+ require .NotEmpty (t , res .UsedByApps [0 ].ID )
293+ require .Len (t , res .Models , 2 )
294+ require .Equal (t , "yolox-object-detection" , res .Models [0 ].ID )
295+ require .Equal (t , "General purpose object detection - YoloX" , res .Models [0 ].Name )
296+ require .Equal (t , "General purpose object detection..." , res .Models [0 ].Description )
297+ require .Equal (t , "face-detection" , res .Models [1 ].ID )
298+ require .Equal (t , "Lightweight-Face-Detection" , res .Models [1 ].Name )
299+ require .Equal (t , "" , res .Models [1 ].Description )
300+ })
301+
302+ t .Run ("Success - Full Details - no models" , func (t * testing.T ) {
303+ res , err := svc .BricksDetails ("arduino:weather_forecast" , idProvider , cfg )
304+ require .NoError (t , err )
305+
306+ require .Equal (t , "arduino:weather_forecast" , res .ID )
307+ require .Equal (t , "Weather Forecast" , res .Name )
308+ require .Equal (t , "Arduino" , res .Author )
309+ require .Equal (t , "installed" , res .Status )
310+ require .Empty (t , res .Variables )
311+ require .Equal (t , "# Documentation" , res .Readme )
312+ require .Contains (t , res .ApiDocsPath , filepath .Join ("arduino" , "app_bricks" , "weather_forecast" , "API.md" ))
313+ require .Len (t , res .CodeExamples , 1 )
314+ require .Contains (t , res .CodeExamples [0 ].Path , "blink.ino" )
315+ require .Len (t , res .UsedByApps , 1 )
316+ require .Equal (t , "My App" , res .UsedByApps [0 ].Name )
317+ require .NotEmpty (t , res .UsedByApps [0 ].ID )
318+ require .Len (t , res .Models , 0 )
319+ })
320+
321+ t .Run ("Success - Full Details - one model" , func (t * testing.T ) {
322+ res , err := svc .BricksDetails ("arduino:one_model_brick" , idProvider , cfg )
323+ require .NoError (t , err )
324+
325+ require .Equal (t , "arduino:one_model_brick" , res .ID )
326+ require .Equal (t , "one model brick" , res .Name )
327+ require .Len (t , res .Models , 1 )
328+ require .Equal (t , "face-detection" , res .Models [0 ].ID )
329+ require .Equal (t , "Lightweight-Face-Detection" , res .Models [0 ].Name )
330+ require .Equal (t , "" , res .Models [0 ].Description )
331+ })
332+ }
333+
334+ func createFakeBrickAssets (t * testing.T , assetsDir , brick string ) {
335+ t .Helper ()
336+
337+ brickDocDir := filepath .Join (assetsDir , "docs" , "arduino" , brick )
338+ require .NoError (t , os .MkdirAll (brickDocDir , 0755 ))
339+ require .NoError (t , os .WriteFile (filepath .Join (brickDocDir , "README.md" ),
340+ []byte ("# Documentation" ), 0600 ))
341+
342+ brickExDir := filepath .Join (assetsDir , "examples" , "arduino" , brick )
343+ require .NoError (t , os .MkdirAll (brickExDir , 0755 ))
344+ require .NoError (t , os .WriteFile (filepath .Join (brickExDir , "blink.ino" ),
345+ []byte ("void setup() {}" ), 0600 ))
346+ }
347+
348+ func createFakeApp (t * testing.T , appsDir string ) {
349+ t .Helper ()
350+ myAppDir := filepath .Join (appsDir , "MyApp" )
351+ require .NoError (t , os .MkdirAll (myAppDir , 0755 ))
352+
353+ appYamlContent := `
354+ name: My App
355+ bricks:
356+ - arduino:object_detection:
357+ - arduino:weather_forecast:
358+ `
359+ require .NoError (t , os .WriteFile (filepath .Join (myAppDir , "app.yaml" ), []byte (appYamlContent ), 0600 ))
360+ pythonDir := filepath .Join (myAppDir , "python" )
361+ require .NoError (t , os .MkdirAll (pythonDir , 0755 ))
362+ require .NoError (t , os .WriteFile (filepath .Join (pythonDir , "main.py" ), []byte ("print('hello')" ), 0600 ))
363+ }
0 commit comments