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

open-source-parsers / jsoncpp / 10784089608

10 Sep 2024 01:27AM UTC coverage: 95.383%. First build
10784089608

Pull #1561

github

web-flow
Merge branch 'master' into baylesj-patch-6
Pull Request #1561: Add code coverage

5268 of 5523 relevant lines covered (95.38%)

12029.53 hits per line

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

91.12
/src/jsontestrunner/main.cpp
1
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
2
// Distributed under MIT license, or public domain if desired and
3
// recognized in your jurisdiction.
4
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5

6
#if defined(__GNUC__)
7
#pragma GCC diagnostic push
8
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
9
#elif defined(_MSC_VER)
10
#pragma warning(disable : 4996)
11
#endif
12

13
/* This executable is used for testing parser/writer using real JSON files.
14
 */
15

16
#include <algorithm> // sort
17
#include <cstdio>
18
#include <iostream>
19
#include <json/json.h>
20
#include <memory>
21
#include <sstream>
22

23
struct Options {
489✔
24
  Json::String path;
25
  Json::Features features;
26
  bool parseOnly;
27
  using writeFuncType = Json::String (*)(Json::Value const&);
28
  writeFuncType write;
29
};
30

31
static Json::String normalizeFloatingPointStr(double value) {
288✔
32
  char buffer[32];
33
  jsoncpp_snprintf(buffer, sizeof(buffer), "%.16g", value);
34
  buffer[sizeof(buffer) - 1] = 0;
288✔
35
  Json::String s(buffer);
288✔
36
  Json::String::size_type index = s.find_last_of("eE");
37
  if (index != Json::String::npos) {
288✔
38
    Json::String::size_type hasSign =
39
        (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
72✔
40
    Json::String::size_type exponentStartIndex = index + 1 + hasSign;
72✔
41
    Json::String normalized = s.substr(0, exponentStartIndex);
72✔
42
    Json::String::size_type indexDigit =
43
        s.find_first_not_of('0', exponentStartIndex);
44
    Json::String exponent = "0";
72✔
45
    if (indexDigit != Json::String::npos) // There is an exponent different
72✔
46
                                          // from 0
47
    {
48
      exponent = s.substr(indexDigit);
144✔
49
    }
50
    return normalized + exponent;
72✔
51
  }
52
  return s;
53
}
54

55
static Json::String readInputTestFile(const char* path) {
831✔
56
  FILE* file = fopen(path, "rb");
831✔
57
  if (!file)
831✔
58
    return "";
×
59
  fseek(file, 0, SEEK_END);
831✔
60
  auto const size = ftell(file);
831✔
61
  auto const usize = static_cast<size_t>(size);
831✔
62
  fseek(file, 0, SEEK_SET);
831✔
63
  auto buffer = new char[size + 1];
831✔
64
  buffer[size] = 0;
831✔
65
  Json::String text;
66
  if (fread(buffer, 1, usize, file) == usize)
831✔
67
    text = buffer;
68
  fclose(file);
831✔
69
  delete[] buffer;
831✔
70
  return text;
71
}
72

73
static void printValueTree(FILE* fout, Json::Value& value,
107,040✔
74
                           const Json::String& path = ".") {
75
  if (value.hasComment(Json::commentBefore)) {
107,040✔
76
    fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
1,584✔
77
  }
78
  switch (value.type()) {
107,040✔
79
  case Json::nullValue:
80
    fprintf(fout, "%s=null\n", path.c_str());
81
    break;
82
  case Json::intValue:
104,940✔
83
    fprintf(fout, "%s=%s\n", path.c_str(),
84
            Json::valueToString(value.asLargestInt()).c_str());
104,940✔
85
    break;
104,940✔
86
  case Json::uintValue:
84✔
87
    fprintf(fout, "%s=%s\n", path.c_str(),
88
            Json::valueToString(value.asLargestUInt()).c_str());
84✔
89
    break;
84✔
90
  case Json::realValue:
288✔
91
    fprintf(fout, "%s=%s\n", path.c_str(),
92
            normalizeFloatingPointStr(value.asDouble()).c_str());
288✔
93
    break;
288✔
94
  case Json::stringValue:
696✔
95
    fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
696✔
96
    break;
696✔
97
  case Json::booleanValue:
96✔
98
    fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
96✔
99
    break;
100
  case Json::arrayValue: {
101
    fprintf(fout, "%s=[]\n", path.c_str());
102
    Json::ArrayIndex size = value.size();
372✔
103
    for (Json::ArrayIndex index = 0; index < size; ++index) {
105,216✔
104
      static char buffer[16];
105
      jsoncpp_snprintf(buffer, sizeof(buffer), "[%u]", index);
106
      printValueTree(fout, value[index], path + buffer);
209,688✔
107
    }
108
  } break;
109
  case Json::objectValue: {
110
    fprintf(fout, "%s={}\n", path.c_str());
111
    Json::Value::Members members(value.getMemberNames());
468✔
112
    std::sort(members.begin(), members.end());
468✔
113
    Json::String suffix = *(path.end() - 1) == '.' ? "" : ".";
732✔
114
    for (const auto& name : members) {
1,272✔
115
      printValueTree(fout, value[name], path + suffix + name);
1,608✔
116
    }
117
  } break;
468✔
118
  default:
119
    break;
120
  }
121

122
  if (value.hasComment(Json::commentAfter)) {
107,040✔
123
    fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
48✔
124
  }
125
}
107,040✔
126

127
static int parseAndSaveValueTree(const Json::String& input,
1,527✔
128
                                 const Json::String& actual,
129
                                 const Json::String& kind,
130
                                 const Json::Features& features, bool parseOnly,
131
                                 Json::Value* root, bool use_legacy) {
132
  if (!use_legacy) {
1,527✔
133
    Json::CharReaderBuilder builder;
843✔
134

135
    builder.settings_["allowComments"] = features.allowComments_;
843✔
136
    builder.settings_["strictRoot"] = features.strictRoot_;
843✔
137
    builder.settings_["allowDroppedNullPlaceholders"] =
843✔
138
        features.allowDroppedNullPlaceholders_;
1,686✔
139
    builder.settings_["allowNumericKeys"] = features.allowNumericKeys_;
843✔
140

141
    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
843✔
142
    Json::String errors;
143
    const bool parsingSuccessful =
144
        reader->parse(input.data(), input.data() + input.size(), root, &errors);
843✔
145

146
    if (!parsingSuccessful) {
837✔
147
      std::cerr << "Failed to parse " << kind << " file: " << std::endl
148
                << errors << std::endl;
149
      return 1;
150
    }
151

152
    // We may instead check the legacy implementation (to ensure it doesn't
153
    // randomly get broken).
154
  } else {
843✔
155
    Json::Reader reader(features);
684✔
156
    const bool parsingSuccessful =
157
        reader.parse(input.data(), input.data() + input.size(), *root);
684✔
158
    if (!parsingSuccessful) {
684✔
159
      std::cerr << "Failed to parse " << kind << " file: " << std::endl
160
                << reader.getFormatedErrorMessages() << std::endl;
×
161
      return 1;
162
    }
163
  }
684✔
164

165
  if (!parseOnly) {
1,401✔
166
    FILE* factual = fopen(actual.c_str(), "wt");
1,392✔
167
    if (!factual) {
1,392✔
168
      std::cerr << "Failed to create '" << kind << "' actual file."
169
                << std::endl;
170
      return 2;
×
171
    }
172
    printValueTree(factual, *root);
1,392✔
173
    fclose(factual);
1,392✔
174
  }
175
  return 0;
176
}
177
// static Json::String useFastWriter(Json::Value const& root) {
178
//   Json::FastWriter writer;
179
//   writer.enableYAMLCompatibility();
180
//   return writer.write(root);
181
// }
182
static Json::String useStyledWriter(Json::Value const& root) {
232✔
183
  Json::StyledWriter writer;
232✔
184
  return writer.write(root);
464✔
185
}
232✔
186
static Json::String useStyledStreamWriter(Json::Value const& root) {
232✔
187
  Json::StyledStreamWriter writer;
232✔
188
  Json::OStringStream sout;
232✔
189
  writer.write(sout, root);
232✔
190
  return sout.str();
232✔
191
}
232✔
192
static Json::String useBuiltStyledStreamWriter(Json::Value const& root) {
232✔
193
  Json::StreamWriterBuilder builder;
232✔
194
  return Json::writeString(builder, root);
464✔
195
}
232✔
196
static int rewriteValueTree(const Json::String& rewritePath,
696✔
197
                            const Json::Value& root,
198
                            Options::writeFuncType write,
199
                            Json::String* rewrite) {
200
  *rewrite = write(root);
1,392✔
201
  FILE* fout = fopen(rewritePath.c_str(), "wt");
696✔
202
  if (!fout) {
696✔
203
    std::cerr << "Failed to create rewrite file: " << rewritePath << std::endl;
204
    return 2;
×
205
  }
206
  fprintf(fout, "%s\n", rewrite->c_str());
207
  fclose(fout);
696✔
208
  return 0;
696✔
209
}
210

211
static Json::String removeSuffix(const Json::String& path,
831✔
212
                                 const Json::String& extension) {
213
  if (extension.length() >= path.length())
831✔
214
    return Json::String("");
×
215
  Json::String suffix = path.substr(path.length() - extension.length());
831✔
216
  if (suffix != extension)
831✔
217
    return Json::String("");
×
218
  return path.substr(0, path.length() - extension.length());
831✔
219
}
220

221
static void printConfig() {
×
222
// Print the configuration used to compile JsonCpp
223
#if defined(JSON_NO_INT64)
224
  std::cout << "JSON_NO_INT64=1" << std::endl;
225
#else
226
  std::cout << "JSON_NO_INT64=0" << std::endl;
227
#endif
228
}
×
229

230
static int printUsage(const char* argv[]) {
×
231
  std::cout << "Usage: " << argv[0] << " [--strict] input-json-file"
×
232
            << std::endl;
233
  return 3;
×
234
}
235

236
static int parseCommandLine(int argc, const char* argv[], Options* opts) {
489✔
237
  opts->parseOnly = false;
489✔
238
  opts->write = &useStyledWriter;
489✔
239
  if (argc < 2) {
489✔
240
    return printUsage(argv);
×
241
  }
242
  int index = 1;
243
  if (Json::String(argv[index]) == "--parse-only") {
978✔
244
    opts->parseOnly = true;
135✔
245
    ++index;
246
  }
247
  if (Json::String(argv[index]) == "--strict") {
978✔
248
    opts->features = Json::Features::strictMode();
99✔
249
    ++index;
99✔
250
  }
251
  if (Json::String(argv[index]) == "--json-config") {
978✔
252
    printConfig();
×
253
    return 3;
×
254
  }
255
  if (Json::String(argv[index]) == "--json-writer") {
978✔
256
    ++index;
489✔
257
    Json::String const writerName(argv[index++]);
489✔
258
    if (writerName == "StyledWriter") {
489✔
259
      opts->write = &useStyledWriter;
163✔
260
    } else if (writerName == "StyledStreamWriter") {
326✔
261
      opts->write = &useStyledStreamWriter;
163✔
262
    } else if (writerName == "BuiltStyledStreamWriter") {
163✔
263
      opts->write = &useBuiltStyledStreamWriter;
163✔
264
    } else {
265
      std::cerr << "Unknown '--json-writer' " << writerName << std::endl;
266
      return 4;
267
    }
268
  }
269
  if (index == argc || index + 1 < argc) {
489✔
270
    return printUsage(argv);
×
271
  }
272
  opts->path = argv[index];
489✔
273
  return 0;
489✔
274
}
275

276
static int runTest(Options const& opts, bool use_legacy) {
831✔
277
  int exitCode = 0;
278

279
  Json::String input = readInputTestFile(opts.path.c_str());
831✔
280
  if (input.empty()) {
831✔
281
    std::cerr << "Invalid input file: " << opts.path << std::endl;
282
    return 3;
283
  }
284

285
  Json::String basePath = removeSuffix(opts.path, ".json");
837✔
286
  if (!opts.parseOnly && basePath.empty()) {
831✔
287
    std::cerr << "Bad input path '" << opts.path
288
              << "'. Must end with '.expected'" << std::endl;
289
    return 3;
290
  }
291

292
  Json::String const actualPath = basePath + ".actual";
831✔
293
  Json::String const rewritePath = basePath + ".rewrite";
831✔
294
  Json::String const rewriteActualPath = basePath + ".actual-rewrite";
831✔
295

296
  Json::Value root;
831✔
297
  exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features,
831✔
298
                                   opts.parseOnly, &root, use_legacy);
831✔
299
  if (exitCode || opts.parseOnly) {
825✔
300
    return exitCode;
301
  }
302

303
  Json::String rewrite;
304
  exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
696✔
305
  if (exitCode) {
696✔
306
    return exitCode;
307
  }
308

309
  Json::Value rewriteRoot;
696✔
310
  exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite",
696✔
311
                                   opts.features, opts.parseOnly, &rewriteRoot,
696✔
312
                                   use_legacy);
313

314
  return exitCode;
315
}
1,527✔
316

317
int main(int argc, const char* argv[]) {
489✔
318
  Options opts;
489✔
319
  try {
320
    int exitCode = parseCommandLine(argc, argv, &opts);
489✔
321
    if (exitCode != 0) {
489✔
322
      std::cerr << "Failed to parse command-line." << std::endl;
323
      return exitCode;
462✔
324
    }
325

326
    const int modern_return_code = runTest(opts, false);
489✔
327
    if (modern_return_code) {
483✔
328
      return modern_return_code;
329
    }
330

331
    const std::string filename =
332
        opts.path.substr(opts.path.find_last_of("\\/") + 1);
363✔
333
    const bool should_run_legacy = (filename.rfind("legacy_", 0) == 0);
334
    if (should_run_legacy) {
363✔
335
      return runTest(opts, true);
342✔
336
    }
337
  } catch (const std::exception& e) {
6✔
338
    std::cerr << "Unhandled exception:" << std::endl << e.what() << std::endl;
6✔
339
    return 1;
340
  }
6✔
341
  return 0;
21✔
342
}
343

344
#if defined(__GNUC__)
345
#pragma GCC diagnostic pop
346
#endif
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

© 2025 Coveralls, Inc