c7d89cefd5b529699e45be0df0b7fddafc78dd86
[govpp.git] / cmd / binapi-generator / generator_test.go
1 // Copyright (c) 2017 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 main
16
17 import (
18         "bufio"
19         "bytes"
20         "os"
21         "testing"
22
23         "github.com/bennyscetbun/jsongo"
24         . "github.com/onsi/gomega"
25 )
26
27 func TestGetInputFiles(t *testing.T) {
28         RegisterTestingT(t)
29         result, err := getInputFiles("testdata")
30         Expect(err).ShouldNot(HaveOccurred())
31         Expect(result).To(HaveLen(5))
32         for _, file := range result {
33                 Expect(file).To(BeAnExistingFile())
34         }
35 }
36
37 func TestGetInputFilesError(t *testing.T) {
38         RegisterTestingT(t)
39         result, err := getInputFiles("nonexisting_directory")
40         Expect(err).Should(HaveOccurred())
41         Expect(result).To(BeNil())
42 }
43
44 func TestGenerateFromFile(t *testing.T) {
45         RegisterTestingT(t)
46         outDir := "test_output_directory"
47         // remove directory created during test
48         defer os.RemoveAll(outDir)
49         err := generateFromFile("testdata/acl.api.json", outDir)
50         Expect(err).ShouldNot(HaveOccurred())
51         fileInfo, err := os.Stat(outDir + "/acl/acl.go")
52         Expect(err).ShouldNot(HaveOccurred())
53         Expect(fileInfo.IsDir()).To(BeFalse())
54         Expect(fileInfo.Name()).To(BeEquivalentTo("acl.go"))
55 }
56
57 func TestGenerateFromFileInputError(t *testing.T) {
58         RegisterTestingT(t)
59         outDir := "test_output_directory"
60         err := generateFromFile("testdata/nonexisting.json", outDir)
61         Expect(err).Should(HaveOccurred())
62         Expect(err.Error()).To(ContainSubstring("reading data from file failed"))
63 }
64
65 func TestGenerateFromFileReadJsonError(t *testing.T) {
66         RegisterTestingT(t)
67         outDir := "test_output_directory"
68         err := generateFromFile("testdata/input-read-json-error.json", outDir)
69         Expect(err).Should(HaveOccurred())
70         Expect(err.Error()).To(ContainSubstring("JSON unmarshall failed"))
71 }
72
73 func TestGenerateFromFileGeneratePackageError(t *testing.T) {
74         RegisterTestingT(t)
75         outDir := "test_output_directory"
76         // generate package throws panic, recover after it
77         defer func() {
78                 if recovery := recover(); recovery != nil {
79                         t.Logf("Recovered from panic: %v", recovery)
80                 }
81                 os.RemoveAll(outDir)
82         }()
83         err := generateFromFile("testdata/input-generate-error.json", outDir)
84         Expect(err).Should(HaveOccurred())
85 }
86
87 func TestGetContext(t *testing.T) {
88         RegisterTestingT(t)
89         outDir := "test_output_directory"
90         result, err := getContext("testdata/af_packet.api.json", outDir)
91         Expect(err).ShouldNot(HaveOccurred())
92         Expect(result).ToNot(BeNil())
93         Expect(result.outputFile).To(BeEquivalentTo(outDir + "/af_packet/af_packet.go"))
94 }
95
96 func TestGetContextNoJsonFile(t *testing.T) {
97         RegisterTestingT(t)
98         outDir := "test_output_directory"
99         result, err := getContext("testdata/input.txt", outDir)
100         Expect(err).Should(HaveOccurred())
101         Expect(err.Error()).To(ContainSubstring("invalid input file name"))
102         Expect(result).To(BeNil())
103 }
104
105 func TestGetContextInterfaceJson(t *testing.T) {
106         RegisterTestingT(t)
107         outDir := "test_output_directory"
108         result, err := getContext("testdata/interface.json", outDir)
109         Expect(err).ShouldNot(HaveOccurred())
110         Expect(result).ToNot(BeNil())
111         Expect(result.outputFile)
112         Expect(result.outputFile).To(BeEquivalentTo(outDir + "/interfaces/interfaces.go"))
113
114 }
115
116 func TestReadJson(t *testing.T) {
117         RegisterTestingT(t)
118         inputData, err := readFile("testdata/af_packet.api.json")
119         Expect(err).ShouldNot(HaveOccurred())
120         result, err := parseJSON(inputData)
121         Expect(err).ShouldNot(HaveOccurred())
122         Expect(result).ToNot(BeNil())
123         Expect(result.Len()).To(BeEquivalentTo(3))
124 }
125
126 func TestReadJsonError(t *testing.T) {
127         RegisterTestingT(t)
128         inputData, err := readFile("testdata/input-read-json-error.json")
129         Expect(err).ShouldNot(HaveOccurred())
130         result, err := parseJSON(inputData)
131         Expect(err).Should(HaveOccurred())
132         Expect(err.Error()).To(ContainSubstring("JSON unmarshall failed"))
133         Expect(result).To(BeNil())
134 }
135
136 func TestGeneratePackage(t *testing.T) {
137         RegisterTestingT(t)
138         // prepare context
139         testCtx := new(context)
140         testCtx.packageName = "test-package-name"
141
142         // prepare input/output output files
143         inputData, err := readFile("testdata/ip.api.json")
144         Expect(err).ShouldNot(HaveOccurred())
145         testCtx.inputBuff = bytes.NewBuffer(inputData)
146         inFile, _ := parseJSON(inputData)
147         outDir := "test_output_directory"
148         outFile, _ := os.Create(outDir)
149         defer os.RemoveAll(outDir)
150
151         // prepare writer
152         writer := bufio.NewWriter(outFile)
153         Expect(writer.Buffered()).To(BeZero())
154         err = generatePackage(testCtx, writer, inFile)
155         Expect(err).ShouldNot(HaveOccurred())
156 }
157
158 func TestGenerateMessageType(t *testing.T) {
159         RegisterTestingT(t)
160         // prepare context
161         testCtx := new(context)
162         testCtx.packageName = "test-package-name"
163
164         // prepare input/output output files
165         inputData, err := readFile("testdata/ip.api.json")
166         Expect(err).ShouldNot(HaveOccurred())
167         testCtx.inputBuff = bytes.NewBuffer(inputData)
168         inFile, _ := parseJSON(inputData)
169         outDir := "test_output_directory"
170         outFile, _ := os.Create(outDir)
171         defer os.RemoveAll(outDir)
172
173         // prepare writer
174         writer := bufio.NewWriter(outFile)
175
176         types := inFile.Map("types")
177         testCtx.types = map[string]string{
178                 "u32": "sw_if_index",
179                 "u8":  "weight",
180         }
181         Expect(types.Len()).To(BeEquivalentTo(1))
182         for i := 0; i < types.Len(); i++ {
183                 typ := types.At(i)
184                 Expect(writer.Buffered()).To(BeZero())
185                 err := generateMessage(testCtx, writer, typ, true)
186                 Expect(err).ShouldNot(HaveOccurred())
187                 Expect(writer.Buffered()).ToNot(BeZero())
188
189         }
190 }
191
192 func TestGenerateMessageName(t *testing.T) {
193         RegisterTestingT(t)
194         // prepare context
195         testCtx := new(context)
196         testCtx.packageName = "test-package-name"
197
198         // prepare input/output output files
199         inputData, err := readFile("testdata/ip.api.json")
200         Expect(err).ShouldNot(HaveOccurred())
201         testCtx.inputBuff = bytes.NewBuffer(inputData)
202         inFile, _ := parseJSON(inputData)
203         outDir := "test_output_directory"
204         outFile, err := os.Create(outDir)
205         Expect(err).ShouldNot(HaveOccurred())
206         defer os.RemoveAll(outDir)
207
208         // prepare writer
209         writer := bufio.NewWriter(outFile)
210
211         types := inFile.Map("types")
212         Expect(types.Len()).To(BeEquivalentTo(1))
213         for i := 0; i < types.Len(); i++ {
214                 typ := types.At(i)
215                 Expect(writer.Buffered()).To(BeZero())
216                 err := generateMessage(testCtx, writer, typ, false)
217                 Expect(err).ShouldNot(HaveOccurred())
218                 Expect(writer.Buffered()).ToNot(BeZero())
219
220         }
221 }
222
223 func TestGenerateMessageFieldTypes(t *testing.T) {
224         // expected results according to acl.api.json in testdata
225         expectedTypes := []string{"\tIsPermit uint8", "\tIsIpv6 uint8", "\tSrcIPAddr []byte     `struc:\"[16]byte\"`",
226                 "\tSrcIPPrefixLen uint8", "\tDstIPAddr []byte   `struc:\"[16]byte\"`", "\tDstIPPrefixLen uint8", "\tProto uint8",
227                 "\tSrcportOrIcmptypeFirst uint16", "\tSrcportOrIcmptypeLast uint16", "\tDstportOrIcmpcodeFirst uint16",
228                 "\tDstportOrIcmpcodeLast uint16", "\tTCPFlagsMask uint8", "\tTCPFlagsValue uint8"}
229         RegisterTestingT(t)
230         // prepare context
231         testCtx := new(context)
232         testCtx.packageName = "test-package-name"
233
234         // prepare input/output output files
235         inputData, err := readFile("testdata/acl.api.json")
236         Expect(err).ShouldNot(HaveOccurred())
237         inFile, _ := parseJSON(inputData)
238         Expect(err).ShouldNot(HaveOccurred())
239         Expect(inFile).ToNot(BeNil())
240
241         // test types
242         types := inFile.Map("types")
243         fields := make([]string, 0)
244         for i := 0; i < types.Len(); i++ {
245                 for j := 0; j < types.At(i).Len(); j++ {
246                         field := types.At(i).At(j)
247                         if jsongo.TypeArray == field.GetType() {
248                                 err := processMessageField(testCtx, &fields, field, otherMessage)
249                                 Expect(err).ShouldNot(HaveOccurred())
250                                 Expect(fields[j-1]).To(BeEquivalentTo(expectedTypes[j-1]))
251                         }
252                 }
253         }
254 }
255
256 func TestGenerateMessageFieldMessages(t *testing.T) {
257         // expected results according to acl.api.json in testdata
258         expectedTypes := []string{"\tMajor uint32", "\tMinor uint32", "\tACLIndex uint32",
259                 "\tTag []byte   `struc:\"[64]byte\"`", "\tACLIndex uint32", "\tRetval int32", "\tACLIndex 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 types
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 jsongo.TypeArray == field.GetType() {
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, requestMessage)
286                                 Expect(err).ShouldNot(HaveOccurred())
287                                 Expect(fields[customIndex]).To(BeEquivalentTo(expectedTypes[customIndex]))
288                                 customIndex++
289                         }
290                 }
291         }
292 }
293
294 func TestGeneratePackageHeader(t *testing.T) {
295         RegisterTestingT(t)
296         // prepare context
297         testCtx := new(context)
298         testCtx.packageName = "test-package-name"
299
300         // prepare input/output output files
301         inputData, err := readFile("testdata/acl.api.json")
302         Expect(err).ShouldNot(HaveOccurred())
303         inFile, err := parseJSON(inputData)
304         Expect(err).ShouldNot(HaveOccurred())
305         outDir := "test_output_directory"
306         outFile, err := os.Create(outDir)
307         Expect(err).ShouldNot(HaveOccurred())
308         defer os.RemoveAll(outDir)
309         // prepare writer
310         writer := bufio.NewWriter(outFile)
311         Expect(writer.Buffered()).To(BeZero())
312         generatePackageHeader(testCtx, writer, inFile)
313         Expect(writer.Buffered()).ToNot(BeZero())
314 }
315
316 func TestGenerateMessageCommentType(t *testing.T) {
317         RegisterTestingT(t)
318         // prepare context
319         testCtx := new(context)
320         testCtx.packageName = "test-package-name"
321         testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
322
323         outDir := "test_output_directory"
324         outFile, err := os.Create(outDir)
325         Expect(err).ShouldNot(HaveOccurred())
326         writer := bufio.NewWriter(outFile)
327         defer os.RemoveAll(outDir)
328         Expect(writer.Buffered()).To(BeZero())
329         generateMessageComment(testCtx, writer, "test-struct", "msg-name", true)
330         Expect(writer.Buffered()).ToNot(BeZero())
331 }
332
333 func TestGenerateMessageCommentMessage(t *testing.T) {
334         RegisterTestingT(t)
335         // prepare context
336         testCtx := new(context)
337         testCtx.packageName = "test-package-name"
338         testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
339
340         outDir := "test_output_directory"
341         outFile, err := os.Create(outDir)
342         Expect(err).ShouldNot(HaveOccurred())
343         writer := bufio.NewWriter(outFile)
344         defer os.RemoveAll(outDir)
345         Expect(writer.Buffered()).To(BeZero())
346         generateMessageComment(testCtx, writer, "test-struct", "msg-name", false)
347         Expect(writer.Buffered()).ToNot(BeZero())
348 }
349
350 func TestGenerateMessageNameGetter(t *testing.T) {
351         RegisterTestingT(t)
352         outDir := "test_output_directory"
353         outFile, err := os.Create(outDir)
354         Expect(err).ShouldNot(HaveOccurred())
355         writer := bufio.NewWriter(outFile)
356         defer os.RemoveAll(outDir)
357         Expect(writer.Buffered()).To(BeZero())
358         generateMessageNameGetter(writer, "test-struct", "msg-name")
359         Expect(writer.Buffered()).ToNot(BeZero())
360 }
361
362 func TestGenerateTypeNameGetter(t *testing.T) {
363         RegisterTestingT(t)
364         outDir := "test_output_directory"
365         outFile, err := os.Create(outDir)
366         Expect(err).ShouldNot(HaveOccurred())
367         writer := bufio.NewWriter(outFile)
368         defer os.RemoveAll(outDir)
369         Expect(writer.Buffered()).To(BeZero())
370         generateTypeNameGetter(writer, "test-struct", "msg-name")
371         Expect(writer.Buffered()).ToNot(BeZero())
372 }
373
374 func TestGenerateCrcGetter(t *testing.T) {
375         RegisterTestingT(t)
376         outDir := "test_output_directory"
377         outFile, err := os.Create(outDir)
378         Expect(err).ShouldNot(HaveOccurred())
379         writer := bufio.NewWriter(outFile)
380         defer os.RemoveAll(outDir)
381         Expect(writer.Buffered()).To(BeZero())
382         generateCrcGetter(writer, "test-struct", "msg-name")
383         Expect(writer.Buffered()).ToNot(BeZero())
384 }
385
386 func TestTranslateVppType(t *testing.T) {
387         RegisterTestingT(t)
388         context := new(context)
389         typesToTranslate := []string{"u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f64"}
390         expected := []string{"uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64", "float64"}
391         translated := []string{}
392         for _, value := range typesToTranslate {
393                 translated = append(translated, translateVppType(context, value, false))
394         }
395         for index, value := range expected {
396                 Expect(value).To(BeEquivalentTo(translated[index]))
397         }
398
399 }
400
401 func TestTranslateVppTypeArray(t *testing.T) {
402         RegisterTestingT(t)
403         context := new(context)
404         translated := translateVppType(context, "u8", true)
405         Expect(translated).To(BeEquivalentTo("byte"))
406 }
407
408 func TestTranslateVppUnknownType(t *testing.T) {
409         defer func() {
410                 if recovery := recover(); recovery != nil {
411                         t.Logf("Recovered from panic: %v", recovery)
412                 }
413         }()
414         context := new(context)
415         translateVppType(context, "?", false)
416 }
417
418 func TestCamelCase(t *testing.T) {
419         RegisterTestingT(t)
420         // test camel case functionality
421         expected := "allYourBaseAreBelongToUs"
422         result := camelCaseName("all_your_base_are_belong_to_us")
423         Expect(expected).To(BeEquivalentTo(result))
424         // test underscore
425         expected = "_"
426         result = camelCaseName(expected)
427         Expect(expected).To(BeEquivalentTo(result))
428         // test all lower
429         expected = "lower"
430         result = camelCaseName(expected)
431         Expect(expected).To(BeEquivalentTo(result))
432 }
433
434 func TestCommonInitialisms(t *testing.T) {
435         RegisterTestingT(t)
436
437         for key, value := range commonInitialisms {
438                 Expect(value).ShouldNot(BeFalse())
439                 Expect(key).ShouldNot(BeEmpty())
440         }
441 }