• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RobotWebTools / rclnodejs / 15724382650

18 Jun 2025 05:06AM UTC coverage: 84.731% (+0.02%) from 84.711%
15724382650

push

github

minggangw
Fix flakiness of test-message-type.js (#1171)

This PR addresses the flakiness of test-message-type.js by ensuring that subscriptions are properly destroyed after each test execution.

- Added node.destroySubscription(subscription) to each test callback following publisher.kill('SIGINT') to ensure a clean test environment.  
- Improves test reliability by explicitly cleaning up subscriptions in all message type tests.

Fix: #1170

768 of 995 branches covered (77.19%)

Branch coverage included in aggregate %.

1890 of 2142 relevant lines covered (88.24%)

3221.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.01
/lib/context.js
1
// Copyright (c) 2019 Intel Corporation. All rights reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
'use strict';
16

17
const rclnodejs = require('bindings')('rclnodejs');
208✔
18

19
let defaultContext = null;
208✔
20

21
/**
22
 * Encapsulates the lifecycle of an rcl environment from init to shutdown.
23
 * A Context serves as a container for a ROS2 RCL environment that holds
24
 * nodes and the resources created by the nodes, e.g.,
25
 * publishers, subscriptions, actions, services...v
26
 *
27
 * A context has 3 states:
28
 * ```
29
 * new Context() --> uninitialized -->
30
 * *                                  |
31
 *         ---------------------------
32
 *        |
33
 *        v
34
 * rcl.init(context) --> initialized ->
35
 *                                     |
36
 *         ----------------------------
37
 *        |
38
 *        v
39
 * rcl.shutdown(context)
40
 *        or
41
 * context.shutdown() ---> shutdown
42
 * ```
43
 * Must call rclnodejs.init(context) to initialize the context
44
 * to the usable 'initialized' (valid) state be using.
45
 */
46
class Context {
47
  /**
48
   * Access the list of usable (initialized/valid) contexts.
49
   * @returns {Context[]} Array of valid contexts
50
   */
51
  static get instances() {
52
    let contexts = [];
192✔
53
    for (const ctx of Context._instances) {
192✔
54
      if (ctx.isValid()) {
192!
55
        contexts.push(ctx);
192✔
56
      }
57
    }
58
    return contexts;
192✔
59
  }
60

61
  /**
62
   * Create a new instance in uninitialized state.
63
   * Call rcl.init(context) to initialize this context state for
64
   * use in creating nodes, etc.
65
   * @constructor
66
   * @param {bigint} - Optional, The domain ID of this context.
67
   */
68
  constructor(domainId) {
69
    this._handle = rclnodejs.createContext();
1,618✔
70
    this._isShutdown = false;
1,618✔
71
    this._nodes = [];
1,618✔
72
    this._domainId = domainId;
1,618✔
73
    Context._instances.push(this);
1,618✔
74
  }
75

76
  /**
77
   * Access the nodes managed by this context.
78
   * @returns {Node[]} The nodes.
79
   */
80
  get nodes() {
81
    return Array.from(this._nodes);
1,618✔
82
  }
83

84
  /**
85
   * Get the handle referencing the internal context object. Do not modify it yourself: only pass it to *rclnodejs* functions!
86
   * @returns {undefined} a reference to the internal context object
87
   */
88
  get handle() {
89
    return this._handle;
43,620✔
90
  }
91

92
  /**
93
   * Test if this context has not been initialized by rcl.init(context).
94
   * @returns {boolean} True if context has been initialized; otherwise false
95
   */
96
  isUninitialized() {
97
    return !this.isShutdown() && !this.isValid();
1,450✔
98
  }
99

100
  /**
101
   * Test if this context has been initialized, i.e., rcl.init(context),
102
   * and not shutdown.
103
   * @returns {boolean} True if context has been initialized; otherwise false
104
   */
105
  isInitialized() {
106
    return !this.isShutdown() && this.isValid();
1,762✔
107
  }
108

109
  /**
110
   * Test if this context has been shutdown, i.e., context.shutdown().
111
   * @returns {boolean} True if context has been shutdown; otherwise false
112
   */
113
  isShutdown() {
114
    return this._isShutdown;
9,476✔
115
  }
116

117
  /**
118
   * Test if this context is the default one.
119
   * @returns {boolean} whether this is the default context
120
   */
121
  isDefaultContext() {
122
    return this === defaultContext;
1,666✔
123
  }
124

125
  /**
126
   * Check that the context is in a usable state, i.e., it
127
   * has been initialized and not yet shutdown.
128
   * @returns {boolean} whether this context is (still) valid
129
   */
130
  isValid() {
131
    return rclnodejs.isContextValid(this.handle);
3,484✔
132
  }
133

134
  /**
135
   * Check that the context is valid.
136
   * @returns {boolean} whether this context is (still) valid
137
   *
138
   * @deprecated since 0.18.0, Use Context.isValid()
139
   */
140
  get isOk() {
141
    return this.isValid();
112✔
142
  }
143

144
  /**
145
   * Shut down the context including destroying all nodes.
146
   * @returns {undefined}
147
   * @throws {Error} If there is a problem shutting down the context.
148
   */
149
  shutdown() {
150
    if (this.isShutdown()) return;
1,586!
151

152
    // shutdown and remove all nodes
153
    for (const node of this.nodes) {
1,586✔
154
      node.destroy();
814✔
155
    }
156

157
    if (this.isInitialized()) {
1,586✔
158
      rclnodejs.shutdown(this.handle);
1,378✔
159
    }
160

161
    this._isShutdown = true;
1,586✔
162

163
    // remove context from _instances[]
164
    const index = Context._instances.indexOf(this);
1,586✔
165
    if (index > -1) {
1,586!
166
      Context._instances.splice(index, 1);
1,586✔
167
    }
168

169
    if (this.isDefaultContext()) {
1,586✔
170
      defaultContext = null;
1,498✔
171
    }
172
  }
173

174
  /**
175
   * Try to shut down the context.
176
   * @returns {undefined}
177
   * @throws {Error} If there is a problem shutting down the context.
178
   */
179
  tryShutdown() {
180
    if (this.isInitialized()) {
×
181
      this.shutdown();
×
182
    }
183
  }
184

185
  onNodeCreated(node) {
186
    if (!node) {
4,654!
187
      throw new Error('Node must be defined to add to Context');
×
188
    }
189

190
    if (this.isShutdown()) {
4,654!
191
      throw new Error('Can not add a Node to a Context that is shutdown');
×
192
    }
193

194
    if (this._nodes.includes(node)) {
4,654!
195
      // do nothing
196
      return;
×
197
    }
198

199
    this._nodes.push(node);
4,654✔
200
  }
201

202
  onNodeDestroyed(node) {
203
    if (!this._nodes) {
4,830!
204
      return;
×
205
    }
206

207
    // remove node from _nodes[]
208
    const index = this._nodes.indexOf(node);
4,830✔
209
    if (index > -1) {
4,830✔
210
      this._nodes.splice(index, 1);
4,654✔
211
    }
212
  }
213

214
  /**
215
   * Get the global default Context object.
216
   * @returns {Context} The default Context
217
   */
218
  static defaultContext() {
219
    if (defaultContext === null) {
7,602✔
220
      defaultContext = new Context();
1,498✔
221
    }
222
    return defaultContext;
7,602✔
223
  }
224

225
  /**
226
   * Get the domain ID of this context.
227
   * @returns {bigint} domain ID of this context
228
   */
229
  get domainId() {
230
    return rclnodejs.getDomainId(this.handle);
24✔
231
  }
232
}
233

234
Context._instances = [];
208✔
235

236
module.exports = Context;
208✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc