TestRunner.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * You may redistribute this program and/or modify it under the terms of
  3. * the GNU General Public License as published by the Free Software Foundation,
  4. * either version 3 of the License, or (at your option) any later version.
  5. *
  6. * This program is distributed in the hope that it will be useful,
  7. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. * GNU General Public License for more details.
  10. *
  11. * You should have received a copy of the GNU General Public License
  12. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. */
  14. var Spawn = require('child_process').spawn;
  15. var Http = require("http");
  16. var Fs = require("fs");
  17. var TIMEOUT = 600000;
  18. if (process.env.TestRunner_TIMEOUT && !isNaN(Number(process.env.TestRunner_TIMEOUT))) {
  19. TIMEOUT = Number(process.env.TestRunner_TIMEOUT);
  20. }
  21. console.log("timeout is " + TIMEOUT);
  22. var parseURL = function (url)
  23. {
  24. url = url.replace(/[a-zA-Z]*:\/\//, '');
  25. var path = url.split('/')[1] || '';
  26. url = url.split('/')[0];
  27. var port = url.split(':')[1];
  28. url = url.split(':')[0];
  29. port = Number(port) || 8083;
  30. var host = url || undefined;
  31. return {
  32. host: host,
  33. port: port,
  34. path: '/' + path
  35. };
  36. };
  37. var remote = module.exports.remote = function (url, argv) {
  38. var params = parseURL(url);
  39. if (!params.host) { throw new Error("For the client, the hostname is required"); }
  40. params.headers = { args: [ JSON.stringify(argv) ] };
  41. params.method = 'POST';
  42. return function (fileName, callback) {
  43. var out = [];
  44. out.push('Testing Remotely');
  45. var req = Http.request(params, function(res) {
  46. if (res.statusCode !== 200) {
  47. out.push('STATUS: ' + res.statusCode);
  48. out.push('HEADERS: ' + JSON.stringify(res.headers));
  49. }
  50. res.setEncoding('utf8');
  51. var body = '';
  52. res.on('data', function (chunk) { body += String(chunk); });
  53. res.on('end', function () {
  54. var ret = JSON.parse(body);
  55. if (ret.returnCode !== 0) {
  56. output.push(ret.stdout);
  57. }
  58. out.push(ret.stderr);
  59. callback(out.join('\n'), (ret.returnCode !== 0));
  60. });
  61. });
  62. Fs.createReadStream(fileName).pipe(req);
  63. req.on('error', function(e) {
  64. console.log('problem with request: ' + e.message);
  65. });
  66. };
  67. };
  68. var client = function (url, fileName, argv) {
  69. var params = parseURL(url);
  70. if (!params.host) { throw new Error("For the client, the hostname is required"); }
  71. params.headers = { args: [ JSON.stringify(argv) ] };
  72. params.method = 'POST';
  73. var req = Http.request(params, function(res) {
  74. if (res.statusCode !== 200) {
  75. console.log('STATUS: ' + res.statusCode);
  76. console.log('HEADERS: ' + JSON.stringify(res.headers));
  77. }
  78. res.setEncoding('utf8');
  79. var body = '';
  80. res.on('data', function (chunk) { body += String(chunk); });
  81. res.on('end', function () {
  82. var ret = JSON.parse(body);
  83. process.stdout.write(ret.stdout);
  84. process.stderr.write(ret.stderr);
  85. process.exit(ret.returnCode);
  86. });
  87. });
  88. Fs.createReadStream(fileName).pipe(req);
  89. req.on('error', function(e) {
  90. console.log('problem with request: ' + e.message);
  91. });
  92. };
  93. /// Server
  94. var spawnProc = function(file, args, callback, timeoutMilliseconds) {
  95. var child = Spawn(file, args);
  96. var out = '', err = '';
  97. var t0 = +new Date();
  98. var to = setTimeout(function() {
  99. child.kill('SIGKILL');
  100. err += "test TIMEOUT in [" + ((+new Date()) - t0) +
  101. "ms] try TestRunner_TIMEOUT=<number>\n";
  102. callback(1000, out, err);
  103. }, timeoutMilliseconds);
  104. child.stdout.on('data', function (data) { out += String(data); });
  105. child.stderr.on('data', function (data) { err += String(data); });
  106. child.on('close', function (code) {
  107. clearTimeout(to);
  108. callback(code, out, err);
  109. });
  110. child.on('error', function(err) {
  111. clearTimeout(to);
  112. callback(1, '', err.stack);
  113. });
  114. };
  115. var local = module.exports.local = function (argv) {
  116. return function (fileName, callback) {
  117. var output = [];
  118. spawnProc(fileName, argv, function (code, out, err) {
  119. if (code !== 0) {
  120. output.push(out);
  121. }
  122. output.push(err);
  123. callback(output.join('\n'), (code !== 0));
  124. }, TIMEOUT);
  125. };
  126. };
  127. var send = function (response, content) {
  128. response.writeHeader(200, {"Content-Type": "text/plain"});
  129. response.write(content);
  130. response.end();
  131. };
  132. var runTest = function (fileName, args, response, timeoutMilliseconds) {
  133. Fs.chmodSync(fileName, '755');
  134. setTimeout(function () {
  135. spawnProc(fileName, args, function(code, out, err) {
  136. send(response, JSON.stringify({ returnCode: code, stdout: out, stderr: err }));
  137. //Fs.unlink(fileName);
  138. }, timeoutMilliseconds);
  139. }, 100);
  140. };
  141. var server = function (url, tempDir) {
  142. var params = parseURL(url);
  143. tempDir = tempDir || '/tmp';
  144. console.log("Serving http://" + (params.host || '<any>') + ':' + params.port + params.path);
  145. Http.createServer(function(request, response) {
  146. if (request.method === 'POST' && request.url === params.path) {
  147. var args = JSON.parse(request.headers.args);
  148. var fileName = tempDir + "/test-" + String(Math.random()).substring(2) + ".exe";
  149. request.pipe(Fs.createWriteStream(fileName));
  150. request.on("end", function() {
  151. runTest(fileName, args, response, TIMEOUT);
  152. });
  153. } else {
  154. response.end();
  155. }
  156. }).listen(params.port, params.host);
  157. };
  158. /// Main
  159. var usage = function ()
  160. {
  161. console.log(
  162. "Usage:\n" +
  163. " TestRunner server [http://][<hostname>][:<port>][/<password>] [tempDir]\n" +
  164. " bind to <port> (default port is 8083) and allow remote execution by anyone\n" +
  165. " providing <password>. <tempDir> will be used for temporary files (default is /tmp)\n" +
  166. " http:// is optional, you can put https:// or penispenispenis:// or whatever you\n" +
  167. " want but only http is supported.\n" +
  168. " To bind to all interfaces, you can omit the hostname, eg: :3333/supersecret\n" +
  169. " and to have the default port, you can provide only the password eg: /secret\n" +
  170. " or the hostname and password, eg: mycomputer/secret\n" +
  171. " The server will print a line explaining exactly what hostname/port/pass it is\n" +
  172. " serving.\n" +
  173. "\n" +
  174. " TestRunner client [http://][<hostname>][:<port>][/<password>] <executable> [argv1, " +
  175. "[argv2, ...]]\n" +
  176. " connect to the specified URL and upload <exectable> to be run with\n" +
  177. " arguments <argv1> <argv2> ...\n" +
  178. " the URL has the same semantics as that for the server except the hostname must be\n" +
  179. " specified.");
  180. };
  181. var main = function (argv)
  182. {
  183. var cli = argv.indexOf("client");
  184. if (cli !== -1) {
  185. argv.splice(0,cli+1);
  186. var cliUrl = argv.shift();
  187. var fileName = argv.shift();
  188. return client(cliUrl, fileName, argv);
  189. }
  190. var serv = argv.indexOf("server");
  191. if (serv !== -1) {
  192. argv.splice(0,serv+1);
  193. var servUrl = argv.shift();
  194. var tempDir = argv.shift();
  195. return server(servUrl, tempDir);
  196. }
  197. return usage();
  198. };
  199. if (!module.parent) {
  200. main(process.argv);
  201. }