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

taosdata / TDengine / #4818

20 Oct 2025 02:09AM UTC coverage: 61.04% (-0.1%) from 61.141%
#4818

push

travis-ci

web-flow
Merge e6f7b1ad7 into 7e74ade39

155148 of 324487 branches covered (47.81%)

Branch coverage included in aggregate %.

152 of 185 new or added lines in 22 files covered. (82.16%)

4700 existing lines in 117 files now uncovered.

207487 of 269610 relevant lines covered (76.96%)

127870039.14 hits per line

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

75.0
/source/common/src/ttypes.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15

16
#define _DEFAULT_SOURCE
17
#include "ttypes.h"
18
#include "tcompression.h"
19

20
const int32_t TYPE_BYTES[TSDB_DATA_TYPE_MAX] = {
21
    2,                        // TSDB_DATA_TYPE_NULL
22
    CHAR_BYTES,               // TSDB_DATA_TYPE_BOOL
23
    CHAR_BYTES,               // TSDB_DATA_TYPE_TINYINT
24
    SHORT_BYTES,              // TSDB_DATA_TYPE_SMALLINT
25
    INT_BYTES,                // TSDB_DATA_TYPE_INT
26
    sizeof(int64_t),          // TSDB_DATA_TYPE_BIGINT
27
    FLOAT_BYTES,              // TSDB_DATA_TYPE_FLOAT
28
    DOUBLE_BYTES,             // TSDB_DATA_TYPE_DOUBLE
29
    sizeof(VarDataOffsetT),   // TSDB_DATA_TYPE_BINARY
30
    sizeof(TSKEY),            // TSDB_DATA_TYPE_TIMESTAMP
31
    sizeof(VarDataOffsetT),   // TSDB_DATA_TYPE_NCHAR
32
    CHAR_BYTES,               // TSDB_DATA_TYPE_UTINYINT
33
    SHORT_BYTES,              // TSDB_DATA_TYPE_USMALLINT
34
    INT_BYTES,                // TSDB_DATA_TYPE_UINT
35
    sizeof(uint64_t),         // TSDB_DATA_TYPE_UBIGINT
36
    TSDB_MAX_JSON_TAG_LEN,    // TSDB_DATA_TYPE_JSON
37
    sizeof(VarDataOffsetT),   // TSDB_DATA_TYPE_VARBINARY
38
    DECIMAL128_BYTES,         // TSDB_DATA_TYPE_DECIMAL: placeholder, not implemented
39
    sizeof(BlobDataOffsetT),  // TSDB_DATA_TYPE_BLOB: placeholder, not implemented
40
    sizeof(BlobDataOffsetT),  // TSDB_DATA_TYPE_MEDIUMBLOB: placeholder, not implemented
41
    sizeof(VarDataOffsetT),   // TSDB_DATA_TYPE_GEOMETRY
42
    DECIMAL64_BYTES,          // TSDB_DATA_TYPE_DECIMAL64
43
};
44

