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

palfrey / tagpy / 35536345018

20 Sep 2026 08:41PM UTC coverage: 67.387% (-0.1%) from 67.483%
35536345018

push

github

web-flow
Merge pull request #73 from palfrey/taglib-2.3-issues

Increase supported taglib versions in CI

444 of 940 branches covered (47.23%)

Branch coverage included in aggregate %.

2 of 3 new or added lines in 1 file covered. (66.67%)

709 of 771 relevant lines covered (91.96%)

104.67 hits per line

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

66.67
/src/wrapper/rest.cpp
1
// Copyright (c) 2006-2008 Andreas Kloeckner
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a copy
4
// of this software and associated documentation files (the "Software"), to
5
// deal in the Software without restriction, including without limitation the
6
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
// sell copies of the Software, and to permit persons to whom the Software is
8
// furnished to do so, subject to the following conditions:
9
//
10
// The above copyright notice and this permission notice shall be included in
11
// all copies or substantial portions of the Software.
12
//
13
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
// IN THE SOFTWARE.
20

21

22

23

24
#include <taglib/apetag.h>
25
#include <taglib/mpcfile.h>
26
#include <taglib/flacfile.h>
27
#include <taglib/xiphcomment.h>
28
#include <taglib/oggflacfile.h>
29
#include <taglib/vorbisfile.h>
30
#include <taglib/apefooter.h>
31
#include <taglib/apeitem.h>
32
#include <taglib/id3v1tag.h>
33
#include <taglib/id3v2tag.h>
34
#include <taglib/wavfile.h>
35
#include <taglib/mp4file.h>
36
#include <taglib/mp4tag.h>
37
#include <taglib/mp4coverart.h>
38
#include <taglib/mp4item.h>
39
#include <taglib/opusfile.h>
40

41
#include "common.hpp"
42

43

44

45

46
namespace
47
{
48
  // -------------------------------------------------------------
49
  // FLAC
50
  // -------------------------------------------------------------
51
  MF_OL(ID3v1Tag, 0, 1);
×
52
  MF_OL(ID3v2Tag, 0, 1);
×
53
  MF_OL(xiphComment, 0, 1);
×
54

55
  // -------------------------------------------------------------
56
  // Ogg
57
  // -------------------------------------------------------------
58
  MF_OL(addField, 2, 3);
×
59
  #if TAGLIB_HEX_VERSION < CHECK_VERSION(1,11,0)
60
  MF_OL(removeField, 1, 2);
61
  #else
62
  MF_OL(removeFields, 1, 2);
63
  #endif
64
  MF_OL(render, 0, 1);
×
65

66
  // -------------------------------------------------------------
67
  // APE
68
  // -------------------------------------------------------------
69
  MF_OL(addValue, 2, 3);
×
70

71
  // -------------------------------------------------------------
72
  // MPC
73
  // -------------------------------------------------------------
74
  #if (TAGLIB_MAJOR_VERSION == 1)
75
  MF_OL(remove, 0, 1);
76
  #endif
77
  //MF_OL(ID3v1Tag, 0, 1);
78
  MF_OL(APETag, 0, 1);
×
79

80
  // WAV
81
  MF_OL(strip, 0, 1);
×
82

83
  // Ogg
84
  // Really should be 2, but the second variant is protected
NEW
85
  MF_OL(packet, 1, 1);
×
86

87
  #if TAGLIB_HEX_VERSION >= CHECK_VERSION(1,10,0)
88
  // MP4
89
  TagLib::MP4::CoverArtList mp4_Tag_GetCovers(TagLib::MP4::Tag &t) {
90
    if (!t.contains("covr")) {
×
91
      return {};
92
    }
93
    return t.item("covr").toCoverArtList();
×
94
  }
95
  void mp4_Tag_SetCovers(TagLib::MP4::Tag &t, TagLib::MP4::CoverArtList& l) {
40 ✔
96
    return t.setItem("covr", l);
40 !
97
  }
98
  #endif
99
}
100

