mocha-distributed 0.8.0 → 0.9.2
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/README.md +201 -93
- package/docker-compose.yml +27 -0
- package/index.js +161 -40
- package/list-tests-from-redis.js +85 -0
- package/package.json +8 -9
- package/constants.js +0 -33
- package/master-mocha-bindings.js +0 -159
- package/master-server.js +0 -194
- package/master.js +0 -47
- package/runner-mocha-bindings.js +0 -212
- package/runner.js +0 -20
package/README.md
CHANGED
|
@@ -1,159 +1,267 @@
|
|
|
1
1
|
# mocha-distributed
|
|
2
2
|
|
|
3
|
+
Run mocha tests faster.
|
|
4
|
+
|
|
5
|
+
Speed up your mocha tests by running them in parallel in multiple machines all
|
|
6
|
+
at once without changing a single line of code. You only need a redis server.
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
3
10
|
The aim of this project is to provide a simple way of running distributed mocha
|
|
4
|
-
tests without having to change
|
|
5
|
-
what to run where.
|
|
11
|
+
tests without having to change any line of code, nor having to decide
|
|
12
|
+
what to run where. Tests spread automatically according to the nodes you have.
|
|
13
|
+
|
|
14
|
+
The concept is very simple, basically you spawn as many runners as you wish
|
|
15
|
+
on as many nodes as you wish, and each node decides whether they should run
|
|
16
|
+
a test or the test has already been executed or is being executed somewhere
|
|
17
|
+
else.
|
|
6
18
|
|
|
7
19
|
It does not matter if you run the tests in one machine as subprocesses or in
|
|
8
20
|
many machines with multiple processes each.
|
|
9
21
|
|
|
10
|
-
|
|
11
|
-
|
|
22
|
+
Because you don't need to change a single line of code, which means that you
|
|
23
|
+
can still run mocha locally as usual.
|
|
12
24
|
|
|
13
|
-
|
|
14
|
-
without any side-effects locally, using mocha, as if nothing was changed.
|
|
25
|
+
## Quick start
|
|
15
26
|
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
You don't need to change a single line of code on your tests, this project uses
|
|
28
|
+
mocha hooks in order to work, so the only thing you'll need to do in preparation
|
|
29
|
+
is:
|
|
18
30
|
|
|
19
|
-
|
|
31
|
+
```bash
|
|
32
|
+
$ npm install -s mocha-distributed
|
|
33
|
+
```
|
|
20
34
|
|
|
21
|
-
|
|
35
|
+
Make sure you have a redis running somewhere with IP visibility from the machine
|
|
36
|
+
or machines where you want to run the tests on.
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
to allow running tests across machines without you having to decide what runs
|
|
25
|
-
where, or splitting tests beforehand, etc...
|
|
38
|
+
Finally, on each of the runners just run:
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
```bash
|
|
41
|
+
$ export MOCHA_DISTRIBUTED_EXECUTION_ID="execution__2021-01-01__20:10"
|
|
42
|
+
$ export MOCHA_DISTRIBUTED="redis://redis.address"
|
|
43
|
+
$ mocha --require mocha-distributed test/**/*.js
|
|
44
|
+
```
|
|
30
45
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
If they are not, they just skip the tests and continue running the next suite.
|
|
46
|
+
There are several environment variables that allow you to control the behaviour
|
|
47
|
+
of distributed tests, but this is the simplest way to launch them.
|
|
34
48
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
that encloses those.
|
|
49
|
+
MOCHA_DISTRIBUTED is the one holding the redis address, this is the only
|
|
50
|
+
requirement to make mocha-distributed work.
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
MOCHA_DISTRIBUTED_EXECUTION_ID is the other variable you want to pay attention
|
|
53
|
+
to. Make sure you use a different value for each group of runners every time
|
|
54
|
+
you launch a test. This variable is what makes possible to make a runner know
|
|
55
|
+
whether a test has already been executed or not by other of their peers.
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
parameters. Using those parameters on the runners won't hurt either.
|
|
57
|
+
## Environment Variables
|
|
44
58
|
|
|
45
|
-
|
|
59
|
+
- **MOCHA_DISTRIBUTED** (required)
|
|
46
60
|
|
|
47
|
-
|
|
61
|
+
Right now this variable is the one used to specify the node that will hold
|
|
62
|
+
information about tests being run. This project only supports redis right
|
|
63
|
+
now. This variable can take the form:
|
|
64
|
+
|
|
65
|
+
redis[s]://[[username][:password]@][host][:port]
|
|
48
66
|
|
|
49
|
-
|
|
50
|
-
as if you would have not installed this module.
|
|
67
|
+
Please make sure it has visibility to the desired redis server.
|
|
51
68
|
|
|
52
|
-
|
|
53
|
-
an HTTP server and listen to other processes or machines to connect to it and
|
|
54
|
-
ask/inform about running the tests.
|
|
69
|
+
- **MOCHA_DISTRIBUTED_EXECUTION_ID** (required)
|
|
55
70
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
tests
|
|
59
|
-
be the IP of that machine in your private network, or the public IP if the
|
|
60
|
-
machines are distributed around the world.
|
|
71
|
+
Make sure this value is different every time you launch your tests. You can
|
|
72
|
+
use any string here, but it should be different across test executions or
|
|
73
|
+
your tests will just be skipped after the second execution.
|
|
61
74
|
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
Execution ID is used in order to differentiate different runs of the same
|
|
76
|
+
tests among parallel executions. If you launch 10 instances and you want
|
|
77
|
+
tests to be distributed among them, all need to have the same value for this
|
|
78
|
+
variable, otherwise each of them will run all the tests on its own.
|
|
64
79
|
|
|
65
|
-
|
|
80
|
+
Reusing this variable in different executions will cause your tests to be
|
|
81
|
+
skipped.
|
|
66
82
|
|
|
67
|
-
|
|
83
|
+
Use a random uuid or other random value, a kubernetes job_name, your
|
|
84
|
+
build system job id, ...
|
|
68
85
|
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
- **MOCHA_DISTRIBUTED_GRANULARITY** = test
|
|
87
|
+
|
|
88
|
+
- test (default)
|
|
89
|
+
Potentially all tests can be executed by any runner in any order. This
|
|
90
|
+
is the default, but if you have trouble running your tests in parallel
|
|
91
|
+
please use "suite" instead
|
|
71
92
|
|
|
72
|
-
|
|
73
|
-
require('mocha-distributed');
|
|
74
|
-
```
|
|
93
|
+
- suite (safest)
|
|
75
94
|
|
|
76
|
-
|
|
95
|
+
Launch all tests from the same suite in the same runner. This prevents
|
|
96
|
+
some parallelization errors if your tests are not prepared for full
|
|
97
|
+
paralelization.
|
|
77
98
|
|
|
78
|
-
|
|
79
|
-
$ mocha test/\*\*/\*.js
|
|
80
|
-
```
|
|
99
|
+
- **MOCHA_DISTRIBUTED_RUNNER_ID** = random-id
|
|
81
100
|
|
|
82
|
-
|
|
101
|
+
By default this value is initialized automatically with a different random
|
|
102
|
+
string in each machine, BUT you can override this in case you need it for
|
|
103
|
+
whatever reason, although in theory you probably shouldn't.
|
|
83
104
|
|
|
84
|
-
-
|
|
105
|
+
- **MOCHA_DISTRIBUTED_EXPIRATION_TIME** = 86400
|
|
85
106
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
107
|
+
Configures to how long the data is kept in redis before it expires (in
|
|
108
|
+
seconds). The amount of data in redis is minimal, so you probably don't want
|
|
109
|
+
to play with it.
|
|
89
110
|
|
|
90
|
-
|
|
111
|
+
It might be helpful to increase it though, if you want to build some sort of
|
|
112
|
+
reporting on top of it, because you can directly explore test results in
|
|
113
|
+
redis. See Tests results in Redis for more info.
|
|
91
114
|
|
|
92
|
-
```bash
|
|
93
|
-
$ MOCHA_DISTRIBUTED="1.2.3.4" mocha test/\*\*/\*.js
|
|
94
|
-
```
|
|
95
115
|
|
|
96
|
-
|
|
116
|
+
- **MOCHA_DISTRIBUTED_VERBOSE** = false
|
|
117
|
+
- false (default)
|
|
118
|
+
Avoid printing verbose information
|
|
97
119
|
|
|
98
|
-
|
|
120
|
+
- true
|
|
121
|
+
Prints some extra information about the variables, the server, ...
|
|
122
|
+
that might be useful for debugging issues and/or informational.
|
|
99
123
|
|
|
100
|
-
|
|
124
|
+
## Reading test results from Redis
|
|
101
125
|
|
|
102
|
-
|
|
103
|
-
|
|
126
|
+
All runners write the test result in JSON format in a specific redis list.
|
|
127
|
+
|
|
128
|
+
The list is basically the execution ID from the variable
|
|
129
|
+
MOCHA_DISTRIBUTED_EXECUTION_ID concatenated to ':test_result'
|
|
130
|
+
|
|
131
|
+
For example, if you are using: MOCHA_DISTRIBUTED_EXECUTION_ID="abcdefg"
|
|
132
|
+
|
|
133
|
+
Then the key you should look at in redis will be "abcdefg:test_result"
|
|
134
|
+
|
|
135
|
+
You can access this list and explore the result of all tests. Each item
|
|
136
|
+
on the list will contain information about the test suite, test id, ...
|
|
137
|
+
test name, if it timed out or not, duration of the test, result of the test,
|
|
138
|
+
if there were any errors, ... all that info is extracted from mocha itself.
|
|
139
|
+
|
|
140
|
+
You will see something like this on each of the items of the list:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"id": [
|
|
145
|
+
"suite-1-async",
|
|
146
|
+
"test-1.1-async"
|
|
147
|
+
],
|
|
148
|
+
"type": "test",
|
|
149
|
+
"title": "test-1.1-async",
|
|
150
|
+
"timedOut": false,
|
|
151
|
+
"startTime": 1642705594300,
|
|
152
|
+
"endTime": 1642705594802,
|
|
153
|
+
"duration": 502,
|
|
154
|
+
"file": "/home/psanchez/github/mocha-distributed/example/suite-1.js",
|
|
155
|
+
"state": "passed",
|
|
156
|
+
"failed": false,
|
|
157
|
+
"speed": "slow",
|
|
158
|
+
"err": 0
|
|
159
|
+
}
|
|
104
160
|
```
|
|
105
161
|
|
|
106
|
-
|
|
162
|
+
The JSON formatting will differ since it is saved in a single line.
|
|
107
163
|
|
|
108
|
-
|
|
164
|
+
Keep in mind that:
|
|
165
|
+
|
|
166
|
+
* Duration and start/end times are in milliseconds.
|
|
167
|
+
* Some fields are duplicated in a way, like "state" and "failed" by design
|
|
168
|
+
because sometimes is handy to have this when reading results back.
|
|
169
|
+
* You can access test_result, passed_count and failed_count in redis
|
|
170
|
+
* Skipped tests are never saved in redis by design, unfortunately
|
|
171
|
+
|
|
172
|
+
You might have a look at list-tests-from-redis.js for an example on how to
|
|
173
|
+
query redis and list all tests.
|
|
174
|
+
|
|
175
|
+
## Examples
|
|
176
|
+
|
|
177
|
+
### Environment-agnostic
|
|
178
|
+
|
|
179
|
+
Make sure at least the following variables are set:
|
|
109
180
|
|
|
110
181
|
```bash
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
$ MOCHA_DISTRIBUTED="localhost" mocha test/\*\*/\*.js > /dev/null &
|
|
114
|
-
...
|
|
115
|
-
$ MOCHA_DISTRIBUTED="localhost" mocha test/\*\*/\*.js > /dev/null &
|
|
182
|
+
MOCHA_DISTRIBUTED="redis://1.2.3.4"
|
|
183
|
+
MOCHA_DISTRIBUTED_EXECUTION_ID="a5ce4d8a-5b06-4ec8-aea2-37d7e4b2ffe1"
|
|
116
184
|
```
|
|
117
185
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
### Run tests in several processes across several machines
|
|
186
|
+
Again, execution ID should be a different random number each time you want to
|
|
187
|
+
launch tests in parallel.
|
|
121
188
|
|
|
122
|
-
|
|
189
|
+
Example:
|
|
123
190
|
|
|
124
191
|
```bash
|
|
125
|
-
$
|
|
192
|
+
$ mocha --require mocha-distributed test/**/*.js
|
|
126
193
|
```
|
|
127
194
|
|
|
128
|
-
|
|
195
|
+
Of course, this assumes you have already installed mocha-distributed.
|
|
129
196
|
|
|
130
|
-
|
|
131
|
-
|
|
197
|
+
### Run tests in parallel in the same machine
|
|
198
|
+
|
|
199
|
+
To keep things simple, do something like this:
|
|
132
200
|
|
|
133
201
|
```bash
|
|
134
|
-
$
|
|
135
|
-
$ MOCHA_DISTRIBUTED="
|
|
202
|
+
$ MOCHA_DISTRIBUTED_EXECUTION_ID=`uuidgen`
|
|
203
|
+
$ MOCHA_DISTRIBUTED="redis://redis-server"
|
|
204
|
+
|
|
205
|
+
$ mocha --require mocha-distributed test/**/*.js > output01.txt &
|
|
206
|
+
$ mocha --require mocha-distributed test/**/*.js > output02.txt &
|
|
136
207
|
...
|
|
137
|
-
$
|
|
208
|
+
$ mocha --require mocha-distributed test/**/*.js > output0N.txt &
|
|
138
209
|
```
|
|
139
210
|
|
|
140
|
-
|
|
211
|
+
Run as many processes as you'd like.
|
|
212
|
+
|
|
213
|
+
### Using kubernetes parallel jobs to launch tests
|
|
214
|
+
|
|
215
|
+
If you plan to use kubernetes to launch parallel jobs, make sure the backoff
|
|
216
|
+
limit is set to 1, so it does not retry the job after it fails, and make sure
|
|
217
|
+
you set execution ID to a different value each time (but common across all
|
|
218
|
+
parallel executions).
|
|
219
|
+
|
|
220
|
+
The easiest is to use the job ID (not the pod ID). You can do that by exposing
|
|
221
|
+
pod metadata information as environment variables.
|
|
222
|
+
|
|
223
|
+
See https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/
|
|
224
|
+
|
|
225
|
+
### Conceptual overview
|
|
226
|
+
|
|
227
|
+
The concept is very simple, this module hooks all mocha calls and does some magic
|
|
228
|
+
to allow running tests across machines without you having to decide what runs
|
|
229
|
+
where, or splitting tests beforehand, etc...
|
|
230
|
+
|
|
231
|
+
To distribute tests you only need to create several processess across one
|
|
232
|
+
or more machines (this method won't care how you spawn your runners), and either
|
|
233
|
+
set one of them as the master or use a redis database, and launch as many runners
|
|
234
|
+
as you wish.
|
|
235
|
+
|
|
236
|
+
Each runners connects to the redis instance and for each suite or test,
|
|
237
|
+
depending on the granularity, they ask whether they are the 'owners' to run the
|
|
238
|
+
tests on that suite or not. If they are, they run it. If they are not, they just
|
|
239
|
+
skip the tests and continue running the next suite/tests.
|
|
240
|
+
|
|
241
|
+
### Caveats
|
|
242
|
+
|
|
243
|
+
When running with redis, all tests are executed by independent runners, which
|
|
244
|
+
means you need to take a look at the output of all the runners and see which
|
|
245
|
+
ones were skipped and which ones were executed for you to see if some of those
|
|
246
|
+
executed failed.
|
|
247
|
+
|
|
248
|
+
Also the exit code of the different mocha runners will differ. The
|
|
249
|
+
ones whose tests fail, will return an error, and the ones whose tests work well
|
|
250
|
+
or have been skipped will return 0.
|
|
141
251
|
|
|
142
252
|
## Build systems
|
|
143
253
|
|
|
144
254
|
### jenkins, bamboo, circle-ci, gitlab, travis...
|
|
145
255
|
|
|
146
|
-
If you use jenkins, bamboo or any other build system,
|
|
147
|
-
|
|
256
|
+
If you use jenkins, bamboo or any other build system, make sure
|
|
257
|
+
one redis is installed somewhere and all runners can access to it.
|
|
148
258
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
make sure you can send a ping from all the runners to the master).
|
|
259
|
+
Create as many processes, nodes, dockers, kubernetes pods as you wish,
|
|
260
|
+
but for each of the runners that you create, make sure each of them can connect
|
|
261
|
+
to the redis instance (e.g are in the same network).
|
|
153
262
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
inform the wrong master.
|
|
263
|
+
You can use the project name and build ID or job id as the execution ID for
|
|
264
|
+
mocha-distributed. Use something unique among the builds of all your projects.
|
|
157
265
|
|
|
158
266
|
## MIT License
|
|
159
267
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
version: "3.7"
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
|
|
5
|
+
redis:
|
|
6
|
+
image: redis:latest
|
|
7
|
+
ports:
|
|
8
|
+
- 6379:6379
|
|
9
|
+
|
|
10
|
+
networks:
|
|
11
|
+
- mordor_gh_network
|
|
12
|
+
|
|
13
|
+
redis-commander:
|
|
14
|
+
image: rediscommander/redis-commander:latest
|
|
15
|
+
environment:
|
|
16
|
+
- REDIS_HOSTS=local:redis:6379
|
|
17
|
+
ports:
|
|
18
|
+
- 8081:8081
|
|
19
|
+
depends_on:
|
|
20
|
+
- redis
|
|
21
|
+
|
|
22
|
+
networks:
|
|
23
|
+
- mordor_gh_network
|
|
24
|
+
|
|
25
|
+
networks:
|
|
26
|
+
mordor_gh_network:
|
|
27
|
+
external: true
|
package/index.js
CHANGED
|
@@ -1,49 +1,170 @@
|
|
|
1
1
|
// -----------------------------------------------------------------------------
|
|
2
|
-
//
|
|
2
|
+
// Copyright (c) 2018 Pau Sanchez
|
|
3
3
|
//
|
|
4
|
-
//
|
|
4
|
+
// MIT Licensed
|
|
5
5
|
// -----------------------------------------------------------------------------
|
|
6
|
+
const redis = require("redis");
|
|
7
|
+
const crypto = require("crypto");
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const GRANULARITY = {
|
|
10
|
+
TEST: "test",
|
|
11
|
+
SUITE: "suite",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Initialize variables from environment
|
|
15
|
+
const g_redisAddress = process.env.MOCHA_DISTRIBUTED || "";
|
|
16
|
+
const g_testExecutionId = process.env.MOCHA_DISTRIBUTED_EXECUTION_ID || "";
|
|
17
|
+
const g_expirationTime =
|
|
18
|
+
process.env.MOCHA_DISTRIBUTED_EXPIRATION_TIME || `${24 * 3600}`;
|
|
19
|
+
|
|
20
|
+
// Generate a unique random id for this runner (with almost 100% certainty
|
|
21
|
+
// to be different on any machine/environment).
|
|
22
|
+
const _randomRunnerBuf = Buffer.alloc(16);
|
|
23
|
+
const _randomRunnerId = crypto.randomFillSync(_randomRunnerBuf).toString("hex");
|
|
24
|
+
const g_runnerId = process.env.MOCHA_DISTRIBUTED_RUNNER_ID || _randomRunnerId;
|
|
25
|
+
let g_granularity =
|
|
26
|
+
process.env.MOCHA_DISTRIBUTED_GRANULARITY || GRANULARITY.TEST;
|
|
27
|
+
const g_mochaVerbose = process.env.MOCHA_DISTRIBUTED_VERBOSE === "true";
|
|
28
|
+
|
|
29
|
+
if (g_granularity !== GRANULARITY.TEST) {
|
|
30
|
+
g_granularity = GRANULARITY.SUITE;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let g_redis = null;
|
|
34
|
+
|
|
35
|
+
// -----------------------------------------------------------------------------
|
|
36
|
+
// getTestPath
|
|
12
37
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// name from the master, and will ask, before each suite or orphaned test
|
|
16
|
-
// if it needs to run it or not
|
|
38
|
+
// Returns an array with the test suites and test name from a test context
|
|
39
|
+
// as found in the hooks
|
|
17
40
|
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Initialize mode & port from environment variable MOCHA_DISTRIBUTED
|
|
26
|
-
const DEFAULT_PORT = 12421;
|
|
27
|
-
let mode = (process.env.MOCHA_DISTRIBUTED || '').toLowerCase();
|
|
28
|
-
let port = DEFAULT_PORT;
|
|
29
|
-
|
|
30
|
-
if (mode.indexOf (':') >= 0) {
|
|
31
|
-
const splitted = mode.split(':');
|
|
32
|
-
mode = splitted[0];
|
|
33
|
-
port = parseInt (splitted[1], 10);
|
|
34
|
-
}
|
|
41
|
+
// Example:
|
|
42
|
+
//
|
|
43
|
+
// >>> getTestPath(ctxt)
|
|
44
|
+
//
|
|
45
|
+
// -----------------------------------------------------------------------------
|
|
46
|
+
function getTestPath(testContext) {
|
|
47
|
+
const path = [testContext.title];
|
|
35
48
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
runner (masterAddress, port);
|
|
49
|
+
while (!testContext.root && testContext.parent) {
|
|
50
|
+
testContext = testContext.parent;
|
|
51
|
+
|
|
52
|
+
if (testContext && !testContext.root) {
|
|
53
|
+
path.push(testContext.title);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return path.reverse();
|
|
46
58
|
}
|
|
47
59
|
|
|
48
|
-
//
|
|
49
|
-
|
|
60
|
+
// -----------------------------------------------------------------------------
|
|
61
|
+
// Initialize redis once before the tests
|
|
62
|
+
// -----------------------------------------------------------------------------
|
|
63
|
+
exports.mochaGlobalSetup = async function () {
|
|
64
|
+
if (g_mochaVerbose) {
|
|
65
|
+
const redisNoCredentials = g_redisAddress.replace(
|
|
66
|
+
/\/\/[^@]*@/,
|
|
67
|
+
"//***:***@"
|
|
68
|
+
);
|
|
69
|
+
console.log("---------------------------------------------------");
|
|
70
|
+
console.log(" Mocha Distributed");
|
|
71
|
+
console.log(" - Runner Id :", g_runnerId);
|
|
72
|
+
console.log(" - Redis Address :", redisNoCredentials);
|
|
73
|
+
console.log(" - Execution Id :", g_testExecutionId);
|
|
74
|
+
console.log(" - Data Expiration Time :", g_expirationTime);
|
|
75
|
+
console.log(" - Test Parallel Granularity:", g_granularity);
|
|
76
|
+
console.log("---------------------------------------------------");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!g_redisAddress || !g_testExecutionId) {
|
|
80
|
+
console.log (g_redisAddress, g_testExecutionId)
|
|
81
|
+
console.error(
|
|
82
|
+
"You need to set at least the following environment variables:\n" +
|
|
83
|
+
" - MOCHA_DISTRIBUTED\n" +
|
|
84
|
+
" - MOCHA_DISTRIBUTED_EXECUTION_ID\n"
|
|
85
|
+
);
|
|
86
|
+
process.exit(-1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
g_redis = redis.createClient({ url: g_redisAddress });
|
|
90
|
+
g_redis.on("error", (err) => {
|
|
91
|
+
console.log("Redis Client Error", err);
|
|
92
|
+
console.log("Closing application!");
|
|
93
|
+
process.exit(-1);
|
|
94
|
+
});
|
|
95
|
+
await g_redis.connect();
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// -----------------------------------------------------------------------------
|
|
99
|
+
// Quit from redis
|
|
100
|
+
// -----------------------------------------------------------------------------
|
|
101
|
+
exports.mochaGlobalTeardown = async function () {
|
|
102
|
+
if (g_redis) {
|
|
103
|
+
await g_redis.quit();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// -----------------------------------------------------------------------------
|
|
108
|
+
// Hook tests
|
|
109
|
+
//
|
|
110
|
+
// Please note that we run skip before each test if the ownership of it has
|
|
111
|
+
// already been defined by another runner.
|
|
112
|
+
// -----------------------------------------------------------------------------
|
|
113
|
+
exports.mochaHooks = {
|
|
114
|
+
beforeEach: async function () {
|
|
115
|
+
const testPath = getTestPath(this.currentTest);
|
|
116
|
+
const testKeyFullPath = `${g_testExecutionId}:${testPath.join(":")}`;
|
|
117
|
+
const testKeySuite = `${g_testExecutionId}:${testPath[0]}`;
|
|
118
|
+
|
|
119
|
+
const testKey =
|
|
120
|
+
g_granularity === GRANULARITY.TEST ? testKeyFullPath : testKeySuite;
|
|
121
|
+
|
|
122
|
+
// Atomically set/get the runner id associated to this test. Only the first
|
|
123
|
+
// runner to get there will set the value to its own runner id.
|
|
124
|
+
const [_, assignedRunnerId] = await g_redis
|
|
125
|
+
.multi()
|
|
126
|
+
.set(testKey, g_runnerId, { EX: g_expirationTime, NX: true })
|
|
127
|
+
.get(testKey)
|
|
128
|
+
.exec();
|
|
129
|
+
|
|
130
|
+
if (assignedRunnerId !== g_runnerId) {
|
|
131
|
+
this.currentTest.title += " (skipped by mocha_distributted)";
|
|
132
|
+
this.skip();
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
afterEach(done) {
|
|
136
|
+
const SKIPPED = "pending";
|
|
137
|
+
const FAILED = "failed";
|
|
138
|
+
|
|
139
|
+
// Save all data in redis in a way it can be retrieved and aggregated
|
|
140
|
+
// easily for all test by an external reporter
|
|
141
|
+
if (this.currentTest.state !== SKIPPED) {
|
|
142
|
+
const testResult = {
|
|
143
|
+
id: getTestPath(this.currentTest),
|
|
144
|
+
type: this.currentTest.type,
|
|
145
|
+
title: this.currentTest.title,
|
|
146
|
+
timedOut: this.currentTest.timedOut,
|
|
147
|
+
duration: this.currentTest.duration,
|
|
148
|
+
startTime: Date.now() - (this.currentTest.duration || 0),
|
|
149
|
+
endTime: Date.now(),
|
|
150
|
+
file: this.currentTest.file,
|
|
151
|
+
state: this.currentTest.state,
|
|
152
|
+
failed: this.currentTest.state === FAILED,
|
|
153
|
+
speed: this.currentTest.speed,
|
|
154
|
+
err: this.currentTest.err || null,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// save results as single line on purpose
|
|
158
|
+
const key = `${g_testExecutionId}:test_result`;
|
|
159
|
+
g_redis.rPush(key, JSON.stringify(testResult));
|
|
160
|
+
g_redis.expire(key, g_expirationTime);
|
|
161
|
+
|
|
162
|
+
// increment passed_count/failed_count & set expiry time
|
|
163
|
+
const countKey = `${g_testExecutionId}:${this.currentTest.state}_count`
|
|
164
|
+
g_redis.incr(countKey);
|
|
165
|
+
g_redis.expire(countKey, g_expirationTime);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
done();
|
|
169
|
+
},
|
|
170
|
+
};
|