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

nasa / trick / 25870723371

14 May 2026 04:03PM UTC coverage: 56.928% (+0.2%) from 56.774%
25870723371

Pull #2117

github

web-flow
Merge 694a6c711 into af8c162b8
Pull Request #2117: Completed std::wstring support.

24 of 43 new or added lines in 6 files covered. (55.81%)

199 existing lines in 2 files now uncovered.

12974 of 22790 relevant lines covered (56.93%)

298441.05 hits per line

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

45.68
/trick_source/sim_services/MemoryManager/MemoryManager_ref_assignment.cpp
1
#include "trick/MemoryManager.hh"
2
#include "trick/bitfield_proto.h"
3
#include "trick/vval.h"
4
#include "trick/wcs_ext.h"
5
#include <limits.h>
6
#include <sstream>
7
#include <udunits2.h>
8
#include <string.h>
9

10
int Trick::MemoryManager::assign_recursive(void* base_addr, ATTRIBUTES* attr, int curr_dim, int offset, V_TREE* v_tree, cv_converter* cf) {
21,095✔
11

12
   char* assign_addr;
13
   int remaining_dimensions = attr->num_index - curr_dim;
21,095✔
14
   // local_type is set to the type of the attribute, but if it's a STL type, we need to use the element type.
15
   TRICK_TYPE local_type = attr->type;
21,095✔
16
   if (local_type == TRICK_STL) {
21,095✔
17
      local_type = attr->stl_elem_type;
×
18
   }
19

20
   if ( remaining_dimensions == 0 ) {
21,095✔
21

22
      switch (local_type) {
18,666✔
23

24
           case TRICK_CHARACTER :
725✔
25
           case TRICK_UNSIGNED_CHARACTER :
26
               assign_addr = (char*)base_addr + offset * sizeof(char);
725✔
27
               if (v_tree && v_tree->v_data) {
725✔
28
                   *(char*)assign_addr = vval_char(v_tree->v_data);
725✔
29
               } else {
30
                   *(char*)assign_addr = '\0';
×
31
               }
32
               if (debug_level) {
725✔
33
                   std::cout << std::endl << "Assignment: *(char*)" << (void*)assign_addr
×
34
                             << " = ";
×
35
                   if (isprint(*(char*)assign_addr)) {
×
36
                       std::cout << *(char*)assign_addr;
×
37
                   } else {
38
                       std::cout << (int)*(char*)assign_addr;
×
39
                   }
40
                   std::cout << ";" << std::endl;
×
41
                   std::cout.flush();
×
42
               }
43
               break;
18,666✔
44
           case TRICK_WCHAR :
×
45
               assign_addr = (char*)base_addr + offset * sizeof(wchar_t);
×
46
               if (v_tree && v_tree->v_data) {
×
47
                   *(wchar_t*)assign_addr = vval_char(v_tree->v_data);
×
48
               } else {
49
                   *(wchar_t*)assign_addr = 0;
×
50
               }
51
               if (debug_level) {
×
52
                   char buff[16] = {0};
×
53
                   wctomb(buff,*(wchar_t*)assign_addr);
×
54
                   std::cout << std::endl << "Assignment: *(wchar_t*)" << (void*)assign_addr
×
55
                             << " = " << buff << ";" << std::endl;
×
56
                   std::cout.flush();
×
57
               }
58
               break;
×
59
           case TRICK_SHORT :
1,336✔
60
           case TRICK_UNSIGNED_SHORT :
61
               assign_addr = (char*)base_addr + offset * sizeof(short);
1,336✔
62
               if (v_tree && v_tree->v_data) {
1,336✔
63
                   *(short *)assign_addr = vval_short(v_tree->v_data);
1,336✔
64
               } else {
65
                   *(short *)assign_addr = 0;
×
66
               }
67
               if (debug_level) {
1,336✔
68
                   std::cout << std::endl << "Assignment: *(short*)" << (void*)assign_addr
×
69
                             << " = " << *(short*)assign_addr << ";" << std::endl;
×
70
                   std::cout.flush();
×
71
               }
72
               break;
1,336✔
73
           case TRICK_INTEGER :
5,156✔
74
           case TRICK_UNSIGNED_INTEGER :
75
               assign_addr = (char*)base_addr + offset * sizeof(int);
5,156✔
76
               if (v_tree && v_tree->v_data) {
5,156✔
77
                   int input_value;
78
                   input_value = vval_int(v_tree->v_data);
5,150✔
79
                   if (cf == NULL) {
5,150✔
80
                       *(int *)assign_addr = input_value;
5,147✔
81
                   } else {
82
                       *(int *)assign_addr = (int)cv_convert_double(cf, (double)input_value) ;
3✔
83
                   }
5,150✔
84
               } else {
85
                   *(int *)assign_addr = 0;
6✔
86
               }
87
               if (debug_level) {
5,156✔
88
                   std::cout << std::endl << "Assignment: *(int*)" << (void*)assign_addr
×
89
                             << " = " << *(int*)assign_addr << ";" << std::endl;
×
90
                   std::cout.flush();
×
91
               }
92
               break;
5,156✔
93
           case TRICK_BOOLEAN :
1,768✔
94
               assign_addr = (char*)base_addr + offset * sizeof(bool);
1,768✔
95
               if (v_tree && v_tree->v_data) {
1,768✔
96
                   *(bool *)assign_addr = (vval_short(v_tree->v_data)!=0);
1,768✔
97
               } else {
98
                   *(bool *)assign_addr = false;
×
99
               }
100
               if (debug_level) {
1,768✔
101
                   std::cout << std::endl << "Assignment: *(bool*)" << (void*)assign_addr
×
102
                             << " = " << *(bool*)assign_addr << ";" << std::endl;
×
103
                   std::cout.flush();
×
104
               }
105
               break;
1,768✔
106
           case TRICK_ENUMERATED :
36✔
107
               if (v_tree && v_tree->v_data) {
36✔
108
                   if ((size_t)attr->size == sizeof(int)) {
36✔
109
                       assign_addr = (char*)base_addr + offset * sizeof(int);
36✔
110
                       *(int *)assign_addr = vval_int(v_tree->v_data);
36✔
111
                       if (debug_level) {
36✔
112
                           std::cout << std::endl << "Assignment (Enum): *(int*)" << (void*)assign_addr
×
113
                                     << " = " << *(int*)assign_addr << ";" << std::endl;
×
114
                           std::cout.flush();
×
115
                       }
116
                   } else if ((size_t)attr->size == sizeof(short)) {
×
117
                       assign_addr = (char*)base_addr + offset * sizeof(short);
×
118
                       *(short *)assign_addr = vval_short(v_tree->v_data);
×
119
                       if (debug_level) {
×
120
                           std::cout << std::endl << "Assignment (Enum): *(short*)" << (void*)assign_addr
×
121
                                     << " = " << *(short*)assign_addr << ";" << std::endl;
×
122
                           std::cout.flush();
×
123
                       }
124
                   } else if ((size_t)attr->size == sizeof(char)) {
×
125
                       assign_addr = (char*)base_addr + offset * sizeof(char);
×
126
                       *(char *)assign_addr = vval_char(v_tree->v_data);
×
127
                       if (debug_level) {
×
128
                           std::cout << std::endl << "Assignment (Enum): *(char*)" << (void*)assign_addr
×
129
                                     << " = " << *(char*)assign_addr << ";" << std::endl;
×
130
                           std::cout.flush();
×
131
                       }
132
                   } else {
133
                       std::stringstream message;
×
134
                       message << "Enumeration of size " << attr->size << " is not supported.";
×
135
                       emitError(message.str());
×
136
                   }
36✔
137
               } else {
138
                   emitError("v_tree data appears to be corrupted.");
×
139
               }
140
               break;
36✔
141
           case TRICK_LONG :
102✔
142
           case TRICK_UNSIGNED_LONG :
143
               assign_addr = (char*)base_addr + offset * sizeof(long);
102✔
144
               if (v_tree && v_tree->v_data) {
102✔
145
                   long input_value;
146
                   input_value = vval_long(v_tree->v_data);
102✔
147
                   if (cf == NULL) {
102✔
148
                       *(long *)assign_addr = input_value;
100✔
149
                   } else {
150
                       *(long *)assign_addr = (long)cv_convert_double(cf, (double)input_value) ;
2✔
151
                   }
102✔
152
               } else {
153
                   *(long *)assign_addr = 0;
×
154
               }
155
               if (debug_level) {
102✔
156
                   std::cout << std::endl << "Assignment: *(long*)" << (void*)assign_addr
×
157
                             << " = " << *(long*)assign_addr << ";" << std::endl;
×
158
                   std::cout.flush();
×
159
               }
160
               break;
102✔
161
           case TRICK_FLOAT :
36✔
162
               assign_addr = (char*)base_addr + offset * sizeof(float);
36✔
163
               if (v_tree && v_tree->v_data) {
36✔
164
                   float input_value;
165
                   input_value = vval_float(v_tree->v_data);
36✔
166
                   if (cf == NULL) {  // There is no units conversion.
36✔
167
                       *(float *)assign_addr = input_value;
35✔
168
                   } else { // There is units conversion.
169
                       *(float *)assign_addr = (float)cv_convert_double(cf, (double)input_value) ;
1✔
170
                   }
36✔
171
               } else {
172
                   *(float *)assign_addr = 0;
×
173
               }
174
               if (debug_level) {
36✔
175
                   std::cout << std::endl << "Assignment: *(float*)" << (void*)assign_addr
×
176
                             << " = " << *(float*)assign_addr << ";" << std::endl;
×
177
                   std::cout.flush();
×
178
               }
179
               break;
36✔
180
           case TRICK_DOUBLE :
3,613✔
181
               assign_addr = (char*)base_addr + offset * sizeof(double);
3,613✔
182
               if (v_tree && v_tree->v_data) {
3,613✔
183
                   double input_value;
184
                   input_value = vval_double(v_tree->v_data);
3,613✔
185
                   if (cf == NULL) {
3,613✔
186
                       *(double *)assign_addr = input_value;
3,607✔
187
                   } else {
188
                       *(double *)assign_addr = cv_convert_double(cf, input_value) ;
6✔
189
                   }
3,613✔
190
               } else {
191
                   *(double *)assign_addr = 0;
×
192
               }
193
               if (debug_level) {
3,613✔
194
                   std::cout << std::endl << "Assignment: *(double*)" << (void*)assign_addr
×
195
                             << " = " << *(double*)assign_addr << ";" << std::endl;
×
196
                   std::cout.flush();
×
197
               }
198
               break;
3,613✔
199
           case TRICK_LONG_LONG :
641✔
200
           case TRICK_UNSIGNED_LONG_LONG :
201
               assign_addr = (char*)base_addr + offset * sizeof(long long);
641✔
202
               if (v_tree && v_tree->v_data) {
641✔
203
                   *(long long *)assign_addr = vval_longlong(v_tree->v_data);
641✔
204
               } else {
205
                   *(long long *)assign_addr = 0;
×
206
               }
207
               if (debug_level) {
641✔
208
                   std::cout << std::endl << "Assignment: *(long long*)" << (void*)assign_addr
×
209
                             << " = " << *(long long*)assign_addr << ";" << std::endl;
×
210
                   std::cout.flush();
×
211
               }
212
               break;
641✔
213
           case TRICK_BITFIELD :
×
214
           case TRICK_UNSIGNED_BITFIELD : {
215
                   int input_value;
216
                   assign_addr = (char*)base_addr + offset * (size_t)attr->size;
×
217
                   if (v_tree && v_tree->v_data) {
×
218
                       input_value = vval_int(v_tree->v_data);
×
219
                   } else {
220
                       input_value = 0;
×
221
                   }
222
                   if (attr->size == sizeof(int)) {
×
223
                       *(unsigned int*)assign_addr = insert_bitfield_any(
×
224
                           *(unsigned int*)assign_addr, input_value, attr->size, attr->index[0].start, attr->index[0].size);
225
                   } else if (attr->size == sizeof(short)) {
×
226
                       *(unsigned short*)assign_addr = insert_bitfield_any(
×
227
                           *(unsigned short*)assign_addr, input_value, attr->size, attr->index[0].start, attr->index[0].size);
×
228
                   } else if (attr->size == sizeof(char)) {
×
229
                       *(unsigned char*)assign_addr = insert_bitfield_any(
×
230
                           *(unsigned char*)assign_addr, input_value, attr->size, attr->index[0].start, attr->index[0].size);
×
231
                   } else {
232
                       std::stringstream message;
×
233
                       message << "Unhandled bitfield struct size (" << attr->size << ") in bitfield assignment.";
×
234
                       emitError(message.str());
×
235
                   }
236
                   if (debug_level) {
×
237
                       std::cout << std::endl << "Assignment: "
×
238
                                 << "Within the " << attr->size << " byte struct at " << (void*)assign_addr
×
239
                                 << ", the bitfield ["<< attr->index[0].start << ".."
×
240
                                 << attr->index[0].start + attr->index[0].size - 1
×
241
                                 << "] = " << input_value << ";"
×
242
                                 << std::endl;
×
243
                       std::cout.flush();
×
244
                   }
245
               } break;
×
246
           case TRICK_FILE_PTR :
×
247
               if (debug_level) {
×
248
                   std::cout << std::endl << "Assignment: TRICK_FILE_PTR assignments not yet implemented."
×
249
                             << std::endl;
×
250
                   std::cout.flush();
×
251
               }
252
               break;
×
253
           case TRICK_VOID_PTR :
×
254
               if (debug_level) {
×
255
                   std::cout << std::endl << "Assignment: TRICK_VOID_PTR assignments not yet implemented."
×
256
                             << std::endl;
×
257
                   std::cout.flush();
×
258
               }
259
               break;
×
260
           case TRICK_STRING :
5,248✔
261
               assign_addr = (char*)base_addr + offset * sizeof(std::string);
5,248✔
262
               if (v_tree && v_tree->v_data) {
5,248✔
263
                   *(std::string*)assign_addr = vval_string(v_tree->v_data);
5,248✔
264
               } else {
265
                   *(std::string*)assign_addr = "";
×
266
               }
267
               if (debug_level) {
5,248✔
268
                   std::cout << std::endl << "Assignment: *(std::string)" << (void*)assign_addr
×
269
                             << " = \"" << *(std::string*)assign_addr << "\";" << std::endl;
×
270
                   std::cout.flush();
×
271
               }
272
               break;
5,248✔
273
           case TRICK_WSTRING :
5✔
274
               assign_addr = (char*)base_addr + offset * sizeof(std::wstring);
5✔
275
               if (v_tree && v_tree->v_data) {
5✔
276
                   const char* cp = vval_string(v_tree->v_data);
5✔
277
                   if (cp != NULL) {
5✔
278
                       int wcs_len = (int)ncs_to_wcs_len(cp) + 1;
5✔
279
                       std::wstring wide_val(wcs_len, L'\0');
10✔
280
                       ncs_to_wcs(cp, &wide_val[0], wcs_len);
5✔
281
                       wide_val.resize(wcs_len - 1);
5✔
282
                       *(std::wstring*)assign_addr = wide_val;
5✔
283
                   } else {
NEW
284
                       *(std::wstring*)assign_addr = L"";
×
285
                   }
5✔
286
               } else {
NEW
287
                   *(std::wstring*)assign_addr = L"";
×
288
               }
289
               break;
5✔
290
           default:
×
291
               std::stringstream message;
×
292
               message << "Unhandled Type (" << local_type << ") in assignment.";
×
293
               emitError(message.str());
×
294
               return (1);
×
295
               break;
296
      }
297

298
   } else if ( remaining_dimensions > 0 ) {
2,429✔
299

300
       int size_of_curr_dim = attr->index[curr_dim].size ;
2,429✔
301

302
       if ( size_of_curr_dim == 0) { // the remaining dimensions are pointer dimensions.
2,429✔
303

304
           assign_addr = (char*)base_addr + offset * sizeof(void*);
662✔
305

306
           if (v_tree && v_tree->v_data) {
662✔
307

308
               if ((remaining_dimensions == 1) && (v_tree->v_data->type == TRICK_STRING)) {
1,324✔
309
                   *(char**)assign_addr = mm_strdup( vval_string(v_tree->v_data));
67✔
310
               } else {
311
                   *(void**)assign_addr = vval_voidp(v_tree->v_data);
595✔
312
               }
313

314
           } else {
315
               *(void**)assign_addr = 0;
×
316
           }
317

318
           if (debug_level) {
662✔
319
               std::cout << std::endl << "Assignment: *(void**)" << (void*)assign_addr
×
320
                         << " = " << *(void**)assign_addr << ";" << std::endl;
×
321
               std::cout.flush();
×
322
           }
323

324
       } else { // next dimension is fixed.
325

326
           if ((local_type == TRICK_CHARACTER) &&
1,767✔
327
               (remaining_dimensions == 1) &&
43✔
328
               (v_tree) &&
43✔
329
               (v_tree->v_data)
43✔
330
              ) {
331

332
               assign_addr = (char*)base_addr + offset * size_of_curr_dim * sizeof(char);
32✔
333

334
               if ((v_tree->v_data->type == TRICK_STRING) &&
32✔
335
                   (v_tree->v_data->value.cp != NULL)) {
32✔
336

337
                   int rhs_len = (int)strlen(v_tree->v_data->value.cp) + 1;
32✔
338
                   if (rhs_len <= size_of_curr_dim ) {
32✔
339
                       strcpy((char*)assign_addr, v_tree->v_data->value.cp);
32✔
340
                   } else {
341
                       emitError("Memory Manager: char array is too small for the attempted string assignment.");
×
342
                       return (1);
×
343
                   }
32✔
344
               } else {
345
                   *(char*)assign_addr = '\0';
×
346
               }
32✔
347

348
           } else if ( (local_type == TRICK_WCHAR) &&
1,735✔
349
                       (remaining_dimensions == 1)) {
350

351
               assign_addr = (char*)base_addr + offset * size_of_curr_dim * sizeof(wchar_t);
×
352

353
               if ((v_tree) &&
×
354
                   (v_tree->v_data->type == TRICK_WSTRING) &&
×
355
                   (v_tree->v_data->value.wcp != NULL)) {
×
356

357
                       int rhs_len = (int)wcslen(v_tree->v_data->value.wcp) + 1;
×
358
                       if (rhs_len <= size_of_curr_dim ) {
×
359
                           wcscpy((wchar_t*)assign_addr, v_tree->v_data->value.wcp);
×
360
                       } else {
361
                           std::stringstream message;
×
362
                           message << "wchar_t array at [" << (void*)assign_addr
×
363
                                   << "] is to small for the attempted string assignment." ;
×
364
                           emitError(message.str());
×
365
                           return (1);
×
366
                       }
×
367
               } else if ((v_tree) &&
×
368
                          (v_tree->v_data->type == TRICK_STRING) &&
×
369
                          (v_tree->v_data->value.cp != NULL)) {
×
370

371
                       int rhs_len = (int)ncs_to_wcs_len(v_tree->v_data->value.cp) + 1;
×
372
                       if (rhs_len <= size_of_curr_dim ) {
×
373
                           ncs_to_wcs( v_tree->v_data->value.cp, (wchar_t*)assign_addr, rhs_len);
×
374
                       } else {
375
                           std::stringstream message;
×
376
                           message << "wchar_t array at [" << (void*)assign_addr
×
377
                                   << "] is too small for the attempted string assignment." ;
×
378
                           emitError(message.str());
×
379
                           return (1);
×
380
                       }
×
381
               } else {
382
                   *(wchar_t*)assign_addr = (wchar_t) NULL;
×
383
               }
×
384

385
           } else {
386
               int ii;
387
               V_TREE* curr_vt_node;
388
               if (v_tree) {
1,735✔
389
                   curr_vt_node = v_tree->down;
1,735✔
390
               } else {
391
                   curr_vt_node =  NULL;
×
392
               }
393

394
               for (ii=0; ii < size_of_curr_dim; ii++) {
5,006✔
395
                   int ret;
396
                   ret = assign_recursive (base_addr,
6,542✔
397
                                           attr,
398
                                           curr_dim+1,
399
                                           offset * size_of_curr_dim + ii,
3,271✔
400
                                           curr_vt_node,
401
                                           cf);
402

403
                   if (ret !=0) {
3,271✔
404
                       return(1);
×
405
                   }
406

407
                   if (curr_vt_node) {
3,271✔
408
                     curr_vt_node = curr_vt_node->next;
3,265✔
409
                   }
410
               }
411
           }
412
       }
413
   } else {
414
        emitError("This is bad. In assign_recursive(), remaining_dimensions is negative.");
×
415
        return (1);
×
416
   }
417
   return (0);
21,095✔
418
}
419

420
#ifdef TRICK_VER
421
#include "trick/UdUnits.hh"
422
#else
423
static ut_system * u_system ;
424
#endif
425
ut_system * Trick::MemoryManager::get_unit_system() {
38✔
426
#ifdef TRICK_VER
427
    return Trick::UdUnits::get_u_system() ;
38✔
428
#else
429
    /* Initialize the udunits-2 library */
430
    ut_set_error_message_handler(ut_ignore) ;
431
    if( (u_system = ut_read_xml( NULL )) == NULL ) {
432
        std::cerr << "Error initializing udunits-2 unit system" << std::endl ;
433
        return -1 ;
434
    }
435
    ut_set_error_message_handler(ut_write_to_stderr) ;
436
    return u_system ;
437
#endif
438
}
439

440
// MEMBER FUNCTION
441
int Trick::MemoryManager::ref_assignment( REF2* R, V_TREE* V) {
17,825✔
442

443
    int ret = 0;
17,825✔
444
    cv_converter * cf = NULL ;
17,825✔
445

446
    // Create a units conversion function if necessary.
447
    if (R->units) {
17,825✔
448
        ut_unit * from = ut_parse(get_unit_system(), R->units, UT_ASCII) ;
19✔
449
        ut_unit * to = ut_parse(get_unit_system(), R->attr->units, UT_ASCII) ;
19✔
450
        if ( !from or !to ) {
19✔
451
            std::stringstream message;
×
452
            message << "Can't convert \"" << R->units << "\" to \"" << R->attr->units << "\".";
×
453
            emitError(message.str());
×
454
            return TRICK_UNITS_CONVERSION_ERROR ;
×
455
        }
456
        cf = ut_get_converter(from,to) ;
19✔
457
        if ( !cf ) {
19✔
458
            std::stringstream message;
1✔
459
            message << "Can't convert \"" << R->units << "\" to \"" << R->attr->units << "\".";
1✔
460
            emitError(message.str());
1✔
461
            return TRICK_UNITS_CONVERSION_ERROR ;
1✔
462
        }
463
    }
464

465
    // Special handling for vector<bool> assignment
466
    // vector<bool> elements can't be written via get_stl_element (returns temp buffer)
467
    // Instead, use set_stl_element to write directly to the container
468
    // Context (container address + index) is stored in R->ref_attr by ref_dim
469
    if (R->ref_attr != NULL &&
17,824✔
470
        R->ref_attr->type == TRICK_STL &&
1,905✔
471
        R->ref_attr->stl_elem_type == TRICK_BOOLEAN && V && V->v_data)
×
472
    {
473

474
        // Extract stored context from ref_attr
475
        void *container_address = R->ref_attr->attr;                  // Container address
×
476
        size_t element_index = R->ref_attr->offset;                   // Element index
×
477
        ATTRIBUTES *container_attr = (ATTRIBUTES *)R->ref_attr->name; // Original container attr
×
478

479
        // Extract the bool value from V_TREE
480
        bool value = (vval_short(V->v_data) != 0);
×
481

482
        // Call the setter function to write directly to the vector
483
        if (container_attr && container_attr->set_stl_element)
×
484
        {
485
            container_attr->set_stl_element(container_address, element_index, &value);
×
486

487
            if (debug_level)
×
488
            {
489
                std::cout << std::endl
×
490
                          << "Assignment (vector<bool>): " << R->reference << " = " << value << ";" << std::endl;
×
491
                std::cout.flush();
×
492
            }
493

494
            ret = TRICK_NO_ERROR;
×
495
        }
496
        else
497
        {
498
            emitError("set_stl_element function not available for vector<bool>");
×
499
            ret = TRICK_PARAMETER_ADDRESS_NULL;
×
500
        }
501

502
        if (cf)
×
503
            cv_free(cf);
×
504
        return ret;
×
505
    } // End of special vector<bool> handling
506

507
    // R->num_index is badly named. It is really the current dimension
508
    ret = assign_recursive( R->address, R->attr, R->num_index, 0, V, cf);
17,824✔
509
    if ( cf ) cv_free(cf) ;
17,824✔
510

511
    return ( ret);
17,824✔
512

513
}
514

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