101
#if TAGLIB_HEX_VERSION >= CHECK_VERSION(1,11,0)
102
void addPictureWithOwnership(Ogg::XiphComment &cl, std::auto_ptr<TagLib::FLAC::Picture> picture) {
35 ✔
103
  cl.addPicture(picture.get());
35 ✔
104
  picture.release();
35 ✔
105
}
35 ✔
106
#endif
107

108
void flacAddPictureWithOwnership(FLAC::File &cl, std::auto_ptr<TagLib::FLAC::Picture> picture) {
45 ✔
109
  cl.addPicture(picture.get());
45 ✔
110
  picture.release();
45 ✔
111
}
45 ✔
112

113
void flacAddPictureCopy(FLAC::File &cl, TagLib::FLAC::Picture *picture) {
90 ✔
114
  TagLib::FLAC::Picture *picture_copy = new TagLib::FLAC::Picture(picture->render());
90 !
115
  cl.addPicture(picture_copy);
90 ✔
116
}
90 ✔
117

118
void exposeRest()
45 ✔
119
{
120
  // -------------------------------------------------------------
121
  // Ogg
122
  // -------------------------------------------------------------
123
  exposeMap<String, StringList>("ogg_FieldListMap");
45 ✔
124

125
  {
45 ✔
126
    typedef Ogg::XiphComment cl;
45 ✔
127
    class_<cl, bases<Tag>, boost::noncopyable>
45 ✔
128
      ("ogg_XiphComment", init<boost::python::optional<const ByteVector &> >())
45 ✔
129
      .DEF_SIMPLE_METHOD(fieldCount)
45 !
130
      .def("fieldListMap", &cl::fieldListMap,
45 !
131
           return_internal_reference<>())
45 ✔
132
      .DEF_SIMPLE_METHOD(vendorID)
45 !
133
      .DEF_OVERLOADED_METHOD(addField, void (cl::*)(const String &, const String &, bool))
45 !
134
      #if TAGLIB_HEX_VERSION < CHECK_VERSION(1,11,0)
135
      .DEF_OVERLOADED_METHOD(removeField, void (cl::*)(const String &, const String &))
10 !
136
      #else
137
      .DEF_OVERLOADED_METHOD(removeFields, void (cl::*)(const String &, const String &))
35 !
138
      #endif
139
      .DEF_OVERLOADED_METHOD(render, ByteVector (cl::*)(bool) const)
80 !
140
      #if TAGLIB_HEX_VERSION >= CHECK_VERSION(1,11,0)
141
      .DEF_SIMPLE_METHOD(pictureList)
35 !
142
      .DEF_SIMPLE_METHOD(removeAllPictures)
35 !
143
      .def("removePicture", &cl::removePicture)
35 !
144
      .def("addPicture", addPictureWithOwnership);
35 !
145
      #endif
146
      ;
35 ✔
147
  }
148

149
  {
45 ✔
150
    typedef Ogg::File cl;
45 ✔
151

152
    ByteVector (Ogg::File::*pk1)(unsigned int) = &cl::packet;
45 ✔
153
    // FIXME: second variant is protected, so don't have access
154
    // ByteVector (Ogg::File::*pk2)(unsigned int, unsigned int) = &cl::packet;
155

156
    class_<cl, bases<File>, boost::noncopyable>
45 ✔
157
      ("ogg_File", no_init)
158
      .def("packet", pk1, packet_overloads())
90 !
159
      // .def("packet", pk2, packet_overloads())
160
      .DEF_SIMPLE_METHOD(setPacket)
45 !
161
      // MISSING: page headers
162
      ;
163
  }
164

165
  {
45 ✔
166
    typedef Ogg::FLAC::File cl;
45 ✔
167
    class_<cl, bases<Ogg::File>, boost::noncopyable>
45 ✔
168
      ("ogg_flac_File", init<const char *, boost::python::optional<bool, AudioProperties::ReadStyle> >())
45 ✔
169
      ;
170
  }
171

172
  {
45 ✔
173
    typedef Ogg::Vorbis::File cl;
45 ✔
174
    class_<cl, bases<Ogg::File>, boost::noncopyable>
45 ✔
175
      ("ogg_vorbis_File", init<const char *, boost::python::optional<bool, AudioProperties::ReadStyle> >())
45 ✔
176
      ;
177
  }
178

179
  {
45 ✔
180
    typedef Ogg::Opus::File cl;
45 ✔
181
    class_<cl, bases<Ogg::File>, boost::noncopyable>
45 ✔
182
      ("ogg_opus_File", init<const char *, boost::python::optional<bool, AudioProperties::ReadStyle> >())
45 ✔
183
      ;
184
  }
185

186
  // -------------------------------------------------------------
187
  // APE
188
  // -------------------------------------------------------------
189
  {
45 ✔
190
    typedef APE::Footer cl;
45 ✔
191
    class_<cl, boost::noncopyable>(
45 ✔
192
      "ape_Footer", init<boost::python::optional<const ByteVector &> >())
45 ✔
193
      .DEF_SIMPLE_METHOD(version)
45 !
194
      .DEF_SIMPLE_METHOD(headerPresent)
45 !
195
      .DEF_SIMPLE_METHOD(footerPresent)
45 !
196
      .DEF_SIMPLE_METHOD(isHeader)
45 !
197
      .DEF_SIMPLE_METHOD(setHeaderPresent)
45 !
198
      .DEF_SIMPLE_METHOD(itemCount)
45 !
199
      .DEF_SIMPLE_METHOD(setItemCount)
45 !
200
      .DEF_SIMPLE_METHOD(tagSize)
45 !
201
      .DEF_SIMPLE_METHOD(completeTagSize)
45 !
202
      .DEF_SIMPLE_METHOD(setTagSize)
45 !
203
      .DEF_SIMPLE_METHOD(setData)
45 !
204
      .DEF_SIMPLE_METHOD(renderFooter)
45 !
205
      .DEF_SIMPLE_METHOD(renderHeader)
45 !
206
      ;
207
  }
208

209
  {
45 ✔
210
    typedef APE::Item scope;
45 ✔
211
    enum_<scope::ItemTypes>("ape_ItemTypes")
45 !
212
      .ENUM_VALUE(Text)
45 !
213
      .ENUM_VALUE(Binary)
45 !
214
      .ENUM_VALUE(Locator)
45 !
215
      ;
216
  }
217

218
  {
45 ✔
219
    typedef APE::Item cl;
45 ✔
220
    class_<cl>("ape_Item")
45 ✔
221
      .def(init<const String &, const String &>())
45 !
222
      .def(init<const String &, const StringList &>())
45 !
223
      .def(init<const cl &>())
90 !
224
      .DEF_SIMPLE_METHOD(key)
45 !
225
      .DEF_SIMPLE_METHOD(binaryData)
45 !
226
      .DEF_SIMPLE_METHOD(size)
45 !
227
      .DEF_SIMPLE_METHOD(toString)
45 !
228
      .DEF_SIMPLE_METHOD(values)
45 !
229
      .DEF_SIMPLE_METHOD(render)
45 !
230
      .DEF_SIMPLE_METHOD(parse)
45 !
231
      .DEF_SIMPLE_METHOD(setReadOnly)
45 !
232
      .DEF_SIMPLE_METHOD(isReadOnly)
45 !
233
      .DEF_SIMPLE_METHOD(setType)
45 !
234
      .DEF_SIMPLE_METHOD(type)
45 !
235
      .DEF_SIMPLE_METHOD(isEmpty)
45 !
236
      ;
237
  }
238

239
  exposeMap<const String, APE::Item>("ape_ItemListMap");
45 ✔
240

241
  {
45 ✔
242
    typedef APE::Tag cl;
45 ✔
243
    class_<cl, bases<Tag>, boost::noncopyable>("ape_Tag")
45 ✔
244
      .def(init<File *, long>())
45 !
245
      .def("footer", &cl::footer, return_internal_reference<>())
45 !
246
      .def("itemListMap", &cl::itemListMap, return_internal_reference<>())
45 !
247
      .DEF_SIMPLE_METHOD(removeItem)
45 !
248
      .DEF_OVERLOADED_METHOD(addValue, void (cl::*)(const String &, const String &,bool))
90 !
249
      .DEF_SIMPLE_METHOD(setItem)
45 !
250
      ;
251
  }
252

253
  // -------------------------------------------------------------
254
  // FLAC
255
  // -------------------------------------------------------------
256
  enum_<TagLib::FLAC::Picture::Type>("flac_PictureType")
45 !
257
    .value("Other", TagLib::FLAC::Picture::Type::Other)
45 !
258
    .value("FileIcon", TagLib::FLAC::Picture::Type::FileIcon)
45 !
259
    .value("OtherFileIcon", TagLib::FLAC::Picture::Type::OtherFileIcon)
45 !
260
    .value("FrontCover", TagLib::FLAC::Picture::Type::FrontCover)
45 !
261
    .value("BackCover", TagLib::FLAC::Picture::Type::BackCover)
45 !
262
    .value("LeafletPage", TagLib::FLAC::Picture::Type::LeafletPage)
45 !
263
    .value("Media", TagLib::FLAC::Picture::Type::Media)
45 !
264
    .value("LeadArtist", TagLib::FLAC::Picture::Type::LeadArtist)
45 !
265
    .value("Artist", TagLib::FLAC::Picture::Type::Artist)
45 !
266
    .value("Conductor", TagLib::FLAC::Picture::Type::Conductor)
45 !
267
    .value("Band", TagLib::FLAC::Picture::Type::Band)
45 !
268
    .value("Composer", TagLib::FLAC::Picture::Type::Composer)
45 !
269
    .value("Lyricist", TagLib::FLAC::Picture::Type::Lyricist)
45 !
270
    .value("RecordingLocation", TagLib::FLAC::Picture::Type::RecordingLocation)
45 !
271
    .value("DuringRecording", TagLib::FLAC::Picture::Type::DuringRecording)
45 !
272
    .value("DuringPerformance", TagLib::FLAC::Picture::Type::DuringPerformance)
45 !
273
    .value("MovieScreenCapture", TagLib::FLAC::Picture::Type::MovieScreenCapture)
45 !
274
    .value("ColouredFish", TagLib::FLAC::Picture::Type::ColouredFish)
45 !
275
    .value("Illustration", TagLib::FLAC::Picture::Type::Illustration)
45 !
276
    .value("BandLogo", TagLib::FLAC::Picture::Type::BandLogo)
45 !
277
    .value("PublisherLogo", TagLib::FLAC::Picture::Type::PublisherLogo)
45 !
278
    ;
279

280
  {
45 ✔
281
    typedef TagLib::FLAC::Picture cl;
45 ✔
282
    class_<cl, std::auto_ptr<cl>, boost::noncopyable>
45 ✔
283
      ("flac_Picture", init<>())
45 ✔
284
      .DEF_SIMPLE_METHOD(type)
45 !
285
      .DEF_SIMPLE_METHOD(setType)
45 !
286

287
      .DEF_SIMPLE_METHOD(data)
45 !
288
      .DEF_SIMPLE_METHOD(setData)
45 !
289

290
      .DEF_SIMPLE_METHOD(mimeType)
45 !
291
      .DEF_SIMPLE_METHOD(setMimeType)
45 !
292

293
      .DEF_SIMPLE_METHOD(description)
45 !
294
      .DEF_SIMPLE_METHOD(setDescription)
45 !
295

296
      .DEF_SIMPLE_METHOD(width)
45 !
297
      .DEF_SIMPLE_METHOD(setWidth)
45 !
298

299
      .DEF_SIMPLE_METHOD(height)
45 !
300
      .DEF_SIMPLE_METHOD(setHeight)
45 !
301

302
      .DEF_SIMPLE_METHOD(colorDepth)
45 !
303
      .DEF_SIMPLE_METHOD(setColorDepth)
45 !
304

305
      .DEF_SIMPLE_METHOD(numColors)
45 !
306
      .DEF_SIMPLE_METHOD(setNumColors)
45 !
307
      ;
308

309
  }
310

311
  exposePointerList<TagLib::FLAC::Picture>("flac_PictureList");
45 ✔
312

313
  {
45 ✔
314
    typedef FLAC::File cl;
45 ✔
315
    class_<cl, boost::noncopyable, bases<File> >("flac_File",
45 ✔
316
                                   init<const char *, boost::python::optional<bool, AudioProperties::ReadStyle> >())
45 ✔
317
      .def(init<const char *, ID3v2::FrameFactory *, boost::python::optional<bool, AudioProperties::ReadStyle> >())
45 !
318
      .def("ID3v1Tag",
45 !
319
           (ID3v1::Tag *(FLAC::File::*)(bool))
320
           &FLAC::File::ID3v1Tag,
321
           ID3v1Tag_overloads()[return_internal_reference<>()])
45 !
322
      .def("ID3v2Tag",
45 !
323
           (ID3v2::Tag *(FLAC::File::*)(bool))
324
           &FLAC::File::ID3v2Tag,
325
           ID3v2Tag_overloads()[return_internal_reference<>()])
45 !
326
      .def("xiphComment",
45 !
327
           (Ogg::XiphComment *(FLAC::File::*)(bool))
328
           &FLAC::File::xiphComment,
329
           xiphComment_overloads()[return_internal_reference<>()])
90 !
330
      .DEF_SIMPLE_METHOD(pictureList)
45 !
331
      .DEF_SIMPLE_METHOD(removePictures)
45 !
332
      .DEF_SIMPLE_METHOD(removePicture)
45 !
333
      // For existing picture objects from other FLAC files
334
      .def("addPicture", flacAddPictureCopy)
45 !
335
      // For freshly created Picture objects
336
      .def("addPicture", flacAddPictureWithOwnership)
45 !
337
      ;
338
  }
339

340
  // -------------------------------------------------------------
341
  // MPC
342
  // -------------------------------------------------------------
343
  enum_<MPC::File::TagTypes>("mpc_TagTypes")
45 !
344
    .value("NoTags", MPC::File::NoTags)
45 !
345
    .value("ID3v1", MPC::File::ID3v1)
45 !
346
    .value("ID3v2", MPC::File::ID3v2)
45 !
347
    .value("APE", MPC::File::APE)
45 !
348
    .value("AllTags", MPC::File::AllTags)
45 !
349
    ;
350

351
  {
45 ✔
352
    typedef MPC::File cl;
45 ✔
353
    class_<MPC::File, bases<File>, boost::noncopyable>
45 ✔
354
      ("mpc_File", init<const char *, boost::python::optional<bool, AudioProperties::ReadStyle> >())
45 ✔
355
      .def("ID3v1Tag",
45 !
356
           (ID3v1::Tag *(cl::*)(bool))
357
           &cl::ID3v1Tag,
358
           ID3v1Tag_overloads()[return_internal_reference<>()])
45 !
359
      .def("APETag",
45 !
360
           (APE::Tag *(cl::*)(bool))
361
           &cl::APETag,
362
           APETag_overloads()[return_internal_reference<>()])
45 !
363
      .def("remove",
90 ✔
364
           (void (cl::*)(int))
365
           &cl::strip,
366
           strip_overloads())
45 !
367
      ;
368
  }
369

370
  /// WAV
371

372
  enum_<TagLib::RIFF::WAV::File::TagTypes>("wav_TagTypes")
45 !
373
    .value("NoTags", TagLib::RIFF::WAV::File::NoTags)
45 !
374
    .value("ID3v2", TagLib::RIFF::WAV::File::ID3v2)
45 !
375
    .value("Info", TagLib::RIFF::WAV::File::Info)
45 !
376
    .value("AllTags", TagLib::RIFF::WAV::File::AllTags)
45 !
377
    ;
378

379
  {
45 ✔
380
    typedef TagLib::RIFF::WAV::File cl;
45 ✔
381
    class_<cl, bases<File>, boost::noncopyable>
45 ✔
382
      ("wav_File", init<const char *, boost::python::optional<bool, AudioProperties::ReadStyle> >())
45 ✔
383
      .def("ID3v2Tag",
45 !
384
           (ID3v2::Tag *(TagLib::RIFF::WAV::File::*)())
385
           &cl::ID3v2Tag,
386
           return_internal_reference<>())
45 ✔
387
      .def("InfoTag",
45 !
388
           (TagLib::RIFF::Info::Tag *(TagLib::RIFF::WAV::File::*)())
389
           &cl::InfoTag,
390
           return_internal_reference<>())
45 !
391
      #if (TAGPY_TAGLIB_HEX_VERSION >= 0x11100)
392
      .DEF_OVERLOADED_METHOD(strip, void (cl::*)(TagLib::RIFF::WAV::File::TagTypes) const)
393
      #endif
394
      ;
395
  }
396

397
    /// MP4
398
  #if TAGLIB_HEX_VERSION >= CHECK_VERSION(1,13,0)
399
  enum_<TagLib::MP4::File::TagTypes>("mp4_TagTypes")
25 !
400
    .value("NoTags", TagLib::MP4::File::NoTags)
25 !
401
    .value("MP4", TagLib::MP4::File::MP4)
25 !
402
    .value("AllTags", TagLib::MP4::File::AllTags)
25 !
403
    ;
404
  #endif
405
  {
45 ✔
406
    typedef TagLib::MP4::File cl;
45 ✔
407
    class_<cl, bases<File>, boost::noncopyable>
45 ✔
408
      ("mp4_File", init<const char *, boost::python::optional<bool, AudioProperties::ReadStyle> >())
45 ✔
409
      .def("tag",
45 !
410
           (MP4::Tag *(cl::*)() const)
411
           &cl::tag,
412
           return_internal_reference<>())
45 !
413
      ;
414
  }
415
  enum_<TagLib::MP4::CoverArt::Format>("mp4_CoverArtFormats")
45 !
416
    .value("JPEG", TagLib::MP4::CoverArt::JPEG)
45 !
417
    .value("PNG", TagLib::MP4::CoverArt::PNG)
45 !
418
    .value("BMP", TagLib::MP4::CoverArt::BMP)
45 !
419
    .value("GIF", TagLib::MP4::CoverArt::GIF)
45 !
420
    .value("Unknown", TagLib::MP4::CoverArt::Unknown)
45 !
421
    ;
422
  {
45 ✔
423
    typedef TagLib::MP4::CoverArt cl;
45 ✔
424
    class_<cl>
45 ✔
425
      ("mp4_CoverArt", init<TagLib::MP4::CoverArt::Format, const ByteVector &>())
45 ✔
426
      .DEF_SIMPLE_METHOD(format)
45 !
427
      .DEF_SIMPLE_METHOD(data)
45 !
428
      ;
429
  }
430
  exposeList<TagLib::MP4::CoverArt>("mp4_CoverArtList");
45 ✔
431
  {
45 ✔
432
    typedef TagLib::MP4::Tag cl;
45 ✔
433
    class_<TagWrap<cl>, boost::noncopyable>("Tag", no_init)
45 ✔
434
      .add_property("title", &cl::title, &cl::setTitle)
45 !
435
      .add_property("artist", &cl::artist, &cl::setArtist)
45 !
436
      .add_property("album", &cl::album, &cl::setAlbum)
45 !
437
      .add_property("comment", &cl::comment, &cl::setComment)
45 !
438
      .add_property("genre", &cl::genre, &cl::setGenre)
45 !
439
      .add_property("year", &cl::year, &cl::setYear)
45 !
440
      .add_property("track", &cl::track, &cl::setTrack)
45 !
441
      #if TAGLIB_HEX_VERSION >= CHECK_VERSION(1,10,0)
442
      .add_property("covers", &mp4_Tag_GetCovers, &mp4_Tag_SetCovers)
40 !
443
      #endif
444
      .DEF_VIRTUAL_METHOD(isEmpty)
45 !
445
      .DEF_SIMPLE_METHOD(duplicate)
45 !
446
      .staticmethod("duplicate")
45 !
447
      ;
448
  }
449

450
}
45 ✔
451

452
// EMACS-FORMAT-TAG
453
//
454
// Local Variables:
455
// mode: C++
456
// eval: (c-set-style "stroustrup")
457
// eval: (c-set-offset 'access-label -2)
458
// eval: (c-set-offset 'inclass '++)
459
// c-basic-offset: 2
460
// tab-width: 8
461
// End:
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc