github.com/jordan-bonecutter/can-go@v0.0.0-20230901155856-d83995b18e50/pkg/dbc/parser_test.go (about)

     1  package dbc
     2  
     3  import (
     4  	"os"
     5  	"strings"
     6  	"testing"
     7  	"text/scanner"
     8  
     9  	"github.com/davecgh/go-spew/spew"
    10  	"gotest.tools/v3/assert"
    11  )
    12  
    13  func shouldUpdateGoldenFiles() bool {
    14  	return os.Getenv("GOLDEN") == "true"
    15  }
    16  
    17  func TestParse_ExampleDBC(t *testing.T) {
    18  	const inputFile = "../../testdata/dbc/example/example.dbc"
    19  	const goldenFile = "../../testdata/dbc/example/example.dbc.golden"
    20  	data, err := os.ReadFile(inputFile)
    21  	assert.NilError(t, err)
    22  	p := NewParser(inputFile, data)
    23  	assert.NilError(t, p.Parse())
    24  	if shouldUpdateGoldenFiles() {
    25  		assert.NilError(t, os.WriteFile(goldenFile, []byte(dump(p.Defs())), 0o600))
    26  	}
    27  	goldenFileData, err := os.ReadFile(goldenFile)
    28  	assert.NilError(t, err)
    29  	assert.Equal(t, string(goldenFileData), dump(p.Defs()))
    30  }
    31  
    32  func TestParser_Parse(t *testing.T) {
    33  	for _, tt := range []struct {
    34  		name string
    35  		text string
    36  		defs []Def
    37  	}{
    38  		{
    39  			name: "version.dbc",
    40  			text: `VERSION "foo"`,
    41  			defs: []Def{
    42  				&VersionDef{
    43  					Pos: scanner.Position{
    44  						Filename: "version.dbc",
    45  						Line:     1,
    46  						Column:   1,
    47  					},
    48  					Version: "foo",
    49  				},
    50  			},
    51  		},
    52  
    53  		{
    54  			name: "multiple_version.dbc",
    55  			text: strings.Join([]string{
    56  				`VERSION "foo"`,
    57  				`VERSION "bar"`,
    58  			}, "\n"),
    59  			defs: []Def{
    60  				&VersionDef{
    61  					Pos: scanner.Position{
    62  						Filename: "multiple_version.dbc",
    63  						Line:     1,
    64  						Column:   1,
    65  					},
    66  					Version: "foo",
    67  				},
    68  				&VersionDef{
    69  					Pos: scanner.Position{
    70  						Filename: "multiple_version.dbc",
    71  						Line:     2,
    72  						Column:   1,
    73  						Offset:   14,
    74  					},
    75  					Version: "bar",
    76  				},
    77  			},
    78  		},
    79  
    80  		{
    81  			name: "no_bus_speed.dbc",
    82  			text: `BS_:`,
    83  			defs: []Def{
    84  				&BitTimingDef{
    85  					Pos: scanner.Position{
    86  						Filename: "no_bus_speed.dbc",
    87  						Line:     1,
    88  						Column:   1,
    89  					},
    90  				},
    91  			},
    92  		},
    93  
    94  		{
    95  			name: "bus_speed.dbc",
    96  			text: `BS_: 250000`,
    97  			defs: []Def{
    98  				&BitTimingDef{
    99  					Pos: scanner.Position{
   100  						Filename: "bus_speed.dbc",
   101  						Line:     1,
   102  						Column:   1,
   103  					},
   104  					BaudRate: 250000,
   105  				},
   106  			},
   107  		},
   108  
   109  		{
   110  			name: "symbols.dbc",
   111  			text: strings.Join([]string{
   112  				"NS_ :",
   113  				"NS_DESC_",
   114  				"CM_",
   115  				"BA_DEF_",
   116  				"BA_",
   117  				"VAL_",
   118  			}, "\n\t"),
   119  			defs: []Def{
   120  				&NewSymbolsDef{
   121  					Pos: scanner.Position{
   122  						Filename: "symbols.dbc",
   123  						Line:     1,
   124  						Column:   1,
   125  					},
   126  					Symbols: []Keyword{
   127  						"NS_DESC_",
   128  						"CM_",
   129  						"BA_DEF_",
   130  						"BA_",
   131  						"VAL_",
   132  					},
   133  				},
   134  			},
   135  		},
   136  
   137  		{
   138  			name: "standard_message.dbc",
   139  			text: "BO_ 804 CRUISE: 8 PCM",
   140  			defs: []Def{
   141  				&MessageDef{
   142  					Pos: scanner.Position{
   143  						Filename: "standard_message.dbc",
   144  						Line:     1,
   145  						Column:   1,
   146  					},
   147  					Name:        "CRUISE",
   148  					MessageID:   804,
   149  					Size:        8,
   150  					Transmitter: "PCM",
   151  				},
   152  			},
   153  		},
   154  
   155  		{
   156  			name: "extended_message.dbc",
   157  			text: "BO_ 2566857412 BMS2_4: 8 Vector__XXX",
   158  			defs: []Def{
   159  				&MessageDef{
   160  					Pos: scanner.Position{
   161  						Filename: "extended_message.dbc",
   162  						Line:     1,
   163  						Column:   1,
   164  					},
   165  					Name:        "BMS2_4",
   166  					MessageID:   2566857412,
   167  					Size:        8,
   168  					Transmitter: "Vector__XXX",
   169  				},
   170  			},
   171  		},
   172  
   173  		{
   174  			name: "signal.dbc",
   175  			text: `SG_ CellTempLowest : 32|8@0+ (1,-40) [-40|215] "C" Vector__XXX`,
   176  			defs: []Def{
   177  				&SignalDef{
   178  					Pos: scanner.Position{
   179  						Filename: "signal.dbc",
   180  						Line:     1,
   181  						Column:   1,
   182  					},
   183  					Name:        "CellTempLowest",
   184  					StartBit:    32,
   185  					Size:        8,
   186  					IsBigEndian: true,
   187  					Factor:      1,
   188  					Offset:      -40,
   189  					Minimum:     -40,
   190  					Maximum:     215,
   191  					Unit:        "C",
   192  					Receivers:   []Identifier{"Vector__XXX"},
   193  				},
   194  			},
   195  		},
   196  
   197  		{
   198  			name: "multiplexer_signal.dbc",
   199  			text: `SG_ TestSignal M : 56|8@1+ (0.001,0) [0|0.255] "l/mm" XXX`,
   200  			defs: []Def{
   201  				&SignalDef{
   202  					Pos: scanner.Position{
   203  						Filename: "multiplexer_signal.dbc",
   204  						Line:     1,
   205  						Column:   1,
   206  					},
   207  					Name:                "TestSignal",
   208  					StartBit:            56,
   209  					Size:                8,
   210  					Factor:              0.001,
   211  					Offset:              0,
   212  					Minimum:             0,
   213  					Maximum:             0.255,
   214  					Unit:                "l/mm",
   215  					Receivers:           []Identifier{"XXX"},
   216  					IsMultiplexerSwitch: true,
   217  				},
   218  			},
   219  		},
   220  
   221  		{
   222  			name: "multiplexed_signal.dbc",
   223  			text: `SG_ TestSignal m2 : 56|8@1+ (0.001,0) [0|0.255] "l/mm" XXX`,
   224  			defs: []Def{
   225  				&SignalDef{
   226  					Pos: scanner.Position{
   227  						Filename: "multiplexed_signal.dbc",
   228  						Line:     1,
   229  						Column:   1,
   230  					},
   231  					Name:              "TestSignal",
   232  					StartBit:          56,
   233  					Size:              8,
   234  					Factor:            0.001,
   235  					Offset:            0,
   236  					Minimum:           0,
   237  					Maximum:           0.255,
   238  					Unit:              "l/mm",
   239  					Receivers:         []Identifier{"XXX"},
   240  					IsMultiplexed:     true,
   241  					MultiplexerSwitch: 2,
   242  				},
   243  			},
   244  		},
   245  
   246  		{
   247  			name: "comment.dbc",
   248  			text: `CM_ "comment";`,
   249  			defs: []Def{
   250  				&CommentDef{
   251  					Pos: scanner.Position{
   252  						Filename: "comment.dbc",
   253  						Line:     1,
   254  						Column:   1,
   255  					},
   256  					Comment: "comment",
   257  				},
   258  			},
   259  		},
   260  
   261  		{
   262  			name: "node_comment.dbc",
   263  			text: `CM_ BU_ NodeName "node comment";`,
   264  			defs: []Def{
   265  				&CommentDef{
   266  					Pos: scanner.Position{
   267  						Filename: "node_comment.dbc",
   268  						Line:     1,
   269  						Column:   1,
   270  					},
   271  					ObjectType: ObjectTypeNetworkNode,
   272  					NodeName:   "NodeName",
   273  					Comment:    "node comment",
   274  				},
   275  			},
   276  		},
   277  
   278  		{
   279  			name: "message_comment.dbc",
   280  			text: `CM_ BO_ 1234 "message comment";`,
   281  			defs: []Def{
   282  				&CommentDef{
   283  					Pos: scanner.Position{
   284  						Filename: "message_comment.dbc",
   285  						Line:     1,
   286  						Column:   1,
   287  					},
   288  					ObjectType: ObjectTypeMessage,
   289  					MessageID:  1234,
   290  					Comment:    "message comment",
   291  				},
   292  			},
   293  		},
   294  
   295  		{
   296  			name: "signal_comment.dbc",
   297  			text: `CM_ SG_ 1234 SignalName "signal comment";`,
   298  			defs: []Def{
   299  				&CommentDef{
   300  					Pos: scanner.Position{
   301  						Filename: "signal_comment.dbc",
   302  						Line:     1,
   303  						Column:   1,
   304  					},
   305  					ObjectType: ObjectTypeSignal,
   306  					MessageID:  1234,
   307  					SignalName: "SignalName",
   308  					Comment:    "signal comment",
   309  				},
   310  			},
   311  		},
   312  
   313  		{
   314  			name: "int_attribute_definition.dbc",
   315  			text: `BA_DEF_ "AttributeName" INT 5 10;`,
   316  			defs: []Def{
   317  				&AttributeDef{
   318  					Pos: scanner.Position{
   319  						Filename: "int_attribute_definition.dbc",
   320  						Line:     1,
   321  						Column:   1,
   322  					},
   323  					Name:       "AttributeName",
   324  					Type:       AttributeValueTypeInt,
   325  					MinimumInt: 5,
   326  					MaximumInt: 10,
   327  				},
   328  			},
   329  		},
   330  
   331  		{
   332  			name: "int_attribute_definition_no_min_or_max.dbc",
   333  			text: `BA_DEF_ "AttributeName" INT;`,
   334  			defs: []Def{
   335  				&AttributeDef{
   336  					Pos: scanner.Position{
   337  						Filename: "int_attribute_definition_no_min_or_max.dbc",
   338  						Line:     1,
   339  						Column:   1,
   340  					},
   341  					Name: "AttributeName",
   342  					Type: AttributeValueTypeInt,
   343  				},
   344  			},
   345  		},
   346  
   347  		{
   348  			name: "float_attribute_definition.dbc",
   349  			text: `BA_DEF_ "AttributeName" FLOAT 0.5 1.5;`,
   350  			defs: []Def{
   351  				&AttributeDef{
   352  					Pos: scanner.Position{
   353  						Filename: "float_attribute_definition.dbc",
   354  						Line:     1,
   355  						Column:   1,
   356  					},
   357  					Name:         "AttributeName",
   358  					Type:         AttributeValueTypeFloat,
   359  					MinimumFloat: 0.5,
   360  					MaximumFloat: 1.5,
   361  				},
   362  			},
   363  		},
   364  
   365  		{
   366  			name: "float_attribute_definition_no_min_or_max.dbc",
   367  			text: `BA_DEF_ "AttributeName" FLOAT;`,
   368  			defs: []Def{
   369  				&AttributeDef{
   370  					Pos: scanner.Position{
   371  						Filename: "float_attribute_definition_no_min_or_max.dbc",
   372  						Line:     1,
   373  						Column:   1,
   374  					},
   375  					Name: "AttributeName",
   376  					Type: AttributeValueTypeFloat,
   377  				},
   378  			},
   379  		},
   380  
   381  		{
   382  			name: "string_attribute.dbc",
   383  			text: `BA_DEF_ "AttributeName" STRING;`,
   384  			defs: []Def{
   385  				&AttributeDef{
   386  					Pos: scanner.Position{
   387  						Filename: "string_attribute.dbc",
   388  						Line:     1,
   389  						Column:   1,
   390  					},
   391  					Name: "AttributeName",
   392  					Type: AttributeValueTypeString,
   393  				},
   394  			},
   395  		},
   396  
   397  		{
   398  			name: "enum_attribute.dbc",
   399  			text: `BA_DEF_ "AttributeName" ENUM "value1","value2";`,
   400  			defs: []Def{
   401  				&AttributeDef{
   402  					Pos: scanner.Position{
   403  						Filename: "enum_attribute.dbc",
   404  						Line:     1,
   405  						Column:   1,
   406  					},
   407  					Name:       "AttributeName",
   408  					Type:       AttributeValueTypeEnum,
   409  					EnumValues: []string{"value1", "value2"},
   410  				},
   411  			},
   412  		},
   413  
   414  		{
   415  			name: "enum_attribute_for_messages.dbc",
   416  			text: `BA_DEF_ BO_  "VFrameFormat" ENUM  "StandardCAN","ExtendedCAN","reserved","J1939PG";`,
   417  			defs: []Def{
   418  				&AttributeDef{
   419  					Pos: scanner.Position{
   420  						Filename: "enum_attribute_for_messages.dbc",
   421  						Line:     1,
   422  						Column:   1,
   423  					},
   424  					Name:       "VFrameFormat",
   425  					ObjectType: ObjectTypeMessage,
   426  					Type:       AttributeValueTypeEnum,
   427  					EnumValues: []string{"StandardCAN", "ExtendedCAN", "reserved", "J1939PG"},
   428  				},
   429  			},
   430  		},
   431  
   432  		{
   433  			name: "attribute_default_string.dbc",
   434  			text: strings.Join([]string{
   435  				`BA_DEF_ "Foo" STRING;`,
   436  				`BA_DEF_DEF_ "Foo" "string value";`,
   437  			}, "\n"),
   438  			defs: []Def{
   439  				&AttributeDef{
   440  					Pos: scanner.Position{
   441  						Filename: "attribute_default_string.dbc",
   442  						Line:     1,
   443  						Column:   1,
   444  					},
   445  					Name: "Foo",
   446  					Type: AttributeValueTypeString,
   447  				},
   448  				&AttributeDefaultValueDef{
   449  					Pos: scanner.Position{
   450  						Filename: "attribute_default_string.dbc",
   451  						Line:     2,
   452  						Column:   1,
   453  						Offset:   22,
   454  					},
   455  					AttributeName:      "Foo",
   456  					DefaultStringValue: "string value",
   457  				},
   458  			},
   459  		},
   460  
   461  		{
   462  			name: "attribute_default_int.dbc",
   463  			text: strings.Join([]string{
   464  				`BA_DEF_ "Foo" INT 0 200;`,
   465  				`BA_DEF_DEF_ "Foo" 100;`,
   466  			}, "\n"),
   467  			defs: []Def{
   468  				&AttributeDef{
   469  					Pos: scanner.Position{
   470  						Filename: "attribute_default_int.dbc",
   471  						Line:     1,
   472  						Column:   1,
   473  					},
   474  					Name:       "Foo",
   475  					Type:       AttributeValueTypeInt,
   476  					MinimumInt: 0,
   477  					MaximumInt: 200,
   478  				},
   479  				&AttributeDefaultValueDef{
   480  					Pos: scanner.Position{
   481  						Filename: "attribute_default_int.dbc",
   482  						Line:     2,
   483  						Column:   1,
   484  						Offset:   25,
   485  					},
   486  					AttributeName:   "Foo",
   487  					DefaultIntValue: 100,
   488  				},
   489  			},
   490  		},
   491  
   492  		{
   493  			name: "attribute_default_float.dbc",
   494  			text: strings.Join([]string{
   495  				`BA_DEF_ "Foo" FLOAT 0.5 200.5;`,
   496  				`BA_DEF_DEF_ "Foo" 100.5;`,
   497  			}, "\n"),
   498  			defs: []Def{
   499  				&AttributeDef{
   500  					Pos: scanner.Position{
   501  						Filename: "attribute_default_float.dbc",
   502  						Line:     1,
   503  						Column:   1,
   504  					},
   505  					Name:         "Foo",
   506  					Type:         AttributeValueTypeFloat,
   507  					MinimumFloat: 0.5,
   508  					MaximumFloat: 200.5,
   509  				},
   510  				&AttributeDefaultValueDef{
   511  					Pos: scanner.Position{
   512  						Filename: "attribute_default_float.dbc",
   513  						Line:     2,
   514  						Column:   1,
   515  						Offset:   31,
   516  					},
   517  					AttributeName:     "Foo",
   518  					DefaultFloatValue: 100.5,
   519  				},
   520  			},
   521  		},
   522  
   523  		{
   524  			name: "attribute_value.dbc",
   525  			text: strings.Join([]string{
   526  				`BA_DEF_ "Foo" FLOAT;`,
   527  				`BA_ "Foo" 100.5;`,
   528  			}, "\n"),
   529  			defs: []Def{
   530  				&AttributeDef{
   531  					Pos: scanner.Position{
   532  						Filename: "attribute_value.dbc",
   533  						Line:     1,
   534  						Column:   1,
   535  					},
   536  					Name: "Foo",
   537  					Type: AttributeValueTypeFloat,
   538  				},
   539  				&AttributeValueForObjectDef{
   540  					Pos: scanner.Position{
   541  						Filename: "attribute_value.dbc",
   542  						Line:     2,
   543  						Column:   1,
   544  						Offset:   21,
   545  					},
   546  					AttributeName: "Foo",
   547  					FloatValue:    100.5,
   548  				},
   549  			},
   550  		},
   551  
   552  		{
   553  			name: "negative_attribute_value.dbc",
   554  			text: strings.Join([]string{
   555  				`BA_DEF_ "Foo" INT;`,
   556  				`BA_ "Foo" -100;`,
   557  			}, "\n"),
   558  			defs: []Def{
   559  				&AttributeDef{
   560  					Pos: scanner.Position{
   561  						Filename: "negative_attribute_value.dbc",
   562  						Line:     1,
   563  						Column:   1,
   564  					},
   565  					Name: "Foo",
   566  					Type: AttributeValueTypeInt,
   567  				},
   568  				&AttributeValueForObjectDef{
   569  					Pos: scanner.Position{
   570  						Filename: "negative_attribute_value.dbc",
   571  						Line:     2,
   572  						Column:   1,
   573  						Offset:   19,
   574  					},
   575  					AttributeName: "Foo",
   576  					IntValue:      -100,
   577  				},
   578  			},
   579  		},
   580  
   581  		{
   582  			name: "node_attribute_value.dbc",
   583  			text: strings.Join([]string{
   584  				`BA_DEF_ "Foo" INT;`,
   585  				`BA_ "Foo" BU_ TestNode 100;`,
   586  			}, "\n"),
   587  			defs: []Def{
   588  				&AttributeDef{
   589  					Pos: scanner.Position{
   590  						Filename: "node_attribute_value.dbc",
   591  						Line:     1,
   592  						Column:   1,
   593  					},
   594  					Name: "Foo",
   595  					Type: AttributeValueTypeInt,
   596  				},
   597  				&AttributeValueForObjectDef{
   598  					Pos: scanner.Position{
   599  						Filename: "node_attribute_value.dbc",
   600  						Line:     2,
   601  						Column:   1,
   602  						Offset:   19,
   603  					},
   604  					AttributeName: "Foo",
   605  					ObjectType:    ObjectTypeNetworkNode,
   606  					NodeName:      "TestNode",
   607  					IntValue:      100,
   608  				},
   609  			},
   610  		},
   611  
   612  		{
   613  			name: "message_attribute_value.dbc",
   614  			text: strings.Join([]string{
   615  				`BA_DEF_ "Foo" STRING;`,
   616  				`BA_ "Foo" BO_ 1234 "string value";`,
   617  			}, "\n"),
   618  			defs: []Def{
   619  				&AttributeDef{
   620  					Pos: scanner.Position{
   621  						Filename: "message_attribute_value.dbc",
   622  						Line:     1,
   623  						Column:   1,
   624  					},
   625  					Name: "Foo",
   626  					Type: AttributeValueTypeString,
   627  				},
   628  				&AttributeValueForObjectDef{
   629  					Pos: scanner.Position{
   630  						Filename: "message_attribute_value.dbc",
   631  						Line:     2,
   632  						Column:   1,
   633  						Offset:   22,
   634  					},
   635  					AttributeName: "Foo",
   636  					ObjectType:    ObjectTypeMessage,
   637  					MessageID:     1234,
   638  					StringValue:   "string value",
   639  				},
   640  			},
   641  		},
   642  
   643  		{
   644  			name: "signal_attribute_value.dbc",
   645  			text: strings.Join([]string{
   646  				`BA_DEF_ "Foo" STRING;`,
   647  				`BA_ "Foo" SG_ 1234 SignalName "string value";`,
   648  			}, "\n"),
   649  			defs: []Def{
   650  				&AttributeDef{
   651  					Pos: scanner.Position{
   652  						Filename: "signal_attribute_value.dbc",
   653  						Line:     1,
   654  						Column:   1,
   655  					},
   656  					Name: "Foo",
   657  					Type: AttributeValueTypeString,
   658  				},
   659  				&AttributeValueForObjectDef{
   660  					Pos: scanner.Position{
   661  						Filename: "signal_attribute_value.dbc",
   662  						Line:     2,
   663  						Column:   1,
   664  						Offset:   22,
   665  					},
   666  					AttributeName: "Foo",
   667  					ObjectType:    ObjectTypeSignal,
   668  					MessageID:     1234,
   669  					SignalName:    "SignalName",
   670  					StringValue:   "string value",
   671  				},
   672  			},
   673  		},
   674  
   675  		{
   676  			name: "enum_attribute_value_by_index.dbc",
   677  			text: strings.Join([]string{
   678  				`BA_DEF_ BO_  "VFrameFormat" ENUM  "StandardCAN","ExtendedCAN","reserved","J1939PG";`,
   679  				`BA_ "VFrameFormat" BO_ 1234 3;`,
   680  			}, "\n"),
   681  			defs: []Def{
   682  				&AttributeDef{
   683  					Pos: scanner.Position{
   684  						Filename: "enum_attribute_value_by_index.dbc",
   685  						Line:     1,
   686  						Column:   1,
   687  					},
   688  					Name:       "VFrameFormat",
   689  					ObjectType: ObjectTypeMessage,
   690  					Type:       AttributeValueTypeEnum,
   691  					EnumValues: []string{"StandardCAN", "ExtendedCAN", "reserved", "J1939PG"},
   692  				},
   693  				&AttributeValueForObjectDef{
   694  					Pos: scanner.Position{
   695  						Filename: "enum_attribute_value_by_index.dbc",
   696  						Line:     2,
   697  						Column:   1,
   698  						Offset:   84,
   699  					},
   700  					AttributeName: "VFrameFormat",
   701  					ObjectType:    ObjectTypeMessage,
   702  					MessageID:     1234,
   703  					StringValue:   "J1939PG",
   704  				},
   705  			},
   706  		},
   707  
   708  		{
   709  			name: "value_descriptions_for_signal.dbc",
   710  			text: `VAL_ 3 StW_AnglSens_Id 2 "MUST" 0 "PSBL" 1 "SELF";`,
   711  			defs: []Def{
   712  				&ValueDescriptionsDef{
   713  					Pos: scanner.Position{
   714  						Filename: "value_descriptions_for_signal.dbc",
   715  						Line:     1,
   716  						Column:   1,
   717  					},
   718  					ObjectType: ObjectTypeSignal,
   719  					MessageID:  3,
   720  					SignalName: "StW_AnglSens_Id",
   721  					ValueDescriptions: []ValueDescriptionDef{
   722  						{
   723  							Pos: scanner.Position{
   724  								Filename: "value_descriptions_for_signal.dbc",
   725  								Line:     1,
   726  								Column:   24,
   727  								Offset:   23,
   728  							},
   729  							Value:       2,
   730  							Description: "MUST",
   731  						},
   732  						{
   733  							Pos: scanner.Position{
   734  								Filename: "value_descriptions_for_signal.dbc",
   735  								Line:     1,
   736  								Column:   33,
   737  								Offset:   32,
   738  							},
   739  							Value:       0,
   740  							Description: "PSBL",
   741  						},
   742  						{
   743  							Pos: scanner.Position{
   744  								Filename: "value_descriptions_for_signal.dbc",
   745  								Line:     1,
   746  								Column:   42,
   747  								Offset:   41,
   748  							},
   749  							Value:       1,
   750  							Description: "SELF",
   751  						},
   752  					},
   753  				},
   754  			},
   755  		},
   756  
   757  		{
   758  			name: "value_table.dbc",
   759  			text: `VAL_TABLE_ DI_gear 7 "DI_GEAR_SNA" 4 "DI_GEAR_D";`,
   760  			defs: []Def{
   761  				&ValueTableDef{
   762  					Pos: scanner.Position{
   763  						Filename: "value_table.dbc",
   764  						Line:     1,
   765  						Column:   1,
   766  					},
   767  					TableName: "DI_gear",
   768  					ValueDescriptions: []ValueDescriptionDef{
   769  						{
   770  							Pos: scanner.Position{
   771  								Filename: "value_table.dbc",
   772  								Line:     1,
   773  								Column:   20,
   774  								Offset:   19,
   775  							},
   776  							Value:       7,
   777  							Description: "DI_GEAR_SNA",
   778  						},
   779  						{
   780  							Pos: scanner.Position{
   781  								Filename: "value_table.dbc",
   782  								Line:     1,
   783  								Column:   36,
   784  								Offset:   35,
   785  							},
   786  							Value:       4,
   787  							Description: "DI_GEAR_D",
   788  						},
   789  					},
   790  				},
   791  			},
   792  		},
   793  
   794  		{
   795  			name: "node_list.dbc",
   796  			text: `BU_: RSDS`,
   797  			defs: []Def{
   798  				&NodesDef{
   799  					Pos: scanner.Position{
   800  						Filename: "node_list.dbc",
   801  						Line:     1,
   802  						Column:   1,
   803  					},
   804  					NodeNames: []Identifier{"RSDS"},
   805  				},
   806  			},
   807  		},
   808  
   809  		{
   810  			name: "node_list_followed_by_single_newline_followed_by_value_table.dbc",
   811  			text: strings.Join([]string{
   812  				`BU_: RSDS`,
   813  				`VAL_TABLE_ TableName 3 "Value3" 2 "Value2" 1 "Value1" 0 "Value0";`,
   814  			}, "\n"),
   815  			defs: []Def{
   816  				&NodesDef{
   817  					Pos: scanner.Position{
   818  						Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc",
   819  						Line:     1,
   820  						Column:   1,
   821  					},
   822  					NodeNames: []Identifier{"RSDS"},
   823  				},
   824  				&ValueTableDef{
   825  					Pos: scanner.Position{
   826  						Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc",
   827  						Line:     2,
   828  						Column:   1,
   829  						Offset:   10,
   830  					},
   831  					TableName: "TableName",
   832  					ValueDescriptions: []ValueDescriptionDef{
   833  						{
   834  							Pos: scanner.Position{
   835  								Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc",
   836  								Line:     2,
   837  								Column:   22,
   838  								Offset:   31,
   839  							},
   840  							Value:       3,
   841  							Description: "Value3",
   842  						},
   843  						{
   844  							Pos: scanner.Position{
   845  								Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc",
   846  								Line:     2,
   847  								Column:   33,
   848  								Offset:   42,
   849  							},
   850  							Value:       2,
   851  							Description: "Value2",
   852  						},
   853  						{
   854  							Pos: scanner.Position{
   855  								Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc",
   856  								Line:     2,
   857  								Column:   44,
   858  								Offset:   53,
   859  							},
   860  							Value:       1,
   861  							Description: "Value1",
   862  						},
   863  						{
   864  							Pos: scanner.Position{
   865  								Filename: "node_list_followed_by_single_newline_followed_by_value_table.dbc",
   866  								Line:     2,
   867  								Column:   55,
   868  								Offset:   64,
   869  							},
   870  							Value:       0,
   871  							Description: "Value0",
   872  						},
   873  					},
   874  				},
   875  			},
   876  		},
   877  
   878  		{
   879  			name: "signal_value_type.dbc",
   880  			text: `SIG_VALTYPE_ 42 TestSignal 2;`,
   881  			defs: []Def{
   882  				&SignalValueTypeDef{
   883  					Pos: scanner.Position{
   884  						Filename: "signal_value_type.dbc",
   885  						Line:     1,
   886  						Column:   1,
   887  					},
   888  					MessageID:       42,
   889  					SignalName:      "TestSignal",
   890  					SignalValueType: SignalValueTypeFloat64,
   891  				},
   892  			},
   893  		},
   894  
   895  		{
   896  			name: "signal_value_type_with_colon.dbc",
   897  			text: `SIG_VALTYPE_ 42 TestSignal : 2;`,
   898  			defs: []Def{
   899  				&SignalValueTypeDef{
   900  					Pos: scanner.Position{
   901  						Filename: "signal_value_type_with_colon.dbc",
   902  						Line:     1,
   903  						Column:   1,
   904  					},
   905  					MessageID:       42,
   906  					SignalName:      "TestSignal",
   907  					SignalValueType: SignalValueTypeFloat64,
   908  				},
   909  			},
   910  		},
   911  
   912  		{
   913  			name: "message_transmitters.dbc",
   914  			text: `BO_TX_BU_ 42: Node1 Node2;`,
   915  			defs: []Def{
   916  				&MessageTransmittersDef{
   917  					Pos: scanner.Position{
   918  						Filename: "message_transmitters.dbc",
   919  						Line:     1,
   920  						Column:   1,
   921  					},
   922  					MessageID:    42,
   923  					Transmitters: []Identifier{"Node1", "Node2"},
   924  				},
   925  			},
   926  		},
   927  
   928  		{
   929  			name: "message_transmitters_comma_separated.dbc",
   930  			text: `BO_TX_BU_ 42: Node1,Node2;`,
   931  			defs: []Def{
   932  				&MessageTransmittersDef{
   933  					Pos: scanner.Position{
   934  						Filename: "message_transmitters_comma_separated.dbc",
   935  						Line:     1,
   936  						Column:   1,
   937  					},
   938  					MessageID:    42,
   939  					Transmitters: []Identifier{"Node1", "Node2"},
   940  				},
   941  			},
   942  		},
   943  
   944  		{
   945  			name: "environment_variable_data.dbc",
   946  			text: `ENVVAR_DATA_ VariableName: 42;`,
   947  			defs: []Def{
   948  				&EnvironmentVariableDataDef{
   949  					Pos: scanner.Position{
   950  						Filename: "environment_variable_data.dbc",
   951  						Line:     1,
   952  						Column:   1,
   953  					},
   954  					EnvironmentVariableName: "VariableName",
   955  					DataSize:                42,
   956  				},
   957  			},
   958  		},
   959  
   960  		{
   961  			name: "environment_variable_value_descriptions.dbc",
   962  			text: `VAL_ VariableName 2 "Value2" 1 "Value1" 0 "Value0";`,
   963  			defs: []Def{
   964  				&ValueDescriptionsDef{
   965  					Pos: scanner.Position{
   966  						Filename: "environment_variable_value_descriptions.dbc",
   967  						Line:     1,
   968  						Column:   1,
   969  					},
   970  					ObjectType:              ObjectTypeEnvironmentVariable,
   971  					EnvironmentVariableName: "VariableName",
   972  					ValueDescriptions: []ValueDescriptionDef{
   973  						{
   974  							Pos: scanner.Position{
   975  								Filename: "environment_variable_value_descriptions.dbc",
   976  								Line:     1,
   977  								Column:   19,
   978  								Offset:   18,
   979  							},
   980  							Value:       2,
   981  							Description: "Value2",
   982  						},
   983  						{
   984  							Pos: scanner.Position{
   985  								Filename: "environment_variable_value_descriptions.dbc",
   986  								Line:     1,
   987  								Column:   30,
   988  								Offset:   29,
   989  							},
   990  							Value:       1,
   991  							Description: "Value1",
   992  						},
   993  						{
   994  							Pos: scanner.Position{
   995  								Filename: "environment_variable_value_descriptions.dbc",
   996  								Line:     1,
   997  								Column:   41,
   998  								Offset:   40,
   999  							},
  1000  							Value:       0,
  1001  							Description: "Value0",
  1002  						},
  1003  					},
  1004  				},
  1005  			},
  1006  		},
  1007  
  1008  		{
  1009  			name: "unknown_def.dbc",
  1010  			text: `FOO_ Bar 2 Baz;`,
  1011  			defs: []Def{
  1012  				&UnknownDef{
  1013  					Pos: scanner.Position{
  1014  						Filename: "unknown_def.dbc",
  1015  						Line:     1,
  1016  						Column:   1,
  1017  					},
  1018  					Keyword: "FOO_",
  1019  				},
  1020  			},
  1021  		},
  1022  	} {
  1023  		tt := tt
  1024  		t.Run(tt.name, func(t *testing.T) {
  1025  			p := NewParser(tt.name, []byte(tt.text))
  1026  			assert.NilError(t, p.Parse())
  1027  			assert.DeepEqual(t, tt.defs, p.Defs())
  1028  		})
  1029  	}
  1030  }
  1031  
  1032  func TestParser_Parse_Error(t *testing.T) {
  1033  	for _, tt := range []struct {
  1034  		name string
  1035  		text string
  1036  		err  *parseError
  1037  	}{
  1038  		{
  1039  			name: "non_string_version.dbc",
  1040  			text: "VERSION foo",
  1041  			err: &parseError{
  1042  				pos: scanner.Position{
  1043  					Filename: "non_string_version.dbc",
  1044  					Line:     1,
  1045  					Column:   9,
  1046  					Offset:   8,
  1047  				},
  1048  				reason: "expected token \"",
  1049  			},
  1050  		},
  1051  		{
  1052  			name: "invalid_utf8.dbc",
  1053  			text: `VERSION "foo` + string([]byte{0xc3, 0x28}) + `"`,
  1054  			err: &parseError{
  1055  				pos: scanner.Position{
  1056  					Filename: "invalid_utf8.dbc",
  1057  					Line:     1,
  1058  					Column:   13,
  1059  					Offset:   12,
  1060  				},
  1061  				reason: "invalid UTF-8 encoding",
  1062  			},
  1063  		},
  1064  	} {
  1065  		tt := tt
  1066  		t.Run(tt.name, func(t *testing.T) {
  1067  			p := NewParser(tt.name, []byte(tt.text))
  1068  			assert.Error(t, p.Parse(), tt.err.Error())
  1069  		})
  1070  	}
  1071  }
  1072  
  1073  func dump(data interface{}) string {
  1074  	spewConfig := spew.ConfigState{
  1075  		Indent:                  " ",
  1076  		DisablePointerAddresses: true,
  1077  		DisableCapacities:       true,
  1078  		SortKeys:                true,
  1079  	}
  1080  	return spewConfig.Sdump(data)
  1081  }