mocklib.uc 4.1 KB

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