Refactored binapi generator with message encoding
[govpp.git] / binapigen / generate_test.go
1 //  Copyright (c) 2020 Cisco and/or its affiliates.
2 //
3 //  Licensed under the Apache License, Version 2.0 (the "License");
4 //  you may not use this file except in compliance with the License.
5 //  You may obtain a copy of the License at:
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 //  Unless required by applicable law or agreed to in writing, software
10 //  distributed under the License is distributed on an "AS IS" BASIS,
11 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 //  See the License for the specific language governing permissions and
13 //  limitations under the License.
14
15 package binapigen
16
17 import (
18         "os"
19         "testing"
20
21         . "github.com/onsi/gomega"
22
23         "git.fd.io/govpp.git/binapigen/vppapi"
24 )
25
26 const testOutputDir = "test_output_directory"
27
28 func GenerateFromFile(apiDir, outputDir string, opts Options) error {
29         // parse API files
30         apifiles, err := vppapi.ParseDir(apiDir)
31         if err != nil {
32                 return err
33         }
34
35         g, err := New(opts, apifiles)
36         if err != nil {
37                 return err
38         }
39         for _, file := range g.Files {
40                 if !file.Generate {
41                         continue
42                 }
43                 GenerateBinapiFile(g, file, outputDir)
44                 if file.Service != nil {
45                         GenerateRPC(g, file, outputDir)
46                 }
47         }
48
49         if err = g.Generate(); err != nil {
50                 return err
51         }
52
53         return nil
54 }
55
56 func TestGenerateFromFile(t *testing.T) {
57         RegisterTestingT(t)
58
59         // remove directory created during test
60         defer os.RemoveAll(testOutputDir)
61
62         err := GenerateFromFile("testdata/acl.api.json", testOutputDir, Options{})
63         Expect(err).ShouldNot(HaveOccurred())
64         fileInfo, err := os.Stat(testOutputDir + "/acl/acl.ba.go")
65         Expect(err).ShouldNot(HaveOccurred())
66         Expect(fileInfo.IsDir()).To(BeFalse())
67         Expect(fileInfo.Name()).To(BeEquivalentTo("acl.ba.go"))
68 }
69
70 func TestGenerateFromFileInputError(t *testing.T) {
71         RegisterTestingT(t)
72
73         err := GenerateFromFile("testdata/nonexisting.json", testOutputDir, Options{})
74         Expect(err).Should(HaveOccurred())
75         Expect(err.Error()).To(ContainSubstring("invalid input file name"))
76 }
77
78 func TestGenerateFromFileReadJsonError(t *testing.T) {
79         RegisterTestingT(t)
80
81         err := GenerateFromFile("testdata/input-read-json-error.json", testOutputDir, Options{})
82         Expect(err).Should(HaveOccurred())
83         Expect(err.Error()).To(ContainSubstring("invalid input file name"))
84 }
85
86 func TestGenerateFromFileGeneratePackageError(t *testing.T) {
87         RegisterTestingT(t)
88
89         // generate package throws panic, recover after it
90         defer func() {
91                 if recovery := recover(); recovery != nil {
92                         t.Logf("Recovered from panic: %v", recovery)
93                 }
94                 os.RemoveAll(testOutputDir)
95         }()
96
97         err := GenerateFromFile("testdata/input-generate-error.json", testOutputDir, Options{})
98         Expect(err).Should(HaveOccurred())
99 }
100
101 /*func TestGetContext(t *testing.T) {
102         RegisterTestingT(t)
103         outDir := "test_output_directory"
104         result, err := newContext("testdata/af_packet.api.json", outDir)
105         Expect(err).ShouldNot(HaveOccurred())
106         Expect(result).ToNot(BeNil())
107         Expect(result.outputFile).To(BeEquivalentTo(outDir + "/af_packet/af_packet.ba.go"))
108 }
109
110 func TestGetContextNoJsonFile(t *testing.T) {
111         RegisterTestingT(t)
112         outDir := "test_output_directory"
113         result, err := newContext("testdata/input.txt", outDir)
114         Expect(err).Should(HaveOccurred())
115         Expect(err.Error()).To(ContainSubstring("invalid input file name"))
116         Expect(result).To(BeNil())
117 }
118
119 func TestGetContextInterfaceJson(t *testing.T) {
120         RegisterTestingT(t)
121         outDir := "test_output_directory"
122         result, err := newContext("testdata/ip.api.json", outDir)
123         Expect(err).ShouldNot(HaveOccurred())
124         Expect(result).ToNot(BeNil())
125         Expect(result.outputFile)
126         Expect(result.outputFile).To(BeEquivalentTo(outDir + "/ip/ip.ba.go"))
127 }*/
128
129 /*func TestGeneratePackage(t *testing.T) {
130         RegisterTestingT(t)
131         // prepare context
132         testCtx := new(GenFile)
133         testCtx.packageName = "test-package-name"
134
135         // prepare input/output output files
136         inputData, err := ioutil.ReadFile("testdata/ip.api.json")
137         Expect(err).ShouldNot(HaveOccurred())
138         jsonRoot, err := parseInputJSON(inputData)
139         Expect(err).ShouldNot(HaveOccurred())
140         testCtx.file, err = parseModule(testCtx, jsonRoot)
141         Expect(err).ShouldNot(HaveOccurred())
142         outDir := "test_output_directory"
143         outFile, err := os.Create(outDir)
144         Expect(err).ShouldNot(HaveOccurred())
145         defer os.RemoveAll(outDir)
146
147         // prepare writer
148         writer := bufio.NewWriter(outFile)
149         Expect(writer.Buffered()).To(BeZero())
150         err = generatePackage(testCtx, writer)
151         Expect(err).ShouldNot(HaveOccurred())
152 }
153
154 func TestGenerateMessageType(t *testing.T) {
155         RegisterTestingT(t)
156         // prepare context
157         testCtx := new(GenFile)
158         testCtx.packageName = "test-package-name"
159
160         // prepare input/output output files
161         inputData, err := ioutil.ReadFile("testdata/ip.api.json")
162         Expect(err).ShouldNot(HaveOccurred())
163         jsonRoot, err := parseInputJSON(inputData)
164         Expect(err).ShouldNot(HaveOccurred())
165         outDir := "test_output_directory"
166         outFile, err := os.Create(outDir)
167         Expect(err).ShouldNot(HaveOccurred())
168         testCtx.file, err = parseModule(testCtx, jsonRoot)
169         Expect(err).ShouldNot(HaveOccurred())
170         defer os.RemoveAll(outDir)
171
172         // prepare writer
173         writer := bufio.NewWriter(outFile)
174
175         for _, msg := range testCtx.file.Messages {
176                 generateMessage(testCtx, writer, &msg)
177                 Expect(writer.Buffered()).ToNot(BeZero())
178         }
179 }*/
180
181 /*func TestGenerateMessageName(t *testing.T) {
182         RegisterTestingT(t)
183         // prepare context
184         testCtx := new(context)
185         testCtx.packageName = "test-package-name"
186
187         // prepare input/output output files
188         inputData, err := readFile("testdata/ip.api.json")
189         Expect(err).ShouldNot(HaveOccurred())
190         testCtx.inputBuff = bytes.NewBuffer(inputData)
191         inFile, _ := parseJSON(inputData)
192         outDir := "test_output_directory"
193         outFile, err := os.Create(outDir)
194         Expect(err).ShouldNot(HaveOccurred())
195         defer os.RemoveAll(outDir)
196
197         // prepare writer
198         writer := bufio.NewWriter(outFile)
199
200         types := inFile.Map("types")
201         Expect(types.Len()).To(BeEquivalentTo(1))
202         for i := 0; i < types.Len(); i++ {
203                 typ := types.At(i)
204                 Expect(writer.Buffered()).To(BeZero())
205                 err := generateMessage(testCtx, writer, typ, false)
206                 Expect(err).ShouldNot(HaveOccurred())
207                 Expect(writer.Buffered()).ToNot(BeZero())
208
209         }
210 }
211
212 func TestGenerateMessageFieldTypes(t *testing.T) {
213         // expected results according to acl.api.json in testdata
214         expectedTypes := []string{
215                 "\tIsPermit uint8",
216                 "\tIsIpv6 uint8",
217                 "\tSrcIPAddr []byte     `struc:\"[16]byte\"`",
218                 "\tSrcIPPrefixLen uint8",
219                 "\tDstIPAddr []byte     `struc:\"[16]byte\"`",
220                 "\tDstIPPrefixLen uint8",
221                 "\tProto uint8",
222                 "\tSrcportOrIcmptypeFirst uint16",
223                 "\tSrcportOrIcmptypeLast uint16",
224                 "\tDstportOrIcmpcodeFirst uint16",
225                 "\tDstportOrIcmpcodeLast uint16",
226                 "\tTCPFlagsMask uint8",
227                 "\tTCPFlagsValue uint8"}
228         RegisterTestingT(t)
229         // prepare context
230         testCtx := new(context)
231         testCtx.packageName = "test-package-name"
232
233         // prepare input/output output files
234         inputData, err := readFile("testdata/acl.api.json")
235         Expect(err).ShouldNot(HaveOccurred())
236         inFile, err := parseJSON(inputData)
237         Expect(err).ShouldNot(HaveOccurred())
238         Expect(inFile).ToNot(BeNil())
239
240         // test types
241         types := inFile.Map("types")
242         fields := make([]string, 0)
243         for i := 0; i < types.Len(); i++ {
244                 for j := 0; j < types.At(i).Len(); j++ {
245                         field := types.At(i).At(j)
246                         if field.GetType() == jsongo.TypeArray {
247                                 err := processMessageField(testCtx, &fields, field, false)
248                                 Expect(err).ShouldNot(HaveOccurred())
249                                 Expect(fields[j-1]).To(BeEquivalentTo(expectedTypes[j-1]))
250                         }
251                 }
252         }
253 }
254
255 func TestGenerateMessageFieldMessages(t *testing.T) {
256         // expected results according to acl.api.json in testdata
257         expectedFields := []string{"\tMajor uint32", "\tMinor uint32", "\tRetval int32",
258                 "\tVpePid uint32", "\tACLIndex uint32", "\tTag []byte   `struc:\"[64]byte\"`",
259                 "\tCount uint32"}
260         RegisterTestingT(t)
261         // prepare context
262         testCtx := new(context)
263         testCtx.packageName = "test-package-name"
264
265         // prepare input/output output files
266         inputData, err := readFile("testdata/acl.api.json")
267         Expect(err).ShouldNot(HaveOccurred())
268         inFile, err := parseJSON(inputData)
269         Expect(err).ShouldNot(HaveOccurred())
270         Expect(inFile).ToNot(BeNil())
271
272         // test message fields
273         messages := inFile.Map("messages")
274         customIndex := 0
275         fields := make([]string, 0)
276         for i := 0; i < messages.Len(); i++ {
277                 for j := 0; j < messages.At(i).Len(); j++ {
278                         field := messages.At(i).At(j)
279                         if field.GetType() == jsongo.TypeArray {
280                                 specificFieldName := field.At(1).Get().(string)
281                                 if specificFieldName == "crc" || specificFieldName == "_vl_msg_id" ||
282                                         specificFieldName == "client_index" || specificFieldName == "context" {
283                                         continue
284                                 }
285                                 err := processMessageField(testCtx, &fields, field, false)
286                                 Expect(err).ShouldNot(HaveOccurred())
287                                 Expect(fields[customIndex]).To(BeEquivalentTo(expectedFields[customIndex]))
288                                 customIndex++
289                                 if customIndex >= len(expectedFields) {
290                                         // there is too much fields now for one UT...
291                                         return
292                                 }
293                         }
294                 }
295         }
296 }
297
298 func TestGeneratePackageHeader(t *testing.T) {
299         RegisterTestingT(t)
300         // prepare context
301         testCtx := new(context)
302         testCtx.packageName = "test-package-name"
303
304         // prepare input/output output files
305         inputData, err := readFile("testdata/acl.api.json")
306         Expect(err).ShouldNot(HaveOccurred())
307         inFile, err := parseJSON(inputData)
308         Expect(err).ShouldNot(HaveOccurred())
309         outDir := "test_output_directory"
310         outFile, err := os.Create(outDir)
311         Expect(err).ShouldNot(HaveOccurred())
312         defer os.RemoveAll(outDir)
313         // prepare writer
314         writer := bufio.NewWriter(outFile)
315         Expect(writer.Buffered()).To(BeZero())
316         generateHeader(testCtx, writer, inFile)
317         Expect(writer.Buffered()).ToNot(BeZero())
318 }
319
320 func TestGenerateMessageCommentType(t *testing.T) {
321         RegisterTestingT(t)
322         // prepare context
323         testCtx := new(context)
324         testCtx.packageName = "test-package-name"
325         testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
326
327         outDir := "test_output_directory"
328         outFile, err := os.Create(outDir)
329         Expect(err).ShouldNot(HaveOccurred())
330         writer := bufio.NewWriter(outFile)
331         defer os.RemoveAll(outDir)
332         Expect(writer.Buffered()).To(BeZero())
333         generateMessageComment(testCtx, writer, "test-struct", "msg-name", true)
334         Expect(writer.Buffered()).ToNot(BeZero())
335 }
336
337 func TestGenerateMessageCommentMessage(t *testing.T) {
338         RegisterTestingT(t)
339         // prepare context
340         testCtx := new(context)
341         testCtx.packageName = "test-package-name"
342         testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
343
344         outDir := "test_output_directory"
345         outFile, err := os.Create(outDir)
346         Expect(err).ShouldNot(HaveOccurred())
347         writer := bufio.NewWriter(outFile)
348         defer os.RemoveAll(outDir)
349         Expect(writer.Buffered()).To(BeZero())
350         generateMessageComment(testCtx, writer, "test-struct", "msg-name", false)
351         Expect(writer.Buffered()).ToNot(BeZero())
352 }
353
354 func TestGenerateMessageNameGetter(t *testing.T) {
355         RegisterTestingT(t)
356         outDir := "test_output_directory"
357         outFile, err := os.Create(outDir)
358         Expect(err).ShouldNot(HaveOccurred())
359         writer := bufio.NewWriter(outFile)
360         defer os.RemoveAll(outDir)
361         Expect(writer.Buffered()).To(BeZero())
362         generateMessageNameGetter(writer, "test-struct", "msg-name")
363         Expect(writer.Buffered()).ToNot(BeZero())
364 }
365
366 func TestGenerateTypeNameGetter(t *testing.T) {
367         RegisterTestingT(t)
368         outDir := "test_output_directory"
369         outFile, err := os.Create(outDir)
370         Expect(err).ShouldNot(HaveOccurred())
371         writer := bufio.NewWriter(outFile)
372         defer os.RemoveAll(outDir)
373         Expect(writer.Buffered()).To(BeZero())
374         generateTypeNameGetter(writer, "test-struct", "msg-name")
375         Expect(writer.Buffered()).ToNot(BeZero())
376 }
377
378 func TestGenerateCrcGetter(t *testing.T) {
379         RegisterTestingT(t)
380         outDir := "test_output_directory"
381         outFile, err := os.Create(outDir)
382         Expect(err).ShouldNot(HaveOccurred())
383         writer := bufio.NewWriter(outFile)
384         defer os.RemoveAll(outDir)
385         Expect(writer.Buffered()).To(BeZero())
386         generateCrcGetter(writer, "test-struct", "msg-name")
387         Expect(writer.Buffered()).ToNot(BeZero())
388 }
389
390 func TestTranslateVppType(t *testing.T) {
391         RegisterTestingT(t)
392         context := new(context)
393         typesToTranslate := []string{"u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f64"}
394         expected := []string{"uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64", "float64"}
395         var translated []string
396         for _, value := range typesToTranslate {
397                 translated = append(translated, convertToGoType(context, value, false))
398         }
399         for index, value := range expected {
400                 Expect(value).To(BeEquivalentTo(translated[index]))
401         }
402
403 }
404
405 func TestTranslateVppTypeArray(t *testing.T) {
406         RegisterTestingT(t)
407         context := new(context)
408         translated := convertToGoType(context, "u8", true)
409         Expect(translated).To(BeEquivalentTo("byte"))
410 }
411
412 func TestTranslateVppUnknownType(t *testing.T) {
413         defer func() {
414                 if recovery := recover(); recovery != nil {
415                         t.Logf("Recovered from panic: %v", recovery)
416                 }
417         }()
418         context := new(context)
419         convertToGoType(context, "?", false)
420 }
421
422 func TestCamelCase(t *testing.T) {
423         RegisterTestingT(t)
424         // test camel case functionality
425         expected := "allYourBaseAreBelongToUs"
426         result := camelCaseName("all_your_base_are_belong_to_us")
427         Expect(expected).To(BeEquivalentTo(result))
428         // test underscore
429         expected = "_"
430         result = camelCaseName(expected)
431         Expect(expected).To(BeEquivalentTo(result))
432         // test all lower
433         expected = "lower"
434         result = camelCaseName(expected)
435         Expect(expected).To(BeEquivalentTo(result))
436 }
437
438 func TestCommonInitialisms(t *testing.T) {
439         RegisterTestingT(t)
440
441         for key, value := range commonInitialisms {
442                 Expect(value).ShouldNot(BeFalse())
443                 Expect(key).ShouldNot(BeEmpty())
444         }
445 }
446 */