mocha-distributed 0.8.1 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/master-server.js DELETED
@@ -1,194 +0,0 @@
1
- // -----------------------------------------------------------------------------
2
- // master-mocha-bindings.js
3
- //
4
- // Copyright(c) 2019 Pau Sanchez - MIT License
5
- // -----------------------------------------------------------------------------
6
- const url = require('url');
7
- const querystring = require('querystring');
8
- const constants = require('./constants.js');
9
-
10
- let g_testsPerRunner = new Map();
11
- let g_testEventEmitter = null;
12
-
13
- // -----------------------------------------------------------------------------
14
- // setEventEmitter
15
- //
16
- // To communicate with the mocha bindings
17
- // -----------------------------------------------------------------------------
18
- function setEventEmitter (testEventEmitter) {
19
- g_testEventEmitter = testEventEmitter;
20
- }
21
-
22
- // -----------------------------------------------------------------------------
23
- // handleRunnerShouldRun
24
- //
25
- // REST: /runner/<runner-id>/should-run?test=xxxxx
26
- //
27
- // Asks whether the client should run given test
28
- // -----------------------------------------------------------------------------
29
- function handleRunnerShouldRun (req, res) {
30
- const queryObj = querystring.parse(req.route.query);
31
- const splitted = req.route.pathname.split('/');
32
- const runnerId = splitted[2];
33
- const action = splitted[3];
34
-
35
- const testId = queryObj.test || null;
36
-
37
- // is this test already assigned to somebody else?
38
- if (!testId) {
39
- res.write (
40
- JSON.stringify ({
41
- answer : 'skip',
42
- reason : constants.ERRORS.INVALID_TEST_ID
43
- })
44
- )
45
- return;
46
- }
47
-
48
- if (g_testsPerRunner.has(testId)) {
49
- const runner = g_testsPerRunner.get (testId).runner;
50
-
51
- // is it a retry? e.g. same runner...
52
- if (runnerId === runner) {
53
- g_testsPerRunner.get (testId).retries ++;
54
- res.write (JSON.stringify ({ answer : 'run' }));
55
- return;
56
- }
57
-
58
- // otherwise, it is already running :)
59
- res.write (
60
- JSON.stringify ({
61
- answer : 'skip',
62
- reason : constants.ERRORS.ALREADY_RUNNING,
63
- runner : runner
64
- })
65
- );
66
- return;
67
- }
68
-
69
- // make this runner the owner of running this test
70
- g_testsPerRunner.set (testId, {
71
- id : testId,
72
- runner : runnerId,
73
- status : constants.TEST_STATUS_RUNNING,
74
- retries: 0,
75
- start : Date.now(),
76
- end : false,
77
- error : null
78
- });
79
-
80
- res.write (
81
- JSON.stringify ({
82
- answer : 'run'
83
- })
84
- );
85
- }
86
-
87
- // -----------------------------------------------------------------------------
88
- // handleRunnerResult
89
- //
90
- // REST: /runner/<runner-id>/result?test=xxxxx&status=success|error
91
- //
92
- // Informs the result of running given test
93
- // -----------------------------------------------------------------------------
94
- function handleRunnerResult (req, res) {
95
- const queryObj = querystring.parse(req.route.query);
96
- const splitted = req.route.pathname.split('/');
97
- const runnerId = splitted[2];
98
- const action = splitted[3];
99
-
100
- const testId = queryObj.test || null;
101
- if (!testId || !g_testsPerRunner.has(testId)) {
102
- res.write (
103
- JSON.stringify ({
104
- error : constants.ERRORS.INVALID_TEST_ID
105
- })
106
- )
107
- return;
108
- }
109
-
110
- const test = g_testsPerRunner.get (testId);
111
- if (test.runner !== runnerId) {
112
- res.write (
113
- JSON.stringify ({
114
- error : constants.ERRORS.INVALID_RUNNER_OWNERSHIP
115
- })
116
- )
117
- return;
118
- }
119
-
120
- let status = queryObj.status || constants.TEST_STATUS_FAILED;
121
- if (status !== constants.TEST_STATUS_SUCCESS) {
122
- status = constants.TEST_STATUS_FAILED;
123
- }
124
-
125
- test.status = status;
126
- test.error = JSON.parse(querystring.unescape (queryObj.error || 'null'));
127
-
128
- res.write ( JSON.stringify ({ 'status' : status }) );
129
-
130
- // Emit an event-finished for given test ID, we use the ID because that
131
- // should be unique.
132
- g_testEventEmitter.emit (constants.EVENT_FINISHED + ':' + testId, test);
133
- }
134
-
135
- // -----------------------------------------------------------------------------
136
- // handleRunners
137
- //
138
- // REST: /runner/<runner-id>/*
139
- //
140
- // Handles all requests from all runners (registration, queries, ...)
141
- // -----------------------------------------------------------------------------
142
- function handleRunners (req, res) {
143
- const queryObj = querystring.parse(req.route.query);
144
- const splitted = req.route.pathname.split('/');
145
- const runnerId = splitted[2];
146
- const action = splitted[3];
147
-
148
- // TODO: check that runner is registered
149
- if (!runnerId || !action) {
150
- res.end();
151
- return;
152
- }
153
-
154
- switch (action) {
155
- case 'should-run': handleRunnerShouldRun (req, res); break;
156
- case 'result': handleRunnerResult(req, res); break;
157
- default:
158
- console.error ("Invalid action: " + action + "(" + req.url + ")");
159
- res.write (
160
- JSON.stringify ({ error : constants.ERRORS.INVALID_REQUEST_ACTION })
161
- )
162
- break;
163
- }
164
- res.end();
165
- }
166
-
167
- // -----------------------------------------------------------------------------
168
- // mainServerHandler
169
- //
170
- // REST: /*
171
- //
172
- // Handles all requests to this master server
173
- // -----------------------------------------------------------------------------
174
- function mainServerHandler (req, res) {
175
- res.writeHead(200, {'Content-Type': 'application/javascript'});
176
- req.route = url.parse(req.url);
177
-
178
- // REST: /runner/<runner-id>/*
179
- //
180
- // execute actions from given runner
181
- if (req.route.pathname.startsWith (`/runner/`)) {
182
- handleRunners (req, res);
183
- }
184
- else {
185
- res.write(JSON.stringify ({ error : constants.ERRORS.INVALID_REQUEST }));
186
- res.end();
187
- }
188
- }
189
-
190
-
191
- module.exports = {
192
- setEventEmitter,
193
- mainServerHandler
194
- };
package/master.js DELETED
@@ -1,47 +0,0 @@
1
- // -----------------------------------------------------------------------------
2
- // master.js
3
- //
4
- // Copyright(c) 2019 Pau Sanchez - MIT License
5
- // -----------------------------------------------------------------------------
6
- const http = require('http');
7
- const EventEmitter = require('events');
8
- const masterServer = require ('./master-server.js');
9
- const masterMochaBindings = require ('./master-mocha-bindings.js');
10
-
11
- // used to notify master that a test has been executed
12
- class TestEmitter extends EventEmitter {};
13
-
14
- let g_server = null;
15
- let g_eventEmitter = null;
16
-
17
- // -----------------------------------------------------------------------------
18
- // master
19
- //
20
- // Initializes the master server, redefines variables, etc...
21
- // -----------------------------------------------------------------------------
22
- function master (port) {
23
-
24
- // Checking server because this master function might be called once
25
- // per every mocha file, and we only want to initialize once.
26
- if (g_server === null) {
27
- g_eventEmitter = new TestEmitter();
28
-
29
- masterServer.setEventEmitter(g_eventEmitter);
30
- masterMochaBindings.setEventEmitter (g_eventEmitter);
31
-
32
- g_server = http.createServer(masterServer.mainServerHandler);
33
- g_server.listen(port, function() {
34
- console.log("# Mocha distributed master listening at port:", port, "\n");
35
- });
36
- }
37
-
38
- // hook all mocha methods
39
- global.describe = masterMochaBindings.describe;
40
- global.it = masterMochaBindings.it;
41
- global.before = masterMochaBindings.before;
42
- global.beforeEach = masterMochaBindings.beforeEach;
43
- global.after = masterMochaBindings.after;
44
- global.afterEach = masterMochaBindings.afterEach;
45
- }
46
-
47
- module.exports = master;
@@ -1,212 +0,0 @@
1
- // -----------------------------------------------------------------------------
2
- // runner-mocha-bindings.js
3
- //
4
- // NOTE: mocha first does a first pass executing all describe/it/... and then
5
- // when it has gathered all information, it runs the functions associated
6
- // to each. What we do is we hook our method in the middle and call
7
- // or not, the original test method.
8
- //
9
- // Copyright(c) 2019 Pau Sanchez - MIT License
10
- // -----------------------------------------------------------------------------
11
- const axios = require('axios');
12
- const querystring = require('querystring');
13
- const crypto = require('crypto');
14
-
15
- const constants = require('./constants.js');
16
-
17
- let g_masterAddress = null;
18
-
19
- // Generate a unique random id for this runner (with almost 100% certainty
20
- // to be different on any machine/environment).
21
- const g_runnerIdBuffer = Buffer.alloc(16);
22
- const g_runnerId = crypto.randomFillSync(g_runnerIdBuffer).toString('hex');
23
-
24
- let g_mochaMethods = {
25
- describe : global.describe || null,
26
- it : global.it || null,
27
- before : global.before || null,
28
- beforeEach : global.beforeEach || null,
29
- after : global.after || null,
30
- afterEach : global.afterEach || null
31
- };
32
-
33
-
34
- // contains the actual test path such as suite.title > suite.title > it.title
35
- let g_testPath = [];
36
-
37
- // -----------------------------------------------------------------------------
38
- // askMasterIfShouldRun
39
- //
40
- // Asks master server if the current runner should run given suite.
41
- //
42
- // Right now we only ask for suites because it seems the safest approach.
43
- //
44
- // Returns:
45
- // - true: the caller should run the test
46
- // - false: the caller should not run the test
47
- // - null: connectivity issue with the master, it depends on the caller to
48
- // decide what to do
49
- // -----------------------------------------------------------------------------
50
- async function askMasterIfShouldRun (testPath) {
51
- const testSuite = querystring.escape(testPath[0]);
52
- try {
53
- const r = await axios.get (`http://${g_masterAddress}/runner/${g_runnerId}/should-run?test=${testSuite}`);
54
- const ok = (r.data.answer === 'run');
55
-
56
- // we ask for given test, so we can save the result properly
57
- // TODO: this can be removed if the server accepts runners
58
- if (ok) {
59
- const path = querystring.escape(testPath.join(constants.TEST_PATH_SEPARATOR));
60
- const r = await axios.get (`http://${g_masterAddress}/runner/${g_runnerId}/should-run?test=${path}`);
61
- }
62
- return ok;
63
- }
64
- catch (e) {
65
- // ignore connection errors
66
- // the master could have died because it already finished
67
- }
68
-
69
- // skip (should not run) if there is any kind of error with master
70
- return null;
71
- }
72
-
73
- // -----------------------------------------------------------------------------
74
- // sendTestResultToMaster
75
- // -----------------------------------------------------------------------------
76
- async function sendTestResultToMaster (testPath, status, error = null) {
77
- // TODO: use POST
78
- const path = querystring.escape(testPath.join(constants.TEST_PATH_SEPARATOR));
79
- let serror = querystring.escape(JSON.stringify(error || null));
80
-
81
- try {
82
- const r = await axios.get (
83
- `http://${g_masterAddress}/runner/${g_runnerId}/result?test=${path}&status=${status}&error=${serror}`
84
- );
85
- }
86
- catch (e) {
87
- // ignore connection errors
88
- // the master could have died because it already finished
89
- }
90
- }
91
-
92
- // -----------------------------------------------------------------------------
93
- // init
94
- //
95
- // Initialize this module
96
- // -----------------------------------------------------------------------------
97
- function init (masterAddress){
98
- g_masterAddress = masterAddress;
99
- }
100
-
101
- // -----------------------------------------------------------------------------
102
- // describe
103
- //
104
- // Custom version to describe a test, with same signature
105
- // -----------------------------------------------------------------------------
106
- async function describe (title, fn) {
107
- g_testPath.push (title);
108
- const result = g_mochaMethods.describe (title, fn);
109
- g_testPath.pop();
110
-
111
- return result;
112
- }
113
-
114
- // TODO:
115
- // describe.skip = async function (title, fn) { }
116
- // describe.once = async function (title, fn) { }
117
-
118
-
119
- // -----------------------------------------------------------------------------
120
- // it
121
- // -----------------------------------------------------------------------------
122
- async function it (title, fn) {
123
- // store current path for when the 'it' function executes
124
- g_testPath.push (title);
125
- const testPath = g_testPath.slice(0);
126
- g_testPath.pop();
127
-
128
- // define our own function hook
129
- return g_mochaMethods.it (title, async function () {
130
- if (!await askMasterIfShouldRun (testPath)) {
131
- this.skip();
132
- return;
133
- }
134
-
135
- try {
136
- const testResult = await fn();
137
- await sendTestResultToMaster (testPath, constants.TEST_STATUS_SUCCESS);
138
- return testResult;
139
- }
140
- catch (e) {
141
- await sendTestResultToMaster (testPath, constants.TEST_STATUS_FAILED, e);
142
- throw e;
143
- }
144
- });
145
- }
146
-
147
- // -----------------------------------------------------------------------------
148
- // execHookMochaMethod
149
- //
150
- // Method used accross all mocha hooks (before, beforeEach, after, afterEach)
151
- // in order to query the master server if it needs to be executed or not.
152
- // -----------------------------------------------------------------------------
153
- function execHookMochaMethod (title, orgMochaMethod, fn) {
154
- g_testPath.push (title);
155
- const testPath = g_testPath.slice(0);
156
- g_testPath.pop();
157
-
158
- return orgMochaMethod (async function () {
159
- if (!await askMasterIfShouldRun (testPath)) {
160
- this.skip();
161
- return;
162
- }
163
-
164
- try {
165
- const testResult = await fn();
166
- await sendTestResultToMaster (testPath, constants.TEST_STATUS_SUCCESS);
167
- return testResult;
168
- }
169
- catch (e) {
170
- await sendTestResultToMaster (testPath, constants.TEST_STATUS_FAILED, e);
171
- throw e;
172
- }
173
- });
174
- }
175
-
176
- // -----------------------------------------------------------------------------
177
- // before
178
- // -----------------------------------------------------------------------------
179
- async function before (fn) {
180
- return execHookMochaMethod (':before', g_mochaMethods.before, fn);
181
- }
182
-
183
- // -----------------------------------------------------------------------------
184
- // beforeEach
185
- // -----------------------------------------------------------------------------
186
- async function beforeEach (fn) {
187
- return execHookMochaMethod (':beforeEach', g_mochaMethods.beforeEach, fn);
188
- }
189
-
190
- // -----------------------------------------------------------------------------
191
- // after
192
- // -----------------------------------------------------------------------------
193
- async function after (fn) {
194
- return execHookMochaMethod (':after', g_mochaMethods.after, fn);
195
- }
196
-
197
- // -----------------------------------------------------------------------------
198
- // afterEach
199
- // -----------------------------------------------------------------------------
200
- async function afterEach (fn) {
201
- return execHookMochaMethod (':afterEach', g_mochaMethods.after, fn);
202
- }
203
-
204
- module.exports = {
205
- init,
206
- describe,
207
- it,
208
- before,
209
- beforeEach,
210
- after,
211
- afterEach
212
- };
package/runner.js DELETED
@@ -1,20 +0,0 @@
1
- // -----------------------------------------------------------------------------
2
- // runner.js
3
- //
4
- // Copyright(c) 2019 Pau Sanchez - MIT License
5
- // -----------------------------------------------------------------------------
6
- const runnerMochaBindings = require ('./runner-mocha-bindings.js');
7
-
8
- function runner (masterAddress, port) {
9
- runnerMochaBindings.init (masterAddress + ':' + port);
10
-
11
- // hook all mocha methods
12
- global.describe = runnerMochaBindings.describe;
13
- global.it = runnerMochaBindings.it;
14
- global.before = runnerMochaBindings.before;
15
- global.beforeEach = runnerMochaBindings.beforeEach;
16
- global.after = runnerMochaBindings.after;
17
- global.afterEach = runnerMochaBindings.afterEach;
18
- }
19
-
20
- module.exports = runner;