1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2014-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(syntax_SUITE).
22-export([suite/0,all/0,groups/0,
23	 assignment/1,
24	 class/1,
25	 constraints/1,
26	 exports/1,
27	 header/1,
28	 imports/1,
29	 objects/1,
30	 sequence/1,
31	 syntax/1,
32	 tokenizer/1,
33	 types/1,
34	 values/1]).
35
36-include_lib("common_test/include/ct.hrl").
37
38suite() -> [{ct_hooks, [ts_install_cth]}].
39
40all() ->
41    [{group,p}].
42
43groups() ->
44    [{p,parallel(),
45      [assignment,
46       class,
47       constraints,
48       exports,
49       header,
50       imports,
51       objects,
52       sequence,
53       syntax,
54       tokenizer,
55       types,
56       values]}].
57
58parallel() ->
59    case erlang:system_info(schedulers) > 1 of
60        true  -> [parallel];
61        false -> []
62    end.
63
64assignment(Config) ->
65    Head = "Assignment DEFINITIONS AUTOMATIC TAGS ::=\nBEGIN\n",
66    End = "\nEND\n",
67    L0 = [{"42",3,{syntax_error,42}},
68	  {"i",4,{syntax_error,'END'}},
69	  {"i ::=",3,{syntax_error,'::='}},
70	  {"i type",4,{syntax_error,'END'}},
71	  {"i type ::=",3,{syntax_error,'::='}},
72	  {"i TYPE",4,{syntax_error,'END'}},
73	  {"i TYPE ::= ",4,{syntax_error,'END'}},
74	  {"i INTEGER ::= 42 garbage",4,{syntax_error,'END'}},
75	  {"i{T} Type",4,{syntax_error,'END'}},
76	  {"TYPE",4,{syntax_error,'END'}},
77	  {"TYPE ::=",4,{syntax_error,'END'}},
78	  {"TYPE{ ::=",3,{syntax_error,'::='}},
79	  {"TYPE{P, ::=",3,{syntax_error,'::='}},
80	  {"TYPE{P,} ::=",3,{syntax_error,'}'}},
81	  {"TYPE{Gov:} ::=",3,{syntax_error,':'}},
82	  {"TYPE{A} CL ",4,{syntax_error,'END'}},
83	  {"ObjSet CL",4,{syntax_error,'END'}}
84	 ],
85    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
86    run(L, "Assignment", Config),
87    ok.
88
89class(Config) ->
90    Head = "Class DEFINITIONS AUTOMATIC TAGS ::=\n"
91	"BEGIN\n"
92	" CL ::= CLASS {",
93    End = "\nEND\n",
94    L0 = [{"id",3,{syntax_error,'id'}},
95	  {"&id INTEGER",4,{syntax_error,'END'}},
96	  {"&id INTEGER,",4,{syntax_error,'END'}},
97	  {"&id,",3,{syntax_error,','}},
98	  {"&id OPTIONAL",3,{syntax_error,'OPTIONAL'}},
99	  {"&id INTEGER OPTIONAL",4,{syntax_error,'END'}},
100	  {"&var &Field",4,{syntax_error,'END'}},
101	  {"&Type,",4,{syntax_error,'END'}},
102	  {"&Type OPTIONAL",4,{syntax_error,'END'}},
103	  {"&ValueSet INTEGER OPTIONAL",4,{syntax_error,'END'}},
104	  {"&ValueSet INTEGER DEFAULT",4,{syntax_error,'END'}},
105	  {"&ValueSet INTEGER DEFAULT {",4,{syntax_error,'END'}},
106	  {"&ValueSet INTEGER DEFAULT {a",4,{syntax_error,'END'}},
107	  {"&Var &Field",4,{syntax_error,'END'}}
108	 ],
109    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
110    run(L, "Class", Config),
111    ok.
112
113constraints(Config) ->
114    Head = "Constraints DEFINITIONS AUTOMATIC TAGS ::=\n"
115	"BEGIN\n"
116	" Type ::= ",
117    End = "\nEND\n",
118    L0 = [{"INTEGER (",4,{syntax_error,'END'}},
119	  {"INTEGER (10x",3,{syntax_error,x}},
120	  {"INTEGER (10|(10y",3,{syntax_error,y}},
121	  {"INTEGER (CONSTRAINED BY {}",4,{syntax_error,'END'}},
122	  {"INTEGER (CONSTRAINED BY {INTEGER garbage",3,
123	   {syntax_error,garbage}},
124	  {"INTEGER ({ObjSet",4,{syntax_error,'END'}},
125	  {"INTEGER ({ObjSet}{",3,{syntax_error,'{'}},
126	  {"INTEGER ({ObjSet}{@",3,{syntax_error,'{'}},
127	  {"INTEGER ({ObjSet}{@x",3,{syntax_error,'{'}},
128	  {"INTEGER ({ObjSet}{@x}",4,{syntax_error,'END'}},
129	  {"INTEGER (10 !BOOLEAN",4,{syntax_error,'END'}},
130	  {"INTEGER (10 !BOOLEAN:",4,{syntax_error,'END'}},
131	  {"INTEGER (10 !BOOLEAN:FALSE",4,{syntax_error,'END'}},
132	  {"SEQUENCE {} (WITH COMPONENTS { Type })",
133	   3,{syntax_error,'Type'}},
134	  {"SEQUENCE {} (WITH COMPONENTS { x (10)",
135	   4,{syntax_error,'END'}},
136	  {"SEQUENCE {} (WITH COMPONENTS { ..., x (10)",
137	   4,{syntax_error,'END'}}
138	 ],
139    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
140    run(L, "Constraints", Config),
141    ok.
142
143exports(Config) ->
144    Head = "Exports DEFINITIONS AUTOMATIC TAGS ::=\n"
145	"BEGIN\n"
146	" EXPORTS ",
147    End = "\nEND\n",
148    L0 = [{"Type",4,{syntax_error,'END'}}
149	 ],
150    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
151    run(L, "Exports", Config),
152    ok.
153
154header(Config) ->
155    L = [{"lowercase",1,{syntax_error,lowercase}},
156	 {"H ",2,{syntax_error,'END-OF-FILE'}},
157	 {"H-",1,{syntax_error,'-'}},
158	 {"42",1,{syntax_error,42}},
159	 {"H definitions",1,{syntax_error,definitions}},
160	 {"H DEFINITIONS STUPID TAGS",1,{syntax_error,'STUPID'}},
161	 {"H DEFINITIONS WHATEVER",1,{syntax_error,'WHATEVER'}},
162	 {"H DEFINITIONS ::= BEGIN",2,{syntax_error,'END-OF-FILE'}},
163	 {"BOOLEAN",1,{syntax_error,'BOOLEAN'}}
164	],
165    run(L, "H", Config),
166    ok.
167
168imports(Config) ->
169    Head = "Imports DEFINITIONS AUTOMATIC TAGS ::=\n"
170	"BEGIN\n"
171	" IMPORTS ",
172    End = "\nEND\n",
173    L0 = [{"Type FROM X",4,{syntax_error,'END'}},
174	  {"Symbols TO Y",3,{syntax_error,'TO'}}
175	 ],
176    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
177    run(L, "Imports", Config),
178    ok.
179
180objects(Config) ->
181    Head = "Objects DEFINITIONS AUTOMATIC TAGS ::=\n"
182	"BEGIN\n"
183	"  object CLASS-NAME ::= ",
184    End = "\nEND\n",
185    L0 = [{"{",4,{syntax_error,'END'}},
186	  {"{&min 1, max 10}",3,{syntax_error,max}},
187	  {"{&min 1, Max 10}",3,{syntax_error,'Max'}},
188	  {"{min 1, &max 10}",3,{syntax_error,'&max'}},
189	  {"{min 1, &Max 10}",3,{syntax_error,'&Max'}},
190	  {"{RESERVERD WORD BIT}",3,{syntax_error,'BIT'}},
191	  {"{&min 1",4,{syntax_error,'END'}}
192	 ],
193    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
194    run(L, "Objects", Config),
195    ok.
196
197sequence(Config) ->
198    Head = "Sequence DEFINITIONS AUTOMATIC TAGS ::=\n"
199	"BEGIN\n"
200	"  Type ::= SEQUENCE {",
201    End = "\nEND\n",
202    L0 = [{"",4,{syntax_error,'END'}},
203	  {" UpperCase",3,{syntax_error,'UpperCase'}},
204	  {" a b",4,{syntax_error,'END'}},
205	  {" i INTEGER",4,{syntax_error,'END'}},
206	  {" ...",4,{syntax_error,'END'}},
207	  {" ..., [[",4,{syntax_error,'END'}},
208	  {" ..., [[ a INTEGER ]",3,{syntax_error,']'}},
209	  {" ..., [[ a INTEGER,",3,{syntax_error,','}},
210	  {" ..., [[ a INTEGER, ... ]]",3,{syntax_error,','}},
211	  {" ... !42 xxx",3,{syntax_error,'xxx'}},
212	  {" ... !42, a INTEGER,",3,{syntax_error,','}}
213	 ],
214    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
215    run(L, "Sequence", Config),
216    ok.
217
218syntax(Config) ->
219    Head = "Syntax DEFINITIONS AUTOMATIC TAGS ::=\n"
220	"BEGIN\n"
221	" CL ::= CLASS { &id INTEGER UNIQUE } WITH SYNTAX ",
222    End = "\nEND\n",
223    L0 = [{"{}",3,{syntax_error,'}'}},
224	  {"WORD",3,{syntax_error,'WORD'}},
225	  {"{ Word }",3,{syntax_error,'Word'}},
226	  {"{ [ Word ] }",3,{syntax_error,'Word'}},
227	  {"{ [ WORD }",3,{syntax_error,'}'}},
228	  {"{ WORD;",3,{syntax_error,';'}}
229	 ],
230    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
231    run(L, "Syntax", Config),
232    ok.
233
234tokenizer(Config) ->
235    Head = "Tokenize DEFINITIONS AUTOMATIC TAGS ::=\n"
236	"BEGIN\n",
237    End = "\nEND\n",
238    L0 = [{"'",3,eol_in_token},
239	  {"'42'B",3,{invalid_binary_number,"42"}},
240	  {"'ZZZ'H",3,{invalid_hex_number,"ZZZ"}},
241	  {"\"abc",3,missing_quote_at_eof},
242	  {"/*",3,eof_in_comment}
243	 ],
244    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
245    run(L, "Tokenizer", Config, asn1ct_tok),
246    ok.
247
248types(Config) ->
249    Head = "Types DEFINITIONS AUTOMATIC TAGS ::=\n"
250	"BEGIN\n"
251	"  Type ::= ",
252    End = "\nEND\n",
253    L0 = [{"BIT STRING garbage",4,{syntax_error,'END'}},
254	  {"BIT STRING {",4,{syntax_error,'END'}},
255	  {"BIT STRING { a(42",3,{syntax_error,42}},
256	  {"BIT STRING { a(0)",4,{syntax_error,'END'}},
257	  {"CHOICE {",4,{syntax_error,'END'}},
258	  {"CHOICE { ..., a}",3,{syntax_error,'...'}},
259	  {"CHOICE { UpperCase",3,{syntax_error,'UpperCase'}},
260	  {"CHOICE { i INTEGER",4,{syntax_error,'END'}},
261	  {"CHOICE { ..., i INTEGER }",3,{syntax_error,'...'}},
262	  {"CHOICE { b BOOLEAN, ..., i INTEGER",
263	   4,{syntax_error,'END'}},
264	  {"CHOICE { b BOOLEAN, ..., [[ e BOOLEAN, ...]]}",
265	   3,{syntax_error,','}},
266	  {"CHOICE { b BOOLEAN, ..., i INTEGER, ..., x BIT STRING}",
267	   3,{syntax_error,','}},
268	  {"ENUMERATED {",4,{syntax_error,'END'}},
269	  {"ENUMERATED { 42 }",3,{syntax_error,42}},
270	  {"ENUMERATED { a, b",4,{syntax_error,'END'}},
271	  {"ENUMERATED { a, }",3,{syntax_error,','}},
272	  {"ENUMERATED { a, ...,\nb, ..., c }",4,{syntax_error,','}},
273	  {"INTEGER {",4,{syntax_error,'END'}},
274	  {"INTEGER { a(42)",4,{syntax_error,'END'}},
275	  {"SEQUENCE",3,{syntax_error,'SEQUENCE'}},
276	  %% More tests for SEQUENCE in sequence/1.
277	  {"SEQUENCE SIZE (1..10)",4,{syntax_error,'END'}},
278	  {"SEQUENCE (SIZE (1..10))",4,{syntax_error,'END'}},
279	  {"SET { i INTEGER",4,{syntax_error,'END'}},
280	  {"SET { ...",4,{syntax_error,'END'}},
281	  {"SET SIZE (1..10)",4,{syntax_error,'END'}},
282	  {"SET (SIZE (1..10))",4,{syntax_error,'END'}},
283	  {"SET { ... !42 xxx",3,{syntax_error,'xxx'}},
284	  {"SET { ... !42, a INTEGER,",3,{syntax_error,','}},
285	  {"[",4,{syntax_error,'END'}},
286	  {"[42",4,{syntax_error,'END'}}
287	 ],
288    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
289    run(L, "Types", Config),
290    ok.
291
292values(Config) ->
293    Head = "Values DEFINITIONS AUTOMATIC TAGS ::=\n"
294	"BEGIN\n"
295	"  value Type ::= ",
296    End = "\nEND\n",
297    L0 = [{"",4,{syntax_error,'END'}}
298	 ],
299    L = [{Head++S++End,Line,E} || {S,Line,E} <- L0],
300    run(L, "Values", Config),
301    ok.
302
303run(List, File, Config) ->
304    run(List, File, Config, asn1ct_parser2).
305
306run(List, File0, Config, Module) ->
307    Base = File0 ++ ".asn1",
308    File = filename:join(proplists:get_value(priv_dir, Config), Base),
309    case run_1(List, Base, File, Module, 0) of
310	0 -> ok;
311	Errors -> ct:fail(Errors)
312    end.
313
314run_1([{Source,Line,Error}=Exp|T], Base, File, Module, N) ->
315    ok = file:write_file(File, Source),
316    io:format("~s", [Source]),
317    case asn1ct:compile(File) of
318	{error,[{structured_error,{Base,L},Module,E}]} ->
319	    case {L,E} of
320		{Line,Error} ->
321		    run_1(T, Base, File, Module, N);
322		{Line,OtherError} ->
323		    io:format("*** Wrong error: ~p, expected ~p ***\n",
324			      [OtherError,Error]),
325		    run_1(T, Base, File, Module, N+1);
326		{OtherLine,Error} ->
327		    io:format("*** Wrong line: ~p, expected ~p ***\n",
328			      [OtherLine,Line]),
329		    run_1(T, Base, File, Module, N+1);
330		{_,_} ->
331		    io:format("*** Wrong line: ~p, expected ~p ***",
332			      [L,Line]),
333		    io:format("*** Wrong error: ~p, expected ~p ***\n",
334			      [E,Error]),
335		    run_1(T, Base, File, Module, N+1)
336	    end;
337	Other ->
338	    io:format("~p\nGOT: ~p", [Exp,Other])
339    end;
340run_1([], _, _, _, N) ->
341    N.
342