2 let _fs = require("fs");
4 let _log = (level, fmt, ...args) => {
28 let f = sprintf("\u001b[%d;1m[%s] %s\u001b[0m", color, prefix, fmt);
29 warn(replace(sprintf(f, ...args), "\n", "\n "), "\n");
32 let I = (...args) => _log('info', ...args);
33 let N = (...args) => _log('notice', ...args);
34 let W = (...args) => _log('warn', ...args);
35 let E = (...args) => _log('error', ...args);
37 let read_json_file = (path) => {
38 let fd = _fs.open(path, "r");
40 let data = fd.read("all");
47 E("Unable to parse JSON data in %s: %s", path, e);
56 let format_json = (data) => {
59 let format_value = (value) => {
60 switch (type(value)) {
62 return sprintf("{ /* %d keys */ }", length(value));
65 return sprintf("[ /* %d items */ ]", length(value));
68 if (length(value) > 64)
69 value = substr(value, 0, 64) + "...";
72 return sprintf("%J", value);
75 return sprintf("%J", value);
83 let k = sort(keys(data));
86 rv += sprintf("%s %J: %s", i ? "," : "", n, format_value(data[n]));
94 for (let i, v in data)
95 rv += (i ? "," : "") + " " + format_value(v);
101 rv = format_value(data);
107 let trace_call = (ns, func, args) => {
108 let msg = "[call] " +
109 (ns ? ns + "." : "") +
112 for (let k, v in args) {
113 msg += ' ' + k + ' <';
118 msg += format_json(v);
128 switch (TRACE_CALLS) {
141 /* Setup mock environment */
144 /* Mock ubus module */
146 connect: function() {
150 call: (object, method, args) => {
151 let signature = [ object + "~" + method ];
153 if (type(args) == "object") {
154 for (let i, k in sort(keys(args))) {
155 switch (type(args[k])) {
160 push(signature, k + "-" + replace(args[k], /[^A-Za-z0-9_-]+/g, "_"));
164 push(signature, type(args[k]));
171 for (let i = length(signature); i > 0; i--) {
172 let path = sprintf("./tests/mocks/ubus/%s.json", join("~", signature)),
173 mock = read_json_file(path);
176 self._error = "Invalid argument";
181 trace_call("ctx", "call", { object, method, args });
186 push(candidates, path);
190 I("No response fixture defined for ubus call %s/%s with arguments %s.", object, method, args);
191 I("Provide a mock response through one of the following JSON files:\n%s\n", join("\n", candidates));
193 self._error = "Method not found";
198 disconnect: () => null,
200 error: () => self.error()
213 /* Mock uci module */
218 load: function(file) {
219 let basename = replace(file, /^.+\//, ''),
220 path = sprintf("./tests/mocks/uci/%s.json", basename),
221 mock = read_json_file(path);
223 if (!mock || mock != mock) {
224 I("No configuration fixture defined for uci package %s.", file);
225 I("Provide a mock configuration through the following JSON file:\n%s\n", path);
230 this._configs[basename] = mock;
233 _get_section: function(config, section) {
234 if (!exists(this._configs, config)) {
237 if (!exists(this._configs, config))
241 let extended = match(section, "^@([A-Za-z0-9_-]+)\[(-?[0-9]+)\]$");
244 let stype = extended[1],
245 sindex = +extended[2],
248 for (let sid, sobj in this._configs[config])
249 if (sobj[".type"] == stype)
250 push(sections, sobj);
252 sort(sections, (a, b) => (a[".index"] || 999) - (b[".index"] || 999));
255 sindex = sections.length + sindex;
257 return sections[sindex];
260 return this._configs[config][section];
263 get: function(config, section, option) {
264 let sobj = this._get_section(config, section);
266 if (option && index(option, ".") == 0)
268 else if (sobj && option)
271 return sobj[".type"];
274 get_all: function(config, section) {
275 return section ? this._get_section(config, section) : this._configs[config];
278 foreach: function(config, stype, cb) {
281 if (exists(this._configs, config)) {
284 for (let sid, sobj in this._configs[config]) {
287 if (stype == null || sobj[".type"] == stype) {
288 cb({ ".index": i - 1, ".type": stype, ".name": sid, ...sobj });
302 readlink: function(path) {
303 trace_call("fs", "readlink", { path });
305 return path + "-link";
308 stat: function(path) {
309 let file = sprintf("./tests/mocks/fs/stat~%s.json", replace(path, /[^A-Za-z0-9_-]+/g, '_')),
310 mock = read_json_file(file);
312 if (!mock || mock != mock) {
313 I("No stat result fixture defined for fs.stat() call on %s.", path);
314 I("Provide a mock result through the following JSON file:\n%s\n", file);
316 if (match(path, /\/$/))
317 mock = { type: "directory" };
319 mock = { type: "file" };
322 trace_call("fs", "stat", { path });
327 unlink: function(path) {
328 trace_call("fs", "unlink", { path });
333 popen: (cmdline, mode) => {
334 let read = (!mode || index(mode, "r") != -1),
335 path = sprintf("./tests/mocks/fs/popen~%s.txt", replace(cmdline, /[^A-Za-z0-9_-]+/g, '_')),
336 fd = read ? _fs.open(path, "r") : null,
340 mock = fd.read("all");
345 I("No stdout fixture defined for fs.popen() command %s.", cmdline);
346 I("Provide a mock output through the following text file:\n%s\n", path);
351 trace_call("fs", "popen", { cmdline, mode });
354 read: function(amount) {
364 let i = index(mock, "\n");
365 i = (i > -1) ? i + 1 : mock.length;
366 rv = substr(mock, 0, i);
367 mock = substr(mock, i);
373 rv = substr(mock, 0, n);
374 mock = substr(mock, n);
381 write: function() {},
382 close: function() {},
390 open: (fpath, mode) => {
391 let read = (!mode || index(mode, "r") != -1 || index(mode, "+") != -1),
392 path = sprintf("./tests/mocks/fs/open~%s.txt", replace(fpath, /[^A-Za-z0-9_-]+/g, '_')),
393 fd = read ? _fs.open(path, "r") : null,
397 mock = fd.read("all");
402 I("No stdout fixture defined for fs.open() path %s.", fpath);
403 I("Provide a mock output through the following text file:\n%s\n", path);
408 trace_call("fs", "open", { path: fpath, mode });
411 read: function(amount) {
421 let i = index(mock, "\n");
422 i = (i > -1) ? i + 1 : mock.length;
423 rv = substr(mock, 0, i);
424 mock = substr(mock, i);
430 rv = substr(mock, 0, n);
431 mock = substr(mock, n);
438 write: function() {},
439 close: function() {},
447 error: () => "Unspecified error"
451 /* Mock stdlib functions */
453 system: function(argv, timeout) {
454 trace_call(null, "system", { command: argv, timeout });
465 print: function(...args) {
466 if (length(args) == 1 && type(args[0]) in ["array", "object"])
467 printf("%s\n", format_json(args[0]));
469 global.print(...args);
474 /* Execute test file */
477 E("The TESTFILE variable is not defined.");
479 include(TESTFILE, mocks);