45
tDataTypeDescriptor tDataTypes[TSDB_DATA_TYPE_MAX] = {
46
    {TSDB_DATA_TYPE_NULL, 6, 2, "NOTYPE", 0, 0, NULL, NULL},
47
    {TSDB_DATA_TYPE_BOOL, 4, CHAR_BYTES, "BOOL", false, true, tsCompressBool, tsDecompressBool},
48
    {TSDB_DATA_TYPE_TINYINT, 7, CHAR_BYTES, "TINYINT", INT8_MIN, INT8_MAX, tsCompressTinyint, tsDecompressTinyint},
49
    {TSDB_DATA_TYPE_SMALLINT, 8, SHORT_BYTES, "SMALLINT", INT16_MIN, INT16_MAX, tsCompressSmallint,
50
     tsDecompressSmallint},
51
    {TSDB_DATA_TYPE_INT, 3, INT_BYTES, "INT", INT32_MIN, INT32_MAX, tsCompressInt, tsDecompressInt},
52
    {TSDB_DATA_TYPE_BIGINT, 6, LONG_BYTES, "BIGINT", INT64_MIN, INT64_MAX, tsCompressBigint, tsDecompressBigint},
53
    {TSDB_DATA_TYPE_FLOAT, 5, FLOAT_BYTES, "FLOAT", 0, 0, tsCompressFloat, tsDecompressFloat},
54
    {TSDB_DATA_TYPE_DOUBLE, 6, DOUBLE_BYTES, "DOUBLE", 0, 0, tsCompressDouble, tsDecompressDouble},
55
    {TSDB_DATA_TYPE_VARCHAR, 6, 1, "VARCHAR", 0, 0, tsCompressString, tsDecompressString},
56
    {TSDB_DATA_TYPE_TIMESTAMP, 9, LONG_BYTES, "TIMESTAMP", INT64_MIN, INT64_MAX, tsCompressTimestamp,
57
     tsDecompressTimestamp},
58
    {TSDB_DATA_TYPE_NCHAR, 5, 1, "NCHAR", 0, 0, tsCompressString, tsDecompressString},
59
    {TSDB_DATA_TYPE_UTINYINT, 16, CHAR_BYTES, "TINYINT UNSIGNED", 0, UINT8_MAX, tsCompressTinyint, tsDecompressTinyint},
60
    {TSDB_DATA_TYPE_USMALLINT, 17, SHORT_BYTES, "SMALLINT UNSIGNED", 0, UINT16_MAX, tsCompressSmallint,
61
     tsDecompressSmallint},
62
    {TSDB_DATA_TYPE_UINT, 12, INT_BYTES, "INT UNSIGNED", 0, UINT32_MAX, tsCompressInt, tsDecompressInt},
63
    {TSDB_DATA_TYPE_UBIGINT, 15, LONG_BYTES, "BIGINT UNSIGNED", 0, UINT64_MAX, tsCompressBigint, tsDecompressBigint},
64
    {TSDB_DATA_TYPE_JSON, 4, TSDB_MAX_JSON_TAG_LEN, "JSON", 0, 0, tsCompressString, tsDecompressString},
65
    {TSDB_DATA_TYPE_VARBINARY, 9, 1, "VARBINARY", 0, 0, tsCompressString,
66
     tsDecompressString},                                                             // placeholder, not implemented
67
    {TSDB_DATA_TYPE_DECIMAL, 7, DECIMAL128_BYTES, "DECIMAL", 0, 0, NULL, NULL},       // placeholder, not implemented
68
    {TSDB_DATA_TYPE_BLOB, 4, 1, "BLOB", 0, 0, tsCompressString, tsDecompressString},  // placeholder, not implemented
69
    {TSDB_DATA_TYPE_MEDIUMBLOB, 10, 1, "MEDIUMBLOB", 0, 0, tsCompressString,
70
     tsDecompressString},  // placeholder, not implemented
71
    {TSDB_DATA_TYPE_GEOMETRY, 8, 1, "GEOMETRY", 0, 0, tsCompressString, tsDecompressString},
72
    {TSDB_DATA_TYPE_DECIMAL64, 7, DECIMAL64_BYTES, "DECIMAL", 0, 0, NULL, NULL},
73
};
74

