1 // Copyright (c) 2020 Cisco and/or its affiliates.
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:
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
18 "git.fd.io/govpp.git/examples/binapi/interfaces"
19 "git.fd.io/govpp.git/examples/binapi/ip_types"
24 . "github.com/onsi/gomega"
26 "git.fd.io/govpp.git/binapigen/vppapi"
29 const testOutputDir = "test_output_directory"
31 func GenerateFromFile(file, outputDir string, opts Options) error {
32 apifile, err := vppapi.ParseFile(file)
37 g, err := New(opts, []*vppapi.File{apifile})
41 for _, file := range g.Files {
45 GenerateBinapi(g, file, outputDir)
46 if file.Service != nil {
47 GenerateRPC(g, file, outputDir)
51 if err = g.Generate(); err != nil {
58 func TestGenerateFromFile(t *testing.T) {
61 // remove directory created during test
62 defer os.RemoveAll(testOutputDir)
64 err := GenerateFromFile("vppapi/testdata/acl.api.json", testOutputDir, Options{FilesToGenerate: []string{"acl"}})
65 Expect(err).ShouldNot(HaveOccurred())
66 fileInfo, err := os.Stat(testOutputDir + "/acl/acl.ba.go")
67 Expect(err).ShouldNot(HaveOccurred())
68 Expect(fileInfo.IsDir()).To(BeFalse())
69 Expect(fileInfo.Name()).To(BeEquivalentTo("acl.ba.go"))
72 func TestGenerateFromFileInputError(t *testing.T) {
75 err := GenerateFromFile("vppapi/testdata/nonexisting.json", testOutputDir, Options{})
76 Expect(err).Should(HaveOccurred())
77 Expect(err.Error()).To(ContainSubstring("unsupported"))
80 func TestGenerateFromFileReadJsonError(t *testing.T) {
83 err := GenerateFromFile("vppapi/testdata/input-read-json-error.json", testOutputDir, Options{})
84 Expect(err).Should(HaveOccurred())
85 Expect(err.Error()).To(ContainSubstring("unsupported"))
88 func TestGenerateFromFileGeneratePackageError(t *testing.T) {
91 // generate package throws panic, recover after it
93 if recovery := recover(); recovery != nil {
94 t.Logf("Recovered from panic: %v", recovery)
96 os.RemoveAll(testOutputDir)
99 err := GenerateFromFile("vppapi/testdata/input-generate-error.json", testOutputDir, Options{})
100 Expect(err).Should(HaveOccurred())
103 func TestGeneratedParseAddress(t *testing.T) {
106 var data = []struct {
108 result ip_types.Address
110 {"192.168.0.1", ip_types.Address{
111 Af: ip_types.ADDRESS_IP4,
112 Un: ip_types.AddressUnionIP4(ip_types.IP4Address{192, 168, 0, 1}),
114 {"aac1:0:ab45::", ip_types.Address{
115 Af: ip_types.ADDRESS_IP6,
116 Un: ip_types.AddressUnionIP6(ip_types.IP6Address{170, 193, 0, 0, 171, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
120 for _, entry := range data {
121 t.Run(entry.input, func(t *testing.T) {
122 parsedAddress, err := ip_types.ParseAddress(entry.input)
123 Expect(err).ShouldNot(HaveOccurred())
124 Expect(parsedAddress).To(Equal(entry.result))
126 originAddress := parsedAddress.ToString()
127 Expect(originAddress).To(Equal(entry.input))
132 func TestGeneratedParseAddressError(t *testing.T) {
135 _, err := ip_types.ParseAddress("malformed_ip")
136 Expect(err).Should(HaveOccurred())
139 func TestGeneratedParsePrefix(t *testing.T) {
142 var data = []struct {
144 result ip_types.Prefix
146 {"192.168.0.1/24", ip_types.Prefix{
147 Address: ip_types.Address{
148 Af: ip_types.ADDRESS_IP4,
149 Un: ip_types.AddressUnionIP4(ip_types.IP4Address{192, 168, 0, 1}),
153 {"192.168.0.1", ip_types.Prefix{
154 Address: ip_types.Address{
155 Af: ip_types.ADDRESS_IP4,
156 Un: ip_types.AddressUnionIP4(ip_types.IP4Address{192, 168, 0, 1}),
160 {"aac1:0:ab45::/96", ip_types.Prefix{
161 Address: ip_types.Address{
162 Af: ip_types.ADDRESS_IP6,
163 Un: ip_types.AddressUnionIP6(ip_types.IP6Address{170, 193, 0, 0, 171, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
167 {"aac1:0:ab45::", ip_types.Prefix{
168 Address: ip_types.Address{
169 Af: ip_types.ADDRESS_IP6,
170 Un: ip_types.AddressUnionIP6(ip_types.IP6Address{170, 193, 0, 0, 171, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
176 for _, entry := range data {
177 t.Run(entry.input, func(t *testing.T) {
178 parsedAddress, err := ip_types.ParsePrefix(entry.input)
179 Expect(err).ShouldNot(HaveOccurred())
180 Expect(parsedAddress).To(Equal(entry.result))
182 // Parsed IP without prefix receives a default one
183 // so the input data must be adjusted
184 if entry.result.Address.Af == ip_types.ADDRESS_IP4 && !strings.Contains(entry.input, "/") {
185 entry.input = entry.input + "/32"
187 if entry.result.Address.Af == ip_types.ADDRESS_IP6 && !strings.Contains(entry.input, "/") {
188 entry.input = entry.input + "/128"
190 originAddress := parsedAddress.ToString()
191 Expect(originAddress).To(Equal(entry.input))
196 func TestGeneratedParsePrefixError(t *testing.T) {
199 _, err := ip_types.ParsePrefix("malformed_ip")
200 Expect(err).Should(HaveOccurred())
203 func TestGeneratedParseMAC(t *testing.T) {
206 var data = []struct {
208 result interfaces.MacAddress
210 {"b7:b9:bb:a1:5c:af", interfaces.MacAddress{183, 185, 187, 161, 92, 175}},
211 {"47:4b:c7:3e:06:c8", interfaces.MacAddress{71, 75, 199, 62, 6, 200}},
212 {"a7:cc:9f:10:18:e3", interfaces.MacAddress{167, 204, 159, 16, 24, 227}},
215 for _, entry := range data {
216 t.Run(entry.input, func(t *testing.T) {
217 parsedMac, err := interfaces.ParseMAC(entry.input)
218 Expect(err).ShouldNot(HaveOccurred())
219 Expect(parsedMac).To(Equal(entry.result))
221 originAddress := parsedMac.ToString()
222 Expect(originAddress).To(Equal(entry.input))
227 func TestGeneratedParseMACError(t *testing.T) {
230 _, err := interfaces.ParseMAC("malformed_mac")
231 Expect(err).Should(HaveOccurred())
234 /*func TestGetContext(t *testing.T) {
236 outDir := "test_output_directory"
237 result, err := newContext("testdata/af_packet.api.json", outDir)
238 Expect(err).ShouldNot(HaveOccurred())
239 Expect(result).ToNot(BeNil())
240 Expect(result.outputFile).To(BeEquivalentTo(outDir + "/af_packet/af_packet.ba.go"))
243 func TestGetContextNoJsonFile(t *testing.T) {
245 outDir := "test_output_directory"
246 result, err := newContext("testdata/input.txt", outDir)
247 Expect(err).Should(HaveOccurred())
248 Expect(err.Error()).To(ContainSubstring("invalid input file name"))
249 Expect(result).To(BeNil())
252 func TestGetContextInterfaceJson(t *testing.T) {
254 outDir := "test_output_directory"
255 result, err := newContext("testdata/ip.api.json", outDir)
256 Expect(err).ShouldNot(HaveOccurred())
257 Expect(result).ToNot(BeNil())
258 Expect(result.outputFile)
259 Expect(result.outputFile).To(BeEquivalentTo(outDir + "/ip/ip.ba.go"))
262 /*func TestGeneratePackage(t *testing.T) {
265 testCtx := new(GenFile)
266 testCtx.packageName = "test-package-name"
268 // prepare input/output output files
269 inputData, err := ioutil.ReadFile("testdata/ip.api.json")
270 Expect(err).ShouldNot(HaveOccurred())
271 jsonRoot, err := parseInputJSON(inputData)
272 Expect(err).ShouldNot(HaveOccurred())
273 testCtx.file, err = parseModule(testCtx, jsonRoot)
274 Expect(err).ShouldNot(HaveOccurred())
275 outDir := "test_output_directory"
276 outFile, err := os.Create(outDir)
277 Expect(err).ShouldNot(HaveOccurred())
278 defer os.RemoveAll(outDir)
281 writer := bufio.NewWriter(outFile)
282 Expect(writer.Buffered()).To(BeZero())
283 err = generateFileBinapi(testCtx, writer)
284 Expect(err).ShouldNot(HaveOccurred())
287 func TestGenerateMessageType(t *testing.T) {
290 testCtx := new(GenFile)
291 testCtx.packageName = "test-package-name"
293 // prepare input/output output files
294 inputData, err := ioutil.ReadFile("testdata/ip.api.json")
295 Expect(err).ShouldNot(HaveOccurred())
296 jsonRoot, err := parseInputJSON(inputData)
297 Expect(err).ShouldNot(HaveOccurred())
298 outDir := "test_output_directory"
299 outFile, err := os.Create(outDir)
300 Expect(err).ShouldNot(HaveOccurred())
301 testCtx.file, err = parseModule(testCtx, jsonRoot)
302 Expect(err).ShouldNot(HaveOccurred())
303 defer os.RemoveAll(outDir)
306 writer := bufio.NewWriter(outFile)
308 for _, msg := range testCtx.file.Messages {
309 generateMessage(testCtx, writer, &msg)
310 Expect(writer.Buffered()).ToNot(BeZero())
314 /*func TestGenerateMessageName(t *testing.T) {
317 testCtx := new(context)
318 testCtx.packageName = "test-package-name"
320 // prepare input/output output files
321 inputData, err := readFile("testdata/ip.api.json")
322 Expect(err).ShouldNot(HaveOccurred())
323 testCtx.inputBuff = bytes.NewBuffer(inputData)
324 inFile, _ := parseJSON(inputData)
325 outDir := "test_output_directory"
326 outFile, err := os.Create(outDir)
327 Expect(err).ShouldNot(HaveOccurred())
328 defer os.RemoveAll(outDir)
331 writer := bufio.NewWriter(outFile)
333 types := inFile.Map("types")
334 Expect(types.Len()).To(BeEquivalentTo(1))
335 for i := 0; i < types.Len(); i++ {
337 Expect(writer.Buffered()).To(BeZero())
338 err := generateMessage(testCtx, writer, typ, false)
339 Expect(err).ShouldNot(HaveOccurred())
340 Expect(writer.Buffered()).ToNot(BeZero())
345 func TestGenerateMessageFieldTypes(t *testing.T) {
346 // expected results according to acl.api.json in testdata
347 expectedTypes := []string{
350 "\tSrcIPAddr []byte `struc:\"[16]byte\"`",
351 "\tSrcIPPrefixLen uint8",
352 "\tDstIPAddr []byte `struc:\"[16]byte\"`",
353 "\tDstIPPrefixLen uint8",
355 "\tSrcportOrIcmptypeFirst uint16",
356 "\tSrcportOrIcmptypeLast uint16",
357 "\tDstportOrIcmpcodeFirst uint16",
358 "\tDstportOrIcmpcodeLast uint16",
359 "\tTCPFlagsMask uint8",
360 "\tTCPFlagsValue uint8"}
363 testCtx := new(context)
364 testCtx.packageName = "test-package-name"
366 // prepare input/output output files
367 inputData, err := readFile("testdata/acl.api.json")
368 Expect(err).ShouldNot(HaveOccurred())
369 inFile, err := parseJSON(inputData)
370 Expect(err).ShouldNot(HaveOccurred())
371 Expect(inFile).ToNot(BeNil())
374 types := inFile.Map("types")
375 fields := make([]string, 0)
376 for i := 0; i < types.Len(); i++ {
377 for j := 0; j < types.At(i).Len(); j++ {
378 field := types.At(i).At(j)
379 if field.GetType() == jsongo.TypeArray {
380 err := processMessageField(testCtx, &fields, field, false)
381 Expect(err).ShouldNot(HaveOccurred())
382 Expect(fields[j-1]).To(BeEquivalentTo(expectedTypes[j-1]))
388 func TestGenerateMessageFieldMessages(t *testing.T) {
389 // expected results according to acl.api.json in testdata
390 expectedFields := []string{"\tMajor uint32", "\tMinor uint32", "\tRetval int32",
391 "\tVpePid uint32", "\tACLIndex uint32", "\tTag []byte `struc:\"[64]byte\"`",
395 testCtx := new(context)
396 testCtx.packageName = "test-package-name"
398 // prepare input/output output files
399 inputData, err := readFile("testdata/acl.api.json")
400 Expect(err).ShouldNot(HaveOccurred())
401 inFile, err := parseJSON(inputData)
402 Expect(err).ShouldNot(HaveOccurred())
403 Expect(inFile).ToNot(BeNil())
405 // test message fields
406 messages := inFile.Map("messages")
408 fields := make([]string, 0)
409 for i := 0; i < messages.Len(); i++ {
410 for j := 0; j < messages.At(i).Len(); j++ {
411 field := messages.At(i).At(j)
412 if field.GetType() == jsongo.TypeArray {
413 specificFieldName := field.At(1).Get().(string)
414 if specificFieldName == "crc" || specificFieldName == "_vl_msg_id" ||
415 specificFieldName == "client_index" || specificFieldName == "context" {
418 err := processMessageField(testCtx, &fields, field, false)
419 Expect(err).ShouldNot(HaveOccurred())
420 Expect(fields[customIndex]).To(BeEquivalentTo(expectedFields[customIndex]))
422 if customIndex >= len(expectedFields) {
423 // there is too much fields now for one UT...
431 func TestGeneratePackageHeader(t *testing.T) {
434 testCtx := new(context)
435 testCtx.packageName = "test-package-name"
437 // prepare input/output output files
438 inputData, err := readFile("testdata/acl.api.json")
439 Expect(err).ShouldNot(HaveOccurred())
440 inFile, err := parseJSON(inputData)
441 Expect(err).ShouldNot(HaveOccurred())
442 outDir := "test_output_directory"
443 outFile, err := os.Create(outDir)
444 Expect(err).ShouldNot(HaveOccurred())
445 defer os.RemoveAll(outDir)
447 writer := bufio.NewWriter(outFile)
448 Expect(writer.Buffered()).To(BeZero())
449 generatePackageHeader(testCtx, writer, inFile)
450 Expect(writer.Buffered()).ToNot(BeZero())
453 func TestGenerateMessageCommentType(t *testing.T) {
456 testCtx := new(context)
457 testCtx.packageName = "test-package-name"
458 testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
460 outDir := "test_output_directory"
461 outFile, err := os.Create(outDir)
462 Expect(err).ShouldNot(HaveOccurred())
463 writer := bufio.NewWriter(outFile)
464 defer os.RemoveAll(outDir)
465 Expect(writer.Buffered()).To(BeZero())
466 generateMessageComment(testCtx, writer, "test-struct", "msg-name", true)
467 Expect(writer.Buffered()).ToNot(BeZero())
470 func TestGenerateMessageCommentMessage(t *testing.T) {
473 testCtx := new(context)
474 testCtx.packageName = "test-package-name"
475 testCtx.inputBuff = bytes.NewBuffer([]byte("test content"))
477 outDir := "test_output_directory"
478 outFile, err := os.Create(outDir)
479 Expect(err).ShouldNot(HaveOccurred())
480 writer := bufio.NewWriter(outFile)
481 defer os.RemoveAll(outDir)
482 Expect(writer.Buffered()).To(BeZero())
483 generateMessageComment(testCtx, writer, "test-struct", "msg-name", false)
484 Expect(writer.Buffered()).ToNot(BeZero())
487 func TestGenerateMessageNameGetter(t *testing.T) {
489 outDir := "test_output_directory"
490 outFile, err := os.Create(outDir)
491 Expect(err).ShouldNot(HaveOccurred())
492 writer := bufio.NewWriter(outFile)
493 defer os.RemoveAll(outDir)
494 Expect(writer.Buffered()).To(BeZero())
495 generateMessageNameGetter(writer, "test-struct", "msg-name")
496 Expect(writer.Buffered()).ToNot(BeZero())
499 func TestGenerateTypeNameGetter(t *testing.T) {
501 outDir := "test_output_directory"
502 outFile, err := os.Create(outDir)
503 Expect(err).ShouldNot(HaveOccurred())
504 writer := bufio.NewWriter(outFile)
505 defer os.RemoveAll(outDir)
506 Expect(writer.Buffered()).To(BeZero())
507 generateTypeNameGetter(writer, "test-struct", "msg-name")
508 Expect(writer.Buffered()).ToNot(BeZero())
511 func TestGenerateCrcGetter(t *testing.T) {
513 outDir := "test_output_directory"
514 outFile, err := os.Create(outDir)
515 Expect(err).ShouldNot(HaveOccurred())
516 writer := bufio.NewWriter(outFile)
517 defer os.RemoveAll(outDir)
518 Expect(writer.Buffered()).To(BeZero())
519 generateCrcGetter(writer, "test-struct", "msg-name")
520 Expect(writer.Buffered()).ToNot(BeZero())
523 func TestTranslateVppType(t *testing.T) {
525 context := new(context)
526 typesToTranslate := []string{"u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f64"}
527 expected := []string{"uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64", "float64"}
528 var translated []string
529 for _, value := range typesToTranslate {
530 translated = append(translated, convertToGoType(context, value, false))
532 for index, value := range expected {
533 Expect(value).To(BeEquivalentTo(translated[index]))
538 func TestTranslateVppTypeArray(t *testing.T) {
540 context := new(context)
541 translated := convertToGoType(context, "u8", true)
542 Expect(translated).To(BeEquivalentTo("byte"))
545 func TestTranslateVppUnknownType(t *testing.T) {
547 if recovery := recover(); recovery != nil {
548 t.Logf("Recovered from panic: %v", recovery)
551 context := new(context)
552 convertToGoType(context, "?", false)
555 func TestCamelCase(t *testing.T) {
557 // test camel case functionality
558 expected := "allYourBaseAreBelongToUs"
559 result := camelCaseName("all_your_base_are_belong_to_us")
560 Expect(expected).To(BeEquivalentTo(result))
563 result = camelCaseName(expected)
564 Expect(expected).To(BeEquivalentTo(result))
567 result = camelCaseName(expected)
568 Expect(expected).To(BeEquivalentTo(result))
571 func TestCommonInitialisms(t *testing.T) {
574 for key, value := range commonInitialisms {
575 Expect(value).ShouldNot(BeFalse())
576 Expect(key).ShouldNot(BeEmpty())