-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathtest_include.ml
More file actions
292 lines (237 loc) · 8.68 KB
/
Copy pathtest_include.ml
File metadata and controls
292 lines (237 loc) · 8.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
(*
* Copyright 2025 Multikernel Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*)
open Alcotest
open Kernelscript
open Ast
(** Test basic include parsing **)
let test_include_parsing () =
let program = {|
include "common_kfuncs.kh"
include "xdp_kfuncs.kh"
@xdp
fn test_program(ctx: *xdp_md) -> xdp_action {
return 2
}
fn main() -> i32 {
return 0
}
|} in
let ast = Parse.parse_string program in
(* Check that we have the expected declarations *)
check int "Number of declarations" 4 (List.length ast);
(* Check that the first two declarations are includes *)
match ast with
| IncludeDecl include1 :: IncludeDecl include2 :: _ :: _ ->
check string "First include path" "common_kfuncs.kh" include1.include_path;
check string "Second include path" "xdp_kfuncs.kh" include2.include_path
| _ ->
fail "Expected first two declarations to be includes"
(** Test include string representation **)
let test_include_string_representation () =
let program = {|
include "test_header.kh"
|} in
let ast = Parse.parse_string program in
let ast_string = string_of_ast ast in
(* Check that include is properly represented *)
let regex = Str.regexp "include \"test_header.kh\"" in
let contains_include = try ignore (Str.search_forward regex ast_string 0); true with Not_found -> false in
check bool "Contains include declaration" true contains_include
(** Test include with invalid extension should parse but validation can be added later **)
let test_include_any_extension () =
let program = {|
include "invalid_file.ks"
|} in
(* Should parse successfully - validation of .kh extension will be in file processing *)
let ast = Parse.parse_string program in
match ast with
| [IncludeDecl include_decl] ->
check string "Include path" "invalid_file.ks" include_decl.include_path
| _ ->
fail "Expected single include declaration"
(** Test type checking with includes **)
let test_include_type_checking () =
let program = {|
include "kfuncs.kh"
@xdp
fn test_program(ctx: *xdp_md) -> xdp_action {
return 2
}
fn main() -> i32 {
return 0
}
|} in
(* Type check should pass - includes should not break type checking *)
let ast = Parse.parse_string program in
let type_check_result = try
let _symbol_table = Symbol_table.build_symbol_table ast in
ignore (Type_checker.type_check_and_annotate_ast ast);
true
with
| _ -> false
in
check bool "Type checking should pass with includes" true type_check_result
(** Test include processing with real file system operations **)
let test_include_file_processing () =
(* Create temporary header file *)
let temp_dir = Filename.get_temp_dir_name () in
let header_file = Filename.concat temp_dir "test_header.kh" in
let header_content = {|
// Test header file
extern test_kfunc(value: u32) -> u64
type TestType = u32
|} in
let oc = open_out header_file in
output_string oc header_content;
close_out oc;
(* Create main file that includes the header *)
let main_file = Filename.concat temp_dir "test_main.ks" in
let main_content = Printf.sprintf {|
include "%s"
@xdp
fn test_program(ctx: *xdp_md) -> xdp_action {
var result = test_kfunc(42)
var test_val: TestType = 123
return 2
}
fn main() -> i32 {
return 0
}
|} (Filename.basename header_file) in
let oc = open_out main_file in
output_string oc main_content;
close_out oc;
(* Test include processing *)
let result = try
let ic = open_in main_file in
let content = really_input_string ic (in_channel_length ic) in
close_in ic;
let lexbuf = Lexing.from_string content in
let ast = Parser.program Lexer.token lexbuf in
(* Process includes *)
let expanded_ast = Include_resolver.process_includes ast main_file in
(* Check that AST was expanded *)
check bool "AST expanded from includes" true (List.length expanded_ast > List.length ast);
(* Check that extern kfunc is present in expanded AST *)
let has_extern = List.exists (function
| Ast.ExternKfuncDecl extern_decl -> extern_decl.extern_name = "test_kfunc"
| _ -> false
) expanded_ast in
check bool "Extern kfunc included" true has_extern;
(* Check that type alias is present *)
let has_type = List.exists (function
| Ast.TypeDef (Ast.TypeAlias (name, _, _)) -> name = "TestType"
| _ -> false
) expanded_ast in
check bool "Type alias included" true has_type;
true
with
| _ -> false
in
(* Clean up *)
(try Sys.remove header_file with _ -> ());
(try Sys.remove main_file with _ -> ());
check bool "Include processing successful" true result
(** Test error handling for invalid header file **)
let test_include_validation_error () =
(* Create temporary invalid header file *)
let temp_dir = Filename.get_temp_dir_name () in
let header_file = Filename.concat temp_dir "invalid_header.kh" in
let header_content = {|
extern test_kfunc() -> u64
// Invalid: function implementation in header
fn invalid_impl() -> u32 {
return 42
}
|} in
let oc = open_out header_file in
output_string oc header_content;
close_out oc;
(* Create main file that includes the invalid header *)
let main_file = Filename.concat temp_dir "test_main.ks" in
let main_content = Printf.sprintf {|
include "%s"
fn main() -> i32 { return 0 }
|} (Filename.basename header_file) in
let oc = open_out main_file in
output_string oc main_content;
close_out oc;
(* Test that include processing fails *)
let error_caught = try
let ic = open_in main_file in
let content = really_input_string ic (in_channel_length ic) in
close_in ic;
let lexbuf = Lexing.from_string content in
let ast = Parser.program Lexer.token lexbuf in
(* This should throw an error *)
let _ = Include_resolver.process_includes ast main_file in
false (* Should not reach here *)
with
| Include_resolver.Include_error _ -> true (* Expected error *)
| _ -> false (* Unexpected error *)
in
(* Clean up *)
(try Sys.remove header_file with _ -> ());
(try Sys.remove main_file with _ -> ());
check bool "Include validation error caught" true error_caught
(** Test extension validation **)
let test_extension_validation () =
(* Create temporary file with wrong extension *)
let temp_dir = Filename.get_temp_dir_name () in
let wrong_ext_file = Filename.concat temp_dir "wrong_ext.ks" in
let content = "extern test_kfunc() -> u64" in
let oc = open_out wrong_ext_file in
output_string oc content;
close_out oc;
(* Create main file that includes file with wrong extension *)
let main_file = Filename.concat temp_dir "test_main.ks" in
let main_content = Printf.sprintf {|
include "%s"
fn main() -> i32 { return 0 }
|} (Filename.basename wrong_ext_file) in
let oc = open_out main_file in
output_string oc main_content;
close_out oc;
(* Test that extension validation fails *)
let error_caught = try
let ic = open_in main_file in
let content = really_input_string ic (in_channel_length ic) in
close_in ic;
let lexbuf = Lexing.from_string content in
let ast = Parser.program Lexer.token lexbuf in
(* This should throw an error *)
let _ = Include_resolver.process_includes ast main_file in
false (* Should not reach here *)
with
| Include_resolver.Include_validation_error _ -> true (* Expected error *)
| _ -> false (* Unexpected error *)
in
(* Clean up *)
(try Sys.remove wrong_ext_file with _ -> ());
(try Sys.remove main_file with _ -> ());
check bool "Extension validation error caught" true error_caught
let tests = [
"include parsing", `Quick, test_include_parsing;
"include string representation", `Quick, test_include_string_representation;
"include any extension", `Quick, test_include_any_extension;
"include type checking", `Quick, test_include_type_checking;
"include file processing", `Quick, test_include_file_processing;
"include validation error", `Quick, test_include_validation_error;
"extension validation", `Quick, test_extension_validation;
]
let () = Alcotest.run "KernelScript include tests" [
"include_tests", tests
]