75
tDataTypeCompress tDataCompress[TSDB_DATA_TYPE_MAX] = {
76
    {TSDB_DATA_TYPE_NULL, 6, 1, "NOTYPE", 0, 0, NULL, NULL},
77
    {TSDB_DATA_TYPE_BOOL, 4, CHAR_BYTES, "BOOL", false, true, tsCompressBool2, tsDecompressBool2},
78
    {TSDB_DATA_TYPE_TINYINT, 7, CHAR_BYTES, "TINYINT", INT8_MIN, INT8_MAX, tsCompressTinyint2, tsDecompressTinyint2},
79
    {TSDB_DATA_TYPE_SMALLINT, 8, SHORT_BYTES, "SMALLINT", INT16_MIN, INT16_MAX, tsCompressSmallint2,
80
     tsDecompressSmallint2},
81
    {TSDB_DATA_TYPE_INT, 3, INT_BYTES, "INT", INT32_MIN, INT32_MAX, tsCompressInt2, tsDecompressInt2},
82
    {TSDB_DATA_TYPE_BIGINT, 6, LONG_BYTES, "BIGINT", INT64_MIN, INT64_MAX, tsCompressBigint2, tsDecompressBigint2},
83
    {TSDB_DATA_TYPE_FLOAT, 5, FLOAT_BYTES, "FLOAT", 0, 0, tsCompressFloat2, tsDecompressFloat2},
84
    {TSDB_DATA_TYPE_DOUBLE, 6, DOUBLE_BYTES, "DOUBLE", 0, 0, tsCompressDouble2, tsDecompressDouble2},
85
    {TSDB_DATA_TYPE_VARCHAR, 6, 1, "VARCHAR", 0, 0, tsCompressString2, tsDecompressString2},
86
    {TSDB_DATA_TYPE_TIMESTAMP, 9, LONG_BYTES, "TIMESTAMP", INT64_MIN, INT64_MAX, tsCompressTimestamp2,
87
     tsDecompressTimestamp2},
88
    {TSDB_DATA_TYPE_NCHAR, 5, 1, "NCHAR", 0, 0, tsCompressString2, tsDecompressString2},
89
    {TSDB_DATA_TYPE_UTINYINT, 16, CHAR_BYTES, "TINYINT UNSIGNED", 0, UINT8_MAX, tsCompressTinyint2,
90
     tsDecompressTinyint2},
91
    {TSDB_DATA_TYPE_USMALLINT, 17, SHORT_BYTES, "SMALLINT UNSIGNED", 0, UINT16_MAX, tsCompressSmallint2,
92
     tsDecompressSmallint2},
93
    {TSDB_DATA_TYPE_UINT, 12, INT_BYTES, "INT UNSIGNED", 0, UINT32_MAX, tsCompressInt2, tsDecompressInt2},
94
    {TSDB_DATA_TYPE_UBIGINT, 15, LONG_BYTES, "BIGINT UNSIGNED", 0, UINT64_MAX, tsCompressBigint2, tsDecompressBigint2},
95
    {TSDB_DATA_TYPE_JSON, 4, TSDB_MAX_JSON_TAG_LEN, "JSON", 0, 0, tsCompressString2, tsDecompressString2},
96
    {TSDB_DATA_TYPE_VARBINARY, 9, 1, "VARBINARY", 0, 0, tsCompressString2,
97
     tsDecompressString2},  // placeholder, not implemented
98
    {TSDB_DATA_TYPE_DECIMAL, 7, DECIMAL128_BYTES, "DECIMAL", 0, 0, tsCompressDecimal128, tsDecompressDecimal128},
99
    {TSDB_DATA_TYPE_BLOB, 4, 1, "BLOB", 0, 0, tsCompressString2, tsDecompressString2},  // placeholder, not implemented
100
    {TSDB_DATA_TYPE_MEDIUMBLOB, 10, 1, "MEDIUMBLOB", 0, 0, tsCompressString2,
101
     tsDecompressString2},  // placeholder, not implemented
102
    {TSDB_DATA_TYPE_GEOMETRY, 8, 1, "GEOMETRY", 0, 0, tsCompressString2, tsDecompressString2},
103
    {TSDB_DATA_TYPE_DECIMAL64, 9, DECIMAL64_BYTES, "DECIMAL64", 0, 0, tsCompressDecimal64, tsDecompressDecimal64},
104

105
};
106

107
static float  floatMin = -FLT_MAX, floatMax = FLT_MAX;
108
static double doubleMin = -DBL_MAX, doubleMax = DBL_MAX;
109

110
FORCE_INLINE void *getDataMin(int32_t type, void *value) {
29,723,236✔
111
  switch (type) {
29,723,236✔
112
    case TSDB_DATA_TYPE_FLOAT:
103,467✔
113
      *(float *)value = floatMin;
103,467✔
114
      break;
103,467✔
115
    case TSDB_DATA_TYPE_DOUBLE:
2,110,280✔
116
      *(double *)value = doubleMin;
2,110,280✔
117
      break;
2,112,208✔
118
    default:
27,509,489✔
119
      *(int64_t *)value = tDataTypes[type].minValue;
27,509,489✔
120
      break;
27,499,737✔
121
  }
122

123
  return value;
29,715,412✔
124
}
125

