blob: deb06875c359c65e3e8aa5b049be499b1dabfbb0 [file] [log] [blame]
Baptiste Lepilleur7469f1d2010-04-20 21:35:19 +00001// Copyright 2007-2010 Baptiste Lepilleur
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
Baptiste Lepilleurfa130ef2010-12-24 12:47:14 +00006/* This executable is used for testing parser/writer using real JSON files.
7 */
8
Christopher Dunnf9864232007-06-14 21:01:26 +00009#include <json/json.h>
10#include <algorithm> // sort
Christopher Dunn2160c9a2015-01-23 09:02:44 -060011#include <sstream>
Christopher Dunnf9864232007-06-14 21:01:26 +000012#include <stdio.h>
13
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100014#if defined(_MSC_VER) && _MSC_VER >= 1310
15#pragma warning(disable : 4996) // disable fopen deprecation warning
Christopher Dunnf9864232007-06-14 21:01:26 +000016#endif
17
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100018static std::string normalizeFloatingPointStr(double value) {
19 char buffer[32];
Aaron Jacobsd2618802013-08-08 23:08:28 +000020#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100021 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000022#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100023 snprintf(buffer, sizeof(buffer), "%.16g", value);
Aaron Jacobsd2618802013-08-08 23:08:28 +000024#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100025 buffer[sizeof(buffer) - 1] = 0;
26 std::string s(buffer);
27 std::string::size_type index = s.find_last_of("eE");
28 if (index != std::string::npos) {
29 std::string::size_type hasSign =
30 (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
31 std::string::size_type exponentStartIndex = index + 1 + hasSign;
32 std::string normalized = s.substr(0, exponentStartIndex);
33 std::string::size_type indexDigit =
34 s.find_first_not_of('0', exponentStartIndex);
35 std::string exponent = "0";
36 if (indexDigit !=
37 std::string::npos) // There is an exponent different from 0
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000038 {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100039 exponent = s.substr(indexDigit);
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000040 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100041 return normalized + exponent;
42 }
43 return s;
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000044}
45
Aaron Jacobs11086dd2014-09-15 10:15:29 +100046static std::string readInputTestFile(const char* path) {
47 FILE* file = fopen(path, "rb");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100048 if (!file)
49 return std::string("");
50 fseek(file, 0, SEEK_END);
51 long size = ftell(file);
52 fseek(file, 0, SEEK_SET);
53 std::string text;
Aaron Jacobs11086dd2014-09-15 10:15:29 +100054 char* buffer = new char[size + 1];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100055 buffer[size] = 0;
56 if (fread(buffer, 1, size, file) == (unsigned long)size)
57 text = buffer;
58 fclose(file);
59 delete[] buffer;
60 return text;
Christopher Dunnf9864232007-06-14 21:01:26 +000061}
62
Christopher Dunnf9864232007-06-14 21:01:26 +000063static void
Aaron Jacobs11086dd2014-09-15 10:15:29 +100064printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") {
Cory Quammen4d234922014-10-09 16:29:47 -040065 if (value.hasComment(Json::commentBefore)) {
66 fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
67 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +100068 switch (value.type()) {
69 case Json::nullValue:
70 fprintf(fout, "%s=null\n", path.c_str());
71 break;
72 case Json::intValue:
73 fprintf(fout,
74 "%s=%s\n",
75 path.c_str(),
76 Json::valueToString(value.asLargestInt()).c_str());
77 break;
78 case Json::uintValue:
79 fprintf(fout,
80 "%s=%s\n",
81 path.c_str(),
82 Json::valueToString(value.asLargestUInt()).c_str());
83 break;
84 case Json::realValue:
85 fprintf(fout,
86 "%s=%s\n",
87 path.c_str(),
88 normalizeFloatingPointStr(value.asDouble()).c_str());
89 break;
90 case Json::stringValue:
91 fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
92 break;
93 case Json::booleanValue:
94 fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
95 break;
96 case Json::arrayValue: {
97 fprintf(fout, "%s=[]\n", path.c_str());
98 int size = value.size();
99 for (int index = 0; index < size; ++index) {
100 static char buffer[16];
Aaron Jacobsd2618802013-08-08 23:08:28 +0000101#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000102 sprintf_s(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000103#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000104 snprintf(buffer, sizeof(buffer), "[%d]", index);
Aaron Jacobsd2618802013-08-08 23:08:28 +0000105#endif
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000106 printValueTree(fout, value[index], path + buffer);
107 }
108 } break;
109 case Json::objectValue: {
110 fprintf(fout, "%s={}\n", path.c_str());
111 Json::Value::Members members(value.getMemberNames());
112 std::sort(members.begin(), members.end());
113 std::string suffix = *(path.end() - 1) == '.' ? "" : ".";
114 for (Json::Value::Members::iterator it = members.begin();
115 it != members.end();
116 ++it) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000117 const std::string& name = *it;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000118 printValueTree(fout, value[name], path + suffix + name);
119 }
120 } break;
121 default:
122 break;
123 }
Cory Quammen4d234922014-10-09 16:29:47 -0400124
125 if (value.hasComment(Json::commentAfter)) {
126 fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
127 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000128}
129
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000130static int parseAndSaveValueTree(const std::string& input,
131 const std::string& actual,
132 const std::string& kind,
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000133 const Json::Features& features,
Christopher Dunn632c9b52015-01-23 11:09:04 -0600134 bool parseOnly,
Christopher Dunn79211e12015-01-23 11:27:19 -0600135 Json::Value* root)
136{
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000137 Json::Reader reader(features);
Christopher Dunn632c9b52015-01-23 11:09:04 -0600138 bool parsingSuccessful = reader.parse(input, *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000139 if (!parsingSuccessful) {
140 printf("Failed to parse %s file: \n%s\n",
141 kind.c_str(),
142 reader.getFormattedErrorMessages().c_str());
143 return 1;
144 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000145
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000146 if (!parseOnly) {
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000147 FILE* factual = fopen(actual.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000148 if (!factual) {
149 printf("Failed to create %s actual file.\n", kind.c_str());
Christopher Dunnf9864232007-06-14 21:01:26 +0000150 return 2;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000151 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600152 printValueTree(factual, *root);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000153 fclose(factual);
154 }
155 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000156}
Christopher Dunn79211e12015-01-23 11:27:19 -0600157// static std::string useFastWriter(Json::Value const& root) {
158// Json::FastWriter writer;
159// writer.enableYAMLCompatibility();
160// return writer.write(root);
161// }
162static std::string useStyledWriter(
163 Json::Value const& root)
164{
165 Json::StyledWriter writer;
166 return writer.write(root);
167}
168static std::string useStyledStreamWriter(
169 Json::Value const& root)
170{
Christopher Dunn2160c9a2015-01-23 09:02:44 -0600171 Json::StyledStreamWriter writer;
172 std::ostringstream sout;
173 writer.write(sout, root);
Christopher Dunn79211e12015-01-23 11:27:19 -0600174 return sout.str();
175}
176static int rewriteValueTree(
177 const std::string& rewritePath,
178 const Json::Value& root,
179 std::string* rewrite)
180{
181 *rewrite = useStyledWriter(root);
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000182 FILE* fout = fopen(rewritePath.c_str(), "wt");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000183 if (!fout) {
184 printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
185 return 2;
186 }
Christopher Dunn632c9b52015-01-23 11:09:04 -0600187 fprintf(fout, "%s\n", rewrite->c_str());
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000188 fclose(fout);
189 return 0;
Christopher Dunnf9864232007-06-14 21:01:26 +0000190}
191
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000192static std::string removeSuffix(const std::string& path,
193 const std::string& extension) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000194 if (extension.length() >= path.length())
195 return std::string("");
196 std::string suffix = path.substr(path.length() - extension.length());
197 if (suffix != extension)
198 return std::string("");
199 return path.substr(0, path.length() - extension.length());
200}
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000201
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000202static void printConfig() {
203// Print the configuration used to compile JsonCpp
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000204#if defined(JSON_NO_INT64)
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000205 printf("JSON_NO_INT64=1\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000206#else
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000207 printf("JSON_NO_INT64=0\n");
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000208#endif
209}
210
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000211static int printUsage(const char* argv[]) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000212 printf("Usage: %s [--strict] input-json-file", argv[0]);
213 return 3;
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000214}
215
Christopher Dunn79211e12015-01-23 11:27:19 -0600216struct Options
217{
218 std::string path;
219 Json::Features features;
220 bool parseOnly;
221};
222static int parseCommandLine(
223 int argc, const char* argv[], Options* opts)
224{
225 opts->parseOnly = false;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000226 if (argc < 2) {
227 return printUsage(argv);
228 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000229 int index = 1;
230 if (std::string(argv[1]) == "--json-checker") {
Christopher Dunn79211e12015-01-23 11:27:19 -0600231 opts->features = Json::Features::strictMode();
232 opts->parseOnly = true;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000233 ++index;
234 }
Christopher Dunn08cfd022015-01-23 11:33:47 -0600235 if (std::string(argv[index]) == "--json-config") {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000236 printConfig();
237 return 3;
238 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000239 if (index == argc || index + 1 < argc) {
240 return printUsage(argv);
241 }
Christopher Dunn79211e12015-01-23 11:27:19 -0600242 opts->path = argv[index];
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000243 return 0;
244}
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600245static int runTest(Options const& opts)
Christopher Dunn79211e12015-01-23 11:27:19 -0600246{
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600247 int exitCode = 0;
248
249 std::string input = readInputTestFile(opts.path.c_str());
250 if (input.empty()) {
251 printf("Failed to read input or empty input: %s\n", opts.path.c_str());
252 return 3;
253 }
254
255 std::string basePath = removeSuffix(opts.path, ".json");
256 if (!opts.parseOnly && basePath.empty()) {
257 printf("Bad input path. Path does not end with '.expected':\n%s\n",
258 opts.path.c_str());
259 return 3;
260 }
261
262 std::string const actualPath = basePath + ".actual";
263 std::string const rewritePath = basePath + ".rewrite";
264 std::string const rewriteActualPath = basePath + ".actual-rewrite";
265
266 Json::Value root;
267 exitCode = parseAndSaveValueTree(
268 input, actualPath, "input",
269 opts.features, opts.parseOnly, &root);
270 if (exitCode || opts.parseOnly) {
271 return exitCode;
272 }
273 std::string rewrite;
274 exitCode = rewriteValueTree(rewritePath, root, &rewrite);
275 if (exitCode) {
276 return exitCode;
277 }
278 Json::Value rewriteRoot;
279 exitCode = parseAndSaveValueTree(
280 rewrite, rewriteActualPath, "rewrite",
281 opts.features, opts.parseOnly, &rewriteRoot);
282 if (exitCode) {
283 return exitCode;
284 }
285 return 0;
Christopher Dunn79211e12015-01-23 11:27:19 -0600286}
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000287int main(int argc, const char* argv[]) {
Christopher Dunn79211e12015-01-23 11:27:19 -0600288 Options opts;
289 int exitCode = parseCommandLine(argc, argv, &opts);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000290 if (exitCode != 0) {
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600291 printf("Failed to parse command-line.");
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000292 return exitCode;
293 }
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000294 try {
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600295 return runTest(opts);
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000296 }
Aaron Jacobs11086dd2014-09-15 10:15:29 +1000297 catch (const std::exception& e) {
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000298 printf("Unhandled exception:\n%s\n", e.what());
Christopher Dunn58c31ac2015-01-23 11:36:55 -0600299 return 1;
Aaron Jacobs9fa4e842014-07-01 08:48:54 +1000300 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000301}