tests: mocklib: fix infinite recursion in wrapped print()
[project/firewall4.git] / tests / lib / mocklib.uc
1 {%
2 /* strict mode compliance: ensure that global variabes are defined */
3 if (!exists(global, 'REQUIRE_SEARCH_PATH'))
4 global.MOCK_SEARCH_PATH = null;
5
6 if (!exists(global, 'MOCK_SEARCH_PATH'))
7 global.MOCK_SEARCH_PATH = null;
8
9 if (!exists(global, 'TRACE_CALLS'))
10 global.TRACE_CALLS = null;
11
12 let _fs = require("fs");
13
14 /* Force reloading fs module on next require */
15 delete global.modules.fs;
16
17 let _log = (level, fmt, ...args) => {
18 let color, prefix;
19
20 switch (level) {
21 case 'info':
22 color = 34;
23 prefix = '!';
24 break;
25
26 case 'warn':
27 color = 33;
28 prefix = 'W';
29 break;
30
31 case 'error':
32 color = 31;
33 prefix = 'E';
34 break;
35
36 default:
37 color = 0;
38 prefix = 'I';
39 }
40
41 let f = sprintf("\u001b[%d;1m[%s] %s\u001b[0m", color, prefix, fmt);
42 warn(replace(sprintf(f, ...args), "\n", "\n "), "\n");
43 };
44
45 let read_data_file = (path) => {
46 for (let dir in MOCK_SEARCH_PATH) {
47 let fd = _fs.open(dir + '/' + path, "r");
48
49 if (fd) {
50 let data = fd.read("all");
51 fd.close();
52
53 return data;
54 }
55 }
56
57 return null;
58 };
59
60 let read_json_file = (path) => {
61 let data = read_data_file(path);
62
63 if (data != null) {
64 try {
65 return json(data);
66 }
67 catch (e) {
68 _log('error', "Unable to parse JSON data in %s: %s", path, e);
69
70 return NaN;
71 }
72 }
73
74 return null;
75 };
76
77 let format_json = (data) => {
78 let rv;
79
80 let format_value = (value) => {
81 switch (type(value)) {
82 case "object":
83 return sprintf("{ /* %d keys */ }", length(value));
84
85 case "array":
86 return sprintf("[ /* %d items */ ]", length(value));
87
88 case "string":
89 if (length(value) > 64)
90 value = substr(value, 0, 64) + "...";
91
92 /* fall through */
93 return sprintf("%J", value);
94
95 default:
96 return sprintf("%J", value);
97 }
98 };
99
100 switch (type(data)) {
101 case "object":
102 rv = "{";
103
104 let k = sort(keys(data));
105
106 for (let i, n in k)
107 rv += sprintf("%s %J: %s", i ? "," : "", n, format_value(data[n]));
108
109 rv += " }";
110 break;
111
112 case "array":
113 rv = "[";
114
115 for (let i, v in data)
116 rv += (i ? "," : "") + " " + format_value(v);
117
118 rv += " ]";
119 break;
120
121 default:
122 rv = format_value(data);
123 }
124
125 return rv;
126 };
127
128 let trace_call = (ns, func, args) => {
129 let msg = "[call] " +
130 (ns ? ns + "." : "") +
131 func;
132
133 for (let k, v in args) {
134 msg += ' ' + k + ' <';
135
136 switch (type(v)) {
137 case "array":
138 case "object":
139 msg += format_json(v);
140 break;
141
142 default:
143 msg += v;
144 }
145
146 msg += '>';
147 }
148
149 switch (TRACE_CALLS) {
150 case '1':
151 case 'stdout':
152 _fs.stdout.write(msg + "\n");
153 break;
154
155 case 'stderr':
156 _fs.stderr.write(msg + "\n");
157 break;
158 }
159 };
160
161 /* Prepend mocklib to REQUIRE_SEARCH_PATH */
162 for (let pattern in REQUIRE_SEARCH_PATH) {
163 /* Only consider ucode includes */
164 if (!match(pattern, /\*\.uc$/))
165 continue;
166
167 let path = replace(pattern, /\*/, 'mocklib'),
168 stat = _fs.stat(path);
169
170 if (!stat || stat.type != 'file')
171 continue;
172
173 if (type(MOCK_SEARCH_PATH) != 'array' || length(MOCK_SEARCH_PATH) == 0)
174 MOCK_SEARCH_PATH = [ replace(path, /mocklib\.uc$/, '../mocks') ];
175
176 unshift(REQUIRE_SEARCH_PATH, replace(path, /mocklib\.uc$/, 'mocklib/*.uc'));
177 break;
178 }
179
180 if (type(MOCK_SEARCH_PATH) != 'array' || length(MOCK_SEARCH_PATH) == 0)
181 MOCK_SEARCH_PATH = [ './mocks' ];
182
183 let _print = global.print;
184
185 /* Register global mocklib namespace */
186 global.mocklib = {
187 require: function(module) {
188 let path, res, ex;
189
190 if (type(REQUIRE_SEARCH_PATH) == "array" && index(REQUIRE_SEARCH_PATH[0], 'mocklib/*.uc') != -1)
191 path = shift(REQUIRE_SEARCH_PATH);
192
193 try {
194 res = require(module);
195 }
196 catch (e) {
197 ex = e;
198 }
199
200 if (path)
201 unshift(REQUIRE_SEARCH_PATH, path);
202
203 if (ex)
204 die(ex);
205
206 return res;
207 },
208
209 I: (...args) => _log('info', ...args),
210 N: (...args) => _log('notice', ...args),
211 W: (...args) => _log('warn', ...args),
212 E: (...args) => _log('error', ...args),
213
214 format_json,
215 read_data_file,
216 read_json_file,
217 trace_call
218 };
219
220 /* Override stdlib functions */
221 global.system = function(argv, timeout) {
222 trace_call(null, "system", { command: argv, timeout });
223
224 return 0;
225 };
226
227 global.time = function() {
228 trace_call(null, "time");
229
230 return 1615382640;
231 };
232
233 global.print = function(...args) {
234 if (length(args) == 1 && type(args[0]) in ["array", "object"])
235 printf("%s\n", format_json(args[0]));
236 else
237 _print(...args);
238 };
239
240 return global.mocklib;
241