11package generator
22
33import (
4- "flag"
4+ "bytes"
5+ "fmt"
6+ "io/fs"
57 "os"
68 "path/filepath"
9+ "sort"
710 "testing"
811
912 "github.com/arduino/go-paths-helper"
@@ -12,279 +15,96 @@ import (
1215 "github.com/arduino/arduino-app-cli/internal/orchestrator/app"
1316)
1417
15- var update = flag . Bool ( "update" , false , "update golden files" )
16-
17- func TestGenerateSketch ( t * testing. T ) {
18-
19- tempDir := t . TempDir ()
20- err := generateSketch ( paths . New ( tempDir ))
21- require . NoError ( t , err , "generateSketch should run without errors" )
18+ func TestGenerateApp ( t * testing. T ) {
19+ baseApp := app. AppDescriptor {
20+ Name : "test app all" ,
21+ Description : "test description." ,
22+ Icon : "🚀" ,
23+ Ports : [] int { 8080 , 9000 , 90 },
24+ }
2225
2326 testCases := []struct {
24- filename string
27+ name string
28+ options Opts
29+ goldenPath string
2530 }{
26- {filename : "sketch.ino" },
27- {filename : "sketch.yaml" },
31+ {
32+ name : "generate complete app" ,
33+ options : None ,
34+ goldenPath : "testdata/app-all.golden" ,
35+ },
36+ {
37+ name : "skip sketch" ,
38+ options : SkipSketch ,
39+ goldenPath : "testdata/app-no-sketch.golden" ,
40+ },
41+ {
42+ name : "skip python" ,
43+ options : SkipPython ,
44+ goldenPath : "testdata/app-no-python.golden" ,
45+ },
2846 }
2947
3048 for _ , tc := range testCases {
31- t .Run (tc .filename , func (t * testing.T ) {
49+ t .Run (tc .name , func (t * testing.T ) {
50+ tempDir := t .TempDir ()
3251
33- actualFilePath := filepath .Join (tempDir , "sketch" , tc .filename )
34- require .FileExists (t , actualFilePath )
35- actualContent , err := os .ReadFile (actualFilePath )
52+ err := GenerateApp (paths .New (tempDir ), baseApp , tc .options )
3653 require .NoError (t , err )
3754
38- goldenFilePath := filepath .Join ("testdata" , tc .filename + ".golden" )
39-
40- if * update {
41- err := os .WriteFile (goldenFilePath , actualContent , 0600 )
42- require .NoError (t , err , "failed to update golden file: %s" , goldenFilePath )
55+ if os .Getenv ("UPDATE_GOLDEN" ) == "true" {
56+ t .Logf ("UPDATE_GOLDEN=true: updating golden files in %s" , tc .goldenPath )
57+ require .NoError (t , os .RemoveAll (tc .goldenPath ))
58+ require .NoError (t , os .CopyFS (tempDir , os .DirFS (tc .goldenPath )))
59+ } else {
60+ compareFolders (t , paths .New (tempDir ), paths .New (tc .goldenPath ))
4361 }
44-
45- expectedContent , err := os .ReadFile (goldenFilePath )
46- require .NoError (t , err , "failed to read golden file: %s" , goldenFilePath )
47-
48- require .Equal (t , string (expectedContent ), string (actualContent ), "the generated content does not match the .golden file" )
4962 })
5063 }
5164}
5265
53- func TestGeneratePython (t * testing.T ) {
54- tempDir := t .TempDir ()
55-
56- err := generatePython (paths .New (tempDir ))
57- require .NoError (t , err , "GeneratePython() should not return an error" )
58-
59- verifyPythonGenerated (t , tempDir )
60- }
61-
62- func TestGenerateReadme (t * testing.T ) {
63- tempDir := t .TempDir ()
64- testApp := app.AppDescriptor {
65- Name : "test name" ,
66- Description : "test description." ,
67- Icon : "🚀" ,
68- Ports : []int {8080 , 9000 },
69- }
70-
71- err := generateReadme (paths .New (tempDir ), testApp )
72- require .NoError (t , err , "GenerateReadme() should not return an error" )
73-
74- verifyReadmeGenerated (t , testApp , tempDir )
75-
76- }
77-
78- func TestGenerateAppYaml (t * testing.T ) {
79- tempDir := t .TempDir ()
80-
81- testApp := app.AppDescriptor {
82- Name : "test name" ,
83- Description : "test description." ,
84- Icon : "🚀" ,
85- Ports : []int {8080 , 9000 , 90 },
86- }
87-
88- err := generateAppYaml (paths .New (tempDir ), testApp )
89- require .NoError (t , err , "GenerateAppYaml() should not return an error" )
90-
91- yamlPath := filepath .Join (tempDir , "app.yaml" )
92- require .FileExists (t , yamlPath , "The app.yaml file should exist" )
93-
94- actualContent , err := os .ReadFile (yamlPath )
95- require .NoError (t , err , "Failed to read the generated app.yaml file" )
96-
97- contentString := string (actualContent )
98- require .Contains (t , contentString , "name: test name" , "The YAML should contain the correct name" )
99- require .Contains (t , contentString , `description: "test description."` , "The YAML should contain the correct description" )
100- require .Contains (t , contentString , "icon: 🚀" , "The YAML should contain the correct icon" )
101- require .Contains (t , contentString , "ports: [8080, 9000, 90]" , "The YAML should contain the correct ports list" + contentString )
102- require .Contains (t , contentString , "bricks: []" , "The YAML should contain an empty bricks list" )
103-
104- }
105-
106- func TestGenerateAndParseAppYaml_RoundTrip (t * testing.T ) {
107- tempDir := t .TempDir ()
108-
109- testApp := app.AppDescriptor {
110- Name : "test name" ,
111- Description : "test description." ,
112- Icon : "🚀" ,
113- Ports : []int {8080 , 9000 , 90 },
114- Bricks : []app.Brick {},
115- }
116-
117- err := generateAppYaml (paths .New (tempDir ), testApp )
118- require .NoError (t , err , "Step 1: Generation should not fail" )
119-
120- parsedApp , err := app .ParseDescriptorFile (paths .New (tempDir , "app.yaml" ))
121- require .NoError (t , err , "Parsing the generated file should not fail" )
122- require .Equal (t , testApp , parsedApp , "The parsed app should be identical to the original one" )
123- }
124-
125- func TestGeneratCompleteApp (t * testing.T ) {
126- tempDir := t .TempDir ()
127-
128- testApp := app.AppDescriptor {
129- Name : "test name" ,
130- Description : "test description." ,
131- Icon : "🚀" ,
132- Ports : []int {8080 , 9000 , 90 },
133- Bricks : []app.Brick {},
134- }
135-
136- var options Opts = 0
137-
138- err := GenerateApp (paths .New (tempDir ), testApp , options )
139- require .NoError (t , err , "GenerateApp() should not return an error" )
140-
141- parsedApp , err := app .ParseDescriptorFile (paths .New (tempDir , "app.yaml" ))
142- require .NoError (t , err , "Parsing the generated file should not fail" )
143- require .Equal (t , testApp , parsedApp , "The parsed app should be identical to the original one" )
144-
145- verifyReadmeGenerated (t , testApp , tempDir )
146- verifySketchGenerated (t , tempDir )
147- verifyPythonGenerated (t , tempDir )
148-
149- }
150-
151- func TestGeneratAppSkipPython (t * testing.T ) {
152- tempDir := t .TempDir ()
66+ func compareFolders (t * testing.T , actualPath , goldenPath * paths.Path ) {
67+ t .Helper ()
15368
154- testApp := app.AppDescriptor {
155- Name : "test name" ,
156- Description : "test description." ,
157- Icon : "🚀" ,
158- Ports : []int {8080 , 9000 , 90 },
159- Bricks : []app.Brick {},
69+ getFiles := func (root string ) ([]string , error ) {
70+ var files []string
71+ err := filepath .WalkDir (root , func (path string , d fs.DirEntry , err error ) error {
72+ if err != nil {
73+ return err
74+ }
75+ if d .IsDir () {
76+ return nil
77+ }
78+ rel , err := filepath .Rel (root , path )
79+ if err != nil {
80+ return fmt .Errorf ("failed to retrieve relative path for %q: %w" , path , err )
81+ }
82+ files = append (files , rel )
83+ return nil
84+ })
85+ return files , err
16086 }
161- var options Opts = 0
162- options |= SkipPython
16387
164- err := GenerateApp ( paths . New ( tempDir ), testApp , options )
165- require . NoError ( t , err , "GenerateApp() should not return an error" )
88+ goldenDir := goldenPath . String ( )
89+ actualDir := actualPath . String ( )
16690
167- parsedApp , err := app .ParseDescriptorFile (paths .New (tempDir , "app.yaml" ))
168- require .NoError (t , err , "Parsing the generated file should not fail" )
169- require .Equal (t , testApp , parsedApp , "The parsed app should be identical to the original one" )
91+ goldenFiles , err := getFiles (goldenDir )
92+ require .NoError (t , err , "failed reading golden dir" )
17093
171- verifyReadmeGenerated ( t , testApp , tempDir )
172- verifySketchGenerated (t , tempDir )
94+ actualFiles , err := getFiles ( actualDir )
95+ require . NoError (t , err , "failed reading actual dir" )
17396
174- pythonPath := filepath .Join (tempDir , "python" )
175- require .NoDirExists (t , pythonPath , "The 'python' directory should NOT exist when skipped" )
176- }
97+ sort .Strings (goldenFiles )
98+ sort .Strings (actualFiles )
17799
178- func TestGeneratAppSkipSketch (t * testing.T ) {
179- tempDir := t .TempDir ()
100+ require .Equal (t , goldenFiles , actualFiles , "golden dir and actual dir should have the same structure" )
180101
181- testApp := app.AppDescriptor {
182- Name : "test name" ,
183- Description : "test description." ,
184- Icon : "🚀" ,
185- Ports : []int {8080 , 9000 , 90 },
186- Bricks : []app.Brick {},
187- }
188- var options Opts = 0
189- options |= SkipSketch
190-
191- err := GenerateApp (paths .New (tempDir ), testApp , options )
192- require .NoError (t , err , "GenerateApp() should not return an error" )
193-
194- parsedApp , err := app .ParseDescriptorFile (paths .New (tempDir , "app.yaml" ))
195- require .NoError (t , err , "Parsing the generated file should not fail" )
196- require .Equal (t , testApp , parsedApp , "The parsed app should be identical to the original one" )
102+ for _ , relPath := range goldenFiles {
103+ goldenContent , err := os .ReadFile (filepath .Join (goldenDir , relPath ))
104+ require .NoError (t , err , "failed reading golden file: %q" , relPath )
105+ actualContent , err := os .ReadFile (filepath .Join (actualDir , relPath ))
106+ require .NoError (t , err , "failed reading actual file: %s" , relPath )
107+ require .True (t , bytes .Equal (goldenContent , actualContent ), "content should be the same: %q" , relPath )
197108
198- verifyReadmeGenerated (t , testApp , tempDir )
199- verifyPythonGenerated (t , tempDir )
200-
201- sketchPath := filepath .Join (tempDir , "sketch" )
202- require .NoDirExists (t , sketchPath , "The 'sketch' directory should NOT exist when skipped" )
203- }
204-
205- func TestGeneratAppSkipBoth (t * testing.T ) {
206- tempDir := t .TempDir ()
207-
208- testApp := app.AppDescriptor {
209- Name : "test name" ,
210- Description : "test description." ,
211- Icon : "🚀" ,
212- Ports : []int {8080 , 9000 , 90 },
213- Bricks : []app.Brick {},
214109 }
215- var options Opts = 0
216- options |= SkipSketch
217- options |= SkipPython
218-
219- err := GenerateApp (paths .New (tempDir ), testApp , options )
220- require .NoError (t , err , "GenerateApp() should not return an error" )
221-
222- parsedApp , err := app .ParseDescriptorFile (paths .New (tempDir , "app.yaml" ))
223- require .NoError (t , err , "Parsing the generated file should not fail" )
224- require .Equal (t , testApp , parsedApp , "The parsed app should be identical to the original one" )
225-
226- verifyReadmeGenerated (t , testApp , tempDir )
227-
228- pythonPath := filepath .Join (tempDir , "python" )
229- require .NoDirExists (t , pythonPath , "The 'python' directory should NOT exist when skipped" )
230-
231- sketchPath := filepath .Join (tempDir , "sketch" )
232- require .NoDirExists (t , sketchPath , "The 'sketch' directory should NOT exist when skipped" )
233- }
234-
235- func verifySketchGenerated (t * testing.T , basePath string ) {
236- t .Helper ()
237-
238- sketchPath := filepath .Join (basePath , "sketch" )
239- inoPath := filepath .Join (sketchPath , "sketch.ino" )
240- yamlPath := filepath .Join (sketchPath , "sketch.yaml" )
241-
242- require .DirExists (t , sketchPath , "The 'sketch' directory should exist" )
243- require .FileExists (t , inoPath , "The 'sketch.ino' file should exist" )
244- require .FileExists (t , yamlPath , "The 'sketch.yaml' file should exist" )
245-
246- expectedInoContent , err := fsApp .ReadFile ("app_template/sketch/sketch.ino" )
247- require .NoError (t , err )
248- actualInoContent , err := os .ReadFile (inoPath )
249- require .NoError (t , err )
250- require .Equal (t , string (expectedInoContent ), string (actualInoContent ))
251-
252- expectedYamlContent , err := fsApp .ReadFile ("app_template/sketch/sketch.yaml" )
253- require .NoError (t , err )
254- actualYamlContent , err := os .ReadFile (yamlPath )
255- require .NoError (t , err )
256- require .Equal (t , string (expectedYamlContent ), string (actualYamlContent ))
257- }
258-
259- func verifyPythonGenerated (t * testing.T , basePath string ) {
260- t .Helper ()
261-
262- pythonPath := filepath .Join (basePath , "python" )
263- mainPath := filepath .Join (pythonPath , "main.py" )
264-
265- require .DirExists (t , pythonPath , "The 'python' directory should exist" )
266- require .FileExists (t , mainPath , "The 'main.py' file should exist" )
267-
268- expectedMainContent , err := fsApp .ReadFile ("app_template/python/main.py" )
269- require .NoError (t , err )
270- actualMainContent , err := os .ReadFile (mainPath )
271- require .NoError (t , err )
272- require .Equal (t , string (expectedMainContent ), string (actualMainContent ))
273- }
274-
275- func verifyReadmeGenerated (t * testing.T , testApp app.AppDescriptor , basePath string ) {
276- t .Helper ()
277-
278- readmePath := filepath .Join (basePath , "README.md" )
279- require .FileExists (t , readmePath , "The README.md file should exist" )
280-
281- actualContent , err := os .ReadFile (readmePath )
282- require .NoError (t , err , "Failed to read the generated README.md file" )
283-
284- contentString := string (actualContent )
285- require .Contains (t , contentString , testApp .Name , "The README should contain the app name" )
286- require .Contains (t , contentString , testApp .Description , "The README should contain the app description" )
287- require .Contains (t , contentString , testApp .Icon , "The README should contain the app icon" )
288- require .Contains (t , contentString , "8080, 9000" , "The README should contain the formatted port list" )
289- require .Contains (t , contentString , "Available application ports:" , "The README should contain the static text for ports" )
290110}
0 commit comments