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 ) {
@@ -318,3 +323,169 @@ func TestGetBrickInstanceVariableDetails(t *testing.T) {
318323 })
319324 }
320325}
326+
327+ func TestBricksDetails (t * testing.T ) {
328+ tmpDir := t .TempDir ()
329+ appsDir := filepath .Join (tmpDir , "ArduinoApps" )
330+ dataDir := filepath .Join (tmpDir , "Data" )
331+ assetsDir := filepath .Join (dataDir , "assets" )
332+
333+ require .NoError (t , os .MkdirAll (appsDir , 0755 ))
334+ require .NoError (t , os .MkdirAll (assetsDir , 0755 ))
335+
336+ t .Setenv ("ARDUINO_APP_CLI__APPS_DIR" , appsDir )
337+ t .Setenv ("ARDUINO_APP_CLI__DATA_DIR" , dataDir )
338+
339+ cfg , err := config .NewFromEnv ()
340+ require .NoError (t , err )
341+
342+ for _ , brick := range []string {"object_detection" , "weather_forecast" , "one_model_brick" } {
343+ createFakeBrickAssets (t , assetsDir , brick )
344+ }
345+ createFakeApp (t , appsDir )
346+
347+ bIndex := & bricksindex.BricksIndex {
348+ Bricks : []bricksindex.Brick {
349+ {
350+ ID : "arduino:object_detection" ,
351+ Name : "Object Detection" ,
352+ Category : "video" ,
353+ ModelName : "yolox-object-detection" , // Default model
354+ Variables : []bricksindex.BrickVariable {
355+ {Name : "EI_OBJ_DETECTION_MODEL" , DefaultValue : "default_path" , Description : "path to the model file" },
356+ {Name : "CUSTOM_MODEL_PATH" , DefaultValue : "/home/arduino/.arduino-bricks/ei-models" , Description : "path to the custom model directory" },
357+ },
358+ },
359+ {
360+ ID : "arduino:weather_forecast" ,
361+ Name : "Weather Forecast" ,
362+ Category : "miscellaneous" ,
363+ ModelName : "" ,
364+ },
365+ {
366+ ID : "arduino:one_model_brick" ,
367+ Name : "one model brick" ,
368+ Category : "video" ,
369+ ModelName : "face-detection" , // Default model
370+ Variables : []bricksindex.BrickVariable {},
371+ },
372+ },
373+ }
374+ mIndex := & modelsindex.ModelsIndex {
375+ Models : []modelsindex.AIModel {
376+
377+ {
378+ ID : "yolox-object-detection" ,
379+ Name : "General purpose object detection - YoloX" ,
380+ ModuleDescription : "General purpose object detection..." ,
381+ Bricks : []string {"arduino:object_detection" , "arduino:video_object_detection" },
382+ },
383+ {
384+ ID : "face-detection" ,
385+ Name : "Lightweight-Face-Detection" ,
386+ Bricks : []string {"arduino:object_detection" , "arduino:video_object_detection" , "arduino:one_model_brick" },
387+ },
388+ }}
389+
390+ svc := & Service {
391+ bricksIndex : bIndex ,
392+ modelsIndex : mIndex ,
393+ staticStore : store .NewStaticStore (assetsDir ),
394+ }
395+ idProvider := app .NewAppIDProvider (cfg )
396+
397+ t .Run ("Brick Not Found" , func (t * testing.T ) {
398+ res , err := svc .BricksDetails ("arduino:non_existing" , idProvider , cfg )
399+ require .Error (t , err )
400+ require .Equal (t , ErrBrickNotFound , err )
401+ require .Empty (t , res .ID )
402+ })
403+
404+ t .Run ("Success - Full Details - multiple models" , func (t * testing.T ) {
405+ res , err := svc .BricksDetails ("arduino:object_detection" , idProvider , cfg )
406+ require .NoError (t , err )
407+
408+ require .Equal (t , "arduino:object_detection" , res .ID )
409+ require .Equal (t , "Object Detection" , res .Name )
410+ require .Equal (t , "Arduino" , res .Author )
411+ require .Equal (t , "installed" , res .Status )
412+ require .Contains (t , res .Variables , "EI_OBJ_DETECTION_MODEL" )
413+ require .Equal (t , "default_path" , res .Variables ["EI_OBJ_DETECTION_MODEL" ].DefaultValue )
414+ require .Equal (t , "# Documentation" , res .Readme )
415+ require .Contains (t , res .ApiDocsPath , filepath .Join ("arduino" , "app_bricks" , "object_detection" , "API.md" ))
416+ require .Len (t , res .CodeExamples , 1 )
417+ require .Contains (t , res .CodeExamples [0 ].Path , "blink.ino" )
418+ require .Len (t , res .UsedByApps , 1 )
419+ require .Equal (t , "My App" , res .UsedByApps [0 ].Name )
420+ require .NotEmpty (t , res .UsedByApps [0 ].ID )
421+ require .Len (t , res .Models , 2 )
422+ require .Equal (t , "yolox-object-detection" , res .Models [0 ].ID )
423+ require .Equal (t , "General purpose object detection - YoloX" , res .Models [0 ].Name )
424+ require .Equal (t , "General purpose object detection..." , res .Models [0 ].Description )
425+ require .Equal (t , "face-detection" , res .Models [1 ].ID )
426+ require .Equal (t , "Lightweight-Face-Detection" , res .Models [1 ].Name )
427+ require .Equal (t , "" , res .Models [1 ].Description )
428+ })
429+
430+ t .Run ("Success - Full Details - no models" , func (t * testing.T ) {
431+ res , err := svc .BricksDetails ("arduino:weather_forecast" , idProvider , cfg )
432+ require .NoError (t , err )
433+
434+ require .Equal (t , "arduino:weather_forecast" , res .ID )
435+ require .Equal (t , "Weather Forecast" , res .Name )
436+ require .Equal (t , "Arduino" , res .Author )
437+ require .Equal (t , "installed" , res .Status )
438+ require .Empty (t , res .Variables )
439+ require .Equal (t , "# Documentation" , res .Readme )
440+ require .Contains (t , res .ApiDocsPath , filepath .Join ("arduino" , "app_bricks" , "weather_forecast" , "API.md" ))
441+ require .Len (t , res .CodeExamples , 1 )
442+ require .Contains (t , res .CodeExamples [0 ].Path , "blink.ino" )
443+ require .Len (t , res .UsedByApps , 1 )
444+ require .Equal (t , "My App" , res .UsedByApps [0 ].Name )
445+ require .NotEmpty (t , res .UsedByApps [0 ].ID )
446+ require .Len (t , res .Models , 0 )
447+ })
448+
449+ t .Run ("Success - Full Details - one model" , func (t * testing.T ) {
450+ res , err := svc .BricksDetails ("arduino:one_model_brick" , idProvider , cfg )
451+ require .NoError (t , err )
452+
453+ require .Equal (t , "arduino:one_model_brick" , res .ID )
454+ require .Equal (t , "one model brick" , res .Name )
455+ require .Len (t , res .Models , 1 )
456+ require .Equal (t , "face-detection" , res .Models [0 ].ID )
457+ require .Equal (t , "Lightweight-Face-Detection" , res .Models [0 ].Name )
458+ require .Equal (t , "" , res .Models [0 ].Description )
459+ })
460+ }
461+
462+ func createFakeBrickAssets (t * testing.T , assetsDir , brick string ) {
463+ t .Helper ()
464+
465+ brickDocDir := filepath .Join (assetsDir , "docs" , "arduino" , brick )
466+ require .NoError (t , os .MkdirAll (brickDocDir , 0755 ))
467+ require .NoError (t , os .WriteFile (filepath .Join (brickDocDir , "README.md" ),
468+ []byte ("# Documentation" ), 0600 ))
469+
470+ brickExDir := filepath .Join (assetsDir , "examples" , "arduino" , brick )
471+ require .NoError (t , os .MkdirAll (brickExDir , 0755 ))
472+ require .NoError (t , os .WriteFile (filepath .Join (brickExDir , "blink.ino" ),
473+ []byte ("void setup() {}" ), 0600 ))
474+ }
475+
476+ func createFakeApp (t * testing.T , appsDir string ) {
477+ t .Helper ()
478+ myAppDir := filepath .Join (appsDir , "MyApp" )
479+ require .NoError (t , os .MkdirAll (myAppDir , 0755 ))
480+
481+ appYamlContent := `
482+ name: My App
483+ bricks:
484+ - arduino:object_detection:
485+ - arduino:weather_forecast:
486+ `
487+ require .NoError (t , os .WriteFile (filepath .Join (myAppDir , "app.yaml" ), []byte (appYamlContent ), 0600 ))
488+ pythonDir := filepath .Join (myAppDir , "python" )
489+ require .NoError (t , os .MkdirAll (pythonDir , 0755 ))
490+ require .NoError (t , os .WriteFile (filepath .Join (pythonDir , "main.py" ), []byte ("print('hello')" ), 0600 ))
491+ }
0 commit comments