126
FORCE_INLINE void *getDataMax(int32_t type, void *value) {
34,139,984✔
127
  switch (type) {
34,139,984✔
128
    case TSDB_DATA_TYPE_FLOAT:
272,633✔
129
      *(float *)value = floatMax;
272,633✔
130
      break;
272,201✔
131
    case TSDB_DATA_TYPE_DOUBLE:
2,183,218✔
132
      *(double *)value = doubleMax;
2,183,218✔
133
      break;
2,183,598✔
134
    default:
31,684,133✔
135
      *(int64_t *)value = tDataTypes[type].maxValue;
31,684,133✔
136
      break;
31,681,845✔
137
  }
138

139
  return value;
34,137,644✔
140
}
141

142
bool isValidDataType(int32_t type) { return type >= TSDB_DATA_TYPE_NULL && type < TSDB_DATA_TYPE_MAX; }
601,881,595!
143

144
#define POINTER_SHIFT(p, b) ((void *)((char *)(p) + (b)))
145

146
void assignVal(char *val, const char *src, int32_t len, int32_t type) {
63,146,202✔
147
  switch (type) {
63,146,202✔
148
    case TSDB_DATA_TYPE_BOOL:
3,720,778✔
149
    case TSDB_DATA_TYPE_TINYINT:
150
    case TSDB_DATA_TYPE_UTINYINT:
151
      *((int8_t *)val) = GET_INT8_VAL(src);
3,720,778✔
152
      break;
3,721,363✔
153
    case TSDB_DATA_TYPE_SMALLINT:
2,309,094✔
154
    case TSDB_DATA_TYPE_USMALLINT:
155
      *((int16_t *)val) = GET_INT16_VAL(src);
2,309,094✔
156
      break;
2,309,100✔
157
    case TSDB_DATA_TYPE_INT:
10,079,900✔
158
    case TSDB_DATA_TYPE_UINT:
159
      *((int32_t *)val) = GET_INT32_VAL(src);
10,079,900✔
160
      break;
10,081,796✔
161

162
    case TSDB_DATA_TYPE_FLOAT:
23,431✔
163
      SET_FLOAT_VAL(val, GET_FLOAT_VAL(src));
23,431✔
164
      break;
23,431✔
165
    case TSDB_DATA_TYPE_DOUBLE:
2,884,947✔
166
      SET_DOUBLE_VAL(val, GET_DOUBLE_VAL(src));
2,884,947✔
167
      break;
2,889,168✔
168
    case TSDB_DATA_TYPE_BIGINT:
34,261,100✔
169
    case TSDB_DATA_TYPE_UBIGINT:
170
    case TSDB_DATA_TYPE_TIMESTAMP:
171
      *((int64_t *)val) = GET_INT64_VAL(src);
34,261,100✔
172
      break;
34,265,607✔
173
    case TSDB_DATA_TYPE_BINARY:
9,834,806✔
174
    case TSDB_DATA_TYPE_VARBINARY:
175
    case TSDB_DATA_TYPE_GEOMETRY:
176
      varDataCopy(val, src);
9,834,806!
177
      break;
9,840,285✔
178
    case TSDB_DATA_TYPE_NCHAR:
12,712✔
179
      varDataCopy(val, src);
12,712!
180
      break;
12,712✔
181
    default: {
19,434✔
182
      if (len > 0) {
19,434!
183
        (void)memcpy(val, src, len);
×
184
      }
185

186
      break;
19,434✔
187
    }
188
  }
189
}
63,162,896✔
190

191
int32_t operateVal(void *dst, void *s1, void *s2, int32_t optr, int32_t type) {
108,040✔
192
  if (optr == OP_TYPE_ADD) {
108,040!
193
    switch (type) {
108,040!
194
      case TSDB_DATA_TYPE_TINYINT:
×
195
        *((int8_t *)dst) = GET_INT8_VAL(s1) + GET_INT8_VAL(s2);
×
196
        break;
×
197
      case TSDB_DATA_TYPE_UTINYINT:
×
198
        *((uint8_t *)dst) = GET_UINT8_VAL(s1) + GET_UINT8_VAL(s2);
×
199
        break;
×
200
      case TSDB_DATA_TYPE_SMALLINT:
×
201
        *((int16_t *)dst) = GET_INT16_VAL(s1) + GET_INT16_VAL(s2);
×
202
        break;
×
203
      case TSDB_DATA_TYPE_USMALLINT:
×
204
        *((uint16_t *)dst) = GET_UINT16_VAL(s1) + GET_UINT16_VAL(s2);
×
205
        break;
×
206
      case TSDB_DATA_TYPE_INT:
×
207
        *((int32_t *)dst) = GET_INT32_VAL(s1) + GET_INT32_VAL(s2);
×
208
        break;
×
209
      case TSDB_DATA_TYPE_UINT:
×
210
        *((uint32_t *)dst) = GET_UINT32_VAL(s1) + GET_UINT32_VAL(s2);
×
211
        break;
×
212
      case TSDB_DATA_TYPE_BIGINT:
×
213
        *((int64_t *)dst) = GET_INT64_VAL(s1) + GET_INT64_VAL(s2);
×
214
        break;
×
215
      case TSDB_DATA_TYPE_UBIGINT:
×
216
        *((uint64_t *)dst) = GET_UINT64_VAL(s1) + GET_UINT64_VAL(s2);
×
217
        break;
×
218
      case TSDB_DATA_TYPE_TIMESTAMP:
108,040✔
219
        *((int64_t *)dst) = GET_INT64_VAL(s1) + GET_INT64_VAL(s2);
108,040✔
220
        break;
108,040✔
221
      case TSDB_DATA_TYPE_FLOAT:
×
222
        SET_FLOAT_VAL(dst, GET_FLOAT_VAL(s1) + GET_FLOAT_VAL(s2));
×
223
        break;
×
224
      case TSDB_DATA_TYPE_DOUBLE:
×
225
        SET_DOUBLE_VAL(dst, GET_DOUBLE_VAL(s1) + GET_DOUBLE_VAL(s2));
×
226
        break;
×
227
      default: {
×
228
        return -1;
×
229
      }
230
    }
231
  } else {
232
    return -1;
×
233
  }
234

235
  return 0;
108,040✔
236
}
237

238
uint8_t decimalTypeFromPrecision(uint8_t precision) {
243,290✔
239
  return precision > TSDB_DECIMAL64_MAX_PRECISION ? TSDB_DATA_TYPE_DECIMAL : TSDB_DATA_TYPE_DECIMAL64;
243,290✔
240
}
241

242
STypeMod decimalCalcTypeMod(uint8_t prec, uint8_t scale) { return ((STypeMod)prec << 8) + scale; }
7,585,278✔
243

244
void decimalFromTypeMod(STypeMod typeMod, uint8_t *precision, uint8_t *scale) {
1,410,751,643✔
245
  if (precision) *precision = (uint8_t)((typeMod >> 8) & 0xFF);
1,410,751,643!
246
  if (scale) *scale = (uint8_t)(typeMod & 0xFF);
1,412,690,381!
247
}
1,411,921,137✔
248

249
STypeMod typeGetTypeModFromDataType(const SDataType *pDataType) {
738,509,999✔
250
  if (IS_DECIMAL_TYPE(pDataType->type)) return decimalCalcTypeMod(pDataType->precision, pDataType->scale);
738,509,999✔
251
  return 0;
738,565,206✔
252
}
253

254
STypeMod typeGetTypeMod(uint8_t type, uint8_t prec, uint8_t scale, int32_t bytes) {
2,147,483,647✔
255
  if (IS_DECIMAL_TYPE(type)) {
2,147,483,647✔
256
    return decimalCalcTypeMod(prec, scale);
6,754,663✔
257
  }
258
  return 0;
2,147,483,647✔
259
}
260

261
void fillTypeFromTypeMod(SDataType *pType, STypeMod mod) {
748,913,138✔
262
  if (IS_DECIMAL_TYPE(pType->type)) {
748,913,138✔
263
    decimalFromTypeMod(mod, &pType->precision, &pType->scale);
1,455,392✔
264
  }
265
}
748,915,351✔
266

267
void extractTypeFromTypeMod(uint8_t type, STypeMod typeMod, uint8_t *prec, uint8_t *scale, int32_t *bytes) {
1,483,843,356✔
268
  if (IS_DECIMAL_TYPE(type)) {
1,483,843,356✔
269
    decimalFromTypeMod(typeMod, prec, scale);
4,693,260✔
270
  } else {
271
    if (prec) *prec = 0;
1,479,809,694✔
272
    if (scale) *scale = 0;
1,479,812,710✔
273
  }
274
  if (bytes) *bytes = tDataTypes[type].bytes;
1,485,617,141✔
275
}
1,485,617,141✔
276

UNCOV
277
uint8_t getScaleFromTypeMod(int32_t type, STypeMod mod) {
×
UNCOV
278
  if (IS_DECIMAL_TYPE(type)) return (uint8_t)(mod & 0xFF);
×
279
  return 0;
×
280
}
281

282
// bytes, 0, prec, scale
283
void fillBytesForDecimalType(int32_t *pBytes, int32_t type, uint8_t precision, uint8_t scale) {
20,943,783✔
284
  *pBytes = 0;
20,943,783✔
285
  *pBytes = tDataTypes[type].bytes << 24;
20,943,783✔
286
  *pBytes |= (uint32_t)precision << 8;
20,943,783✔
287
  *pBytes |= scale;
20,943,783✔
288
}
20,943,783✔
289

290
void extractDecimalTypeInfoFromBytes(int32_t *pBytes, uint8_t *precision, uint8_t *scale) {
20,435,363✔
291
  *precision = (uint8_t)((*pBytes >> 8) & 0xFF);
20,435,363✔
292
  *scale = (uint8_t)(*pBytes & 0xFF);
20,435,363✔
293
  *pBytes >>= 24;
20,435,363✔
294
}
20,435,363✔
295

296
int32_t calcTypeBytesFromSchemaBytes(int32_t type, int32_t schemaBytes, bool isStmt) {
1,445,634,864✔
297
  if (isStmt) return schemaBytes;
1,445,634,864✔
298
  if (type == TSDB_DATA_TYPE_VARCHAR || type == TSDB_DATA_TYPE_VARBINARY || type == TSDB_DATA_TYPE_GEOMETRY) {
1,445,563,216✔
299
    return schemaBytes - VARSTR_HEADER_SIZE;
289,456,949✔
300
  } else if (type == TSDB_DATA_TYPE_NCHAR || type == TSDB_DATA_TYPE_JSON) {
1,156,106,267✔
301
    return (schemaBytes - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE;
70,240,324✔
302
  } else if (IS_STR_DATA_BLOB(type)) {
1,085,865,943✔
303
    return TSDB_MAX_BLOB_LEN;
80,053✔
304
  }
305
  return schemaBytes;
1,085,785,890✔
306
}
307

308
int32_t calcSchemaBytesFromTypeBytes(int32_t type, int32_t varTypeBytes, bool isStmt) {
2,147,483,647✔
309
  if (isStmt) return varTypeBytes;
2,147,483,647✔
310
  if (type == TSDB_DATA_TYPE_VARCHAR || type == TSDB_DATA_TYPE_VARBINARY || type == TSDB_DATA_TYPE_GEOMETRY) {
2,147,483,647✔
311
    return varTypeBytes + VARSTR_HEADER_SIZE;
2,069,768,347✔
312
  } else if (type == TSDB_DATA_TYPE_NCHAR || type == TSDB_DATA_TYPE_JSON) {
2,147,483,647✔
313
    return varTypeBytes * TSDB_NCHAR_SIZE + VARSTR_HEADER_SIZE;
1,042,768,174✔
314
  } else if (IS_STR_DATA_BLOB(type)) {
2,147,483,647✔
315
    return varTypeBytes + BLOBSTR_HEADER_SIZE;
69,957✔
316
  }
317
  return varTypeBytes;
2,147,483,647✔
318
}
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