c4cfa05a5f6a231f418c54209cbdc348e0c95c93
[platform/upstream/jsoncpp.git] / 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 /* This executable is used for testing parser/writer using real JSON files.
7  */
8
9 #include <json/json.h>
10 #include <algorithm> // sort
11 #include <sstream>
12 #include <stdio.h>
13
14 #if defined(_MSC_VER) && _MSC_VER >= 1310
15 #pragma warning(disable : 4996) // disable fopen deprecation warning
16 #endif
17
18 struct Options
19 {
20   JSONCPP_STRING path;
21   Json::Features features;
22   bool parseOnly;
23   typedef JSONCPP_STRING (*writeFuncType)(Json::Value const&);
24   writeFuncType write;
25 };
26
27 static JSONCPP_STRING normalizeFloatingPointStr(double value) {
28   char buffer[32];
29 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
30   sprintf_s(buffer, sizeof(buffer), "%.16g", value);
31 #else
32   snprintf(buffer, sizeof(buffer), "%.16g", value);
33 #endif
34   buffer[sizeof(buffer) - 1] = 0;
35   JSONCPP_STRING s(buffer);
36   JSONCPP_STRING::size_type index = s.find_last_of("eE");
37   if (index != JSONCPP_STRING::npos) {
38     JSONCPP_STRING::size_type hasSign =
39         (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
40     JSONCPP_STRING::size_type exponentStartIndex = index + 1 + hasSign;
41     JSONCPP_STRING normalized = s.substr(0, exponentStartIndex);
42     JSONCPP_STRING::size_type indexDigit =
43         s.find_first_not_of('0', exponentStartIndex);
44     JSONCPP_STRING exponent = "0";
45     if (indexDigit !=
46         JSONCPP_STRING::npos) // There is an exponent different from 0
47     {
48       exponent = s.substr(indexDigit);
49     }
50     return normalized + exponent;
51   }
52   return s;
53 }
54
55 static JSONCPP_STRING readInputTestFile(const char* path) {
56   FILE* file = fopen(path, "rb");
57   if (!file)
58     return JSONCPP_STRING("");
59   fseek(file, 0, SEEK_END);
60   long const size = ftell(file);
61   unsigned long const usize = static_cast<unsigned long>(size);
62   fseek(file, 0, SEEK_SET);
63   JSONCPP_STRING text;
64   char* buffer = new char[size + 1];
65   buffer[size] = 0;
66   if (fread(buffer, 1, usize, file) == usize)
67     text = buffer;
68   fclose(file);
69   delete[] buffer;
70   return text;
71 }
72
73 static void
74 printValueTree(FILE* fout, Json::Value& value, const JSONCPP_STRING& path = ".") {
75   if (value.hasComment(Json::commentBefore)) {
76     fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
77   }
78   switch (value.type()) {
79   case Json::nullValue:
80     fprintf(fout, "%s=null\n", path.c_str());
81     break;
82   case Json::intValue:
83     fprintf(fout,
84             "%s=%s\n",
85             path.c_str(),
86             Json::valueToString(value.asLargestInt()).c_str());
87     break;
88   case Json::uintValue:
89     fprintf(fout,
90             "%s=%s\n",
91             path.c_str(),
92             Json::valueToString(value.asLargestUInt()).c_str());
93     break;
94   case Json::realValue:
95     fprintf(fout,
96             "%s=%s\n",
97             path.c_str(),
98             normalizeFloatingPointStr(value.asDouble()).c_str());
99     break;
100   case Json::stringValue:
101     fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
102     break;
103   case Json::booleanValue:
104     fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
105     break;
106   case Json::arrayValue: {
107     fprintf(fout, "%s=[]\n", path.c_str());
108     Json::ArrayIndex size = value.size();
109     for (Json::ArrayIndex index = 0; index < size; ++index) {
110       static char buffer[16];
111 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
112       sprintf_s(buffer, sizeof(buffer), "[%d]", index);
113 #else
114       snprintf(buffer, sizeof(buffer), "[%d]", index);
115 #endif
116       printValueTree(fout, value[index], path + buffer);
117     }
118   } break;
119   case Json::objectValue: {
120     fprintf(fout, "%s={}\n", path.c_str());
121     Json::Value::Members members(value.getMemberNames());
122     std::sort(members.begin(), members.end());
123     JSONCPP_STRING suffix = *(path.end() - 1) == '.' ? "" : ".";
124     for (Json::Value::Members::iterator it = members.begin();
125          it != members.end();
126          ++it) {
127       const JSONCPP_STRING name = *it;
128       printValueTree(fout, value[name], path + suffix + name);
129     }
130   } break;
131   default:
132     break;
133   }
134
135   if (value.hasComment(Json::commentAfter)) {
136     fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
137   }
138 }
139
140 static int parseAndSaveValueTree(const JSONCPP_STRING& input,
141                                  const JSONCPP_STRING& actual,
142                                  const JSONCPP_STRING& kind,
143                                  const Json::Features& features,
144                                  bool parseOnly,
145                                  Json::Value* root)
146 {
147   Json::Reader reader(features);
148   bool parsingSuccessful = reader.parse(input.data(), input.data() + input.size(), *root);
149   if (!parsingSuccessful) {
150     printf("Failed to parse %s file: \n%s\n",
151            kind.c_str(),
152            reader.getFormattedErrorMessages().c_str());
153     return 1;
154   }
155   if (!parseOnly) {
156     FILE* factual = fopen(actual.c_str(), "wt");
157     if (!factual) {
158       printf("Failed to create %s actual file.\n", kind.c_str());
159       return 2;
160     }
161     printValueTree(factual, *root);
162     fclose(factual);
163   }
164   return 0;
165 }
166 // static JSONCPP_STRING useFastWriter(Json::Value const& root) {
167 //   Json::FastWriter writer;
168 //   writer.enableYAMLCompatibility();
169 //   return writer.write(root);
170 // }
171 static JSONCPP_STRING useStyledWriter(
172     Json::Value const& root)
173 {
174   Json::StyledWriter writer;
175   return writer.write(root);
176 }
177 static JSONCPP_STRING useStyledStreamWriter(
178     Json::Value const& root)
179 {
180   Json::StyledStreamWriter writer;
181   JSONCPP_OSTRINGSTREAM sout;
182   writer.write(sout, root);
183   return sout.str();
184 }
185 static JSONCPP_STRING useBuiltStyledStreamWriter(
186     Json::Value const& root)
187 {
188   Json::StreamWriterBuilder builder;
189   return Json::writeString(builder, root);
190 }
191 static int rewriteValueTree(
192     const JSONCPP_STRING& rewritePath,
193     const Json::Value& root,
194     Options::writeFuncType write,
195     JSONCPP_STRING* rewrite)
196 {
197   *rewrite = write(root);
198   FILE* fout = fopen(rewritePath.c_str(), "wt");
199   if (!fout) {
200     printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
201     return 2;
202   }
203   fprintf(fout, "%s\n", rewrite->c_str());
204   fclose(fout);
205   return 0;
206 }
207
208 static JSONCPP_STRING removeSuffix(const JSONCPP_STRING& path,
209                                 const JSONCPP_STRING& extension) {
210   if (extension.length() >= path.length())
211     return JSONCPP_STRING("");
212   JSONCPP_STRING suffix = path.substr(path.length() - extension.length());
213   if (suffix != extension)
214     return JSONCPP_STRING("");
215   return path.substr(0, path.length() - extension.length());
216 }
217
218 static void printConfig() {
219 // Print the configuration used to compile JsonCpp
220 #if defined(JSON_NO_INT64)
221   printf("JSON_NO_INT64=1\n");
222 #else
223   printf("JSON_NO_INT64=0\n");
224 #endif
225 }
226
227 static int printUsage(const char* argv[]) {
228   printf("Usage: %s [--strict] input-json-file", argv[0]);
229   return 3;
230 }
231
232 static int parseCommandLine(
233     int argc, const char* argv[], Options* opts)
234 {
235   opts->parseOnly = false;
236   opts->write = &useStyledWriter;
237   if (argc < 2) {
238     return printUsage(argv);
239   }
240   int index = 1;
241   if (JSONCPP_STRING(argv[index]) == "--json-checker") {
242     opts->features = Json::Features::strictMode();
243     opts->parseOnly = true;
244     ++index;
245   }
246   if (JSONCPP_STRING(argv[index]) == "--json-config") {
247     printConfig();
248     return 3;
249   }
250   if (JSONCPP_STRING(argv[index]) == "--json-writer") {
251     ++index;
252     JSONCPP_STRING const writerName(argv[index++]);
253     if (writerName == "StyledWriter") {
254       opts->write = &useStyledWriter;
255     } else if (writerName == "StyledStreamWriter") {
256       opts->write = &useStyledStreamWriter;
257     } else if (writerName == "BuiltStyledStreamWriter") {
258       opts->write = &useBuiltStyledStreamWriter;
259     } else {
260       printf("Unknown '--json-writer %s'\n", writerName.c_str());
261       return 4;
262     }
263   }
264   if (index == argc || index + 1 < argc) {
265     return printUsage(argv);
266   }
267   opts->path = argv[index];
268   return 0;
269 }
270 static int runTest(Options const& opts)
271 {
272   int exitCode = 0;
273
274   JSONCPP_STRING input = readInputTestFile(opts.path.c_str());
275   if (input.empty()) {
276     printf("Failed to read input or empty input: %s\n", opts.path.c_str());
277     return 3;
278   }
279
280   JSONCPP_STRING basePath = removeSuffix(opts.path, ".json");
281   if (!opts.parseOnly && basePath.empty()) {
282     printf("Bad input path. Path does not end with '.expected':\n%s\n",
283             opts.path.c_str());
284     return 3;
285   }
286
287   JSONCPP_STRING const actualPath = basePath + ".actual";
288   JSONCPP_STRING const rewritePath = basePath + ".rewrite";
289   JSONCPP_STRING const rewriteActualPath = basePath + ".actual-rewrite";
290
291   Json::Value root;
292   exitCode = parseAndSaveValueTree(
293       input, actualPath, "input",
294       opts.features, opts.parseOnly, &root);
295   if (exitCode || opts.parseOnly) {
296     return exitCode;
297   }
298   JSONCPP_STRING rewrite;
299   exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
300   if (exitCode) {
301     return exitCode;
302   }
303   Json::Value rewriteRoot;
304   exitCode = parseAndSaveValueTree(
305       rewrite, rewriteActualPath, "rewrite",
306       opts.features, opts.parseOnly, &rewriteRoot);
307   if (exitCode) {
308     return exitCode;
309   }
310   return 0;
311 }
312 int main(int argc, const char* argv[]) {
313   Options opts;
314   try {
315   int exitCode = parseCommandLine(argc, argv, &opts);
316   if (exitCode != 0) {
317     printf("Failed to parse command-line.");
318     return exitCode;
319   }
320     return runTest(opts);
321   }
322   catch (const std::exception& e) {
323     printf("Unhandled exception:\n%s\n", e.what());
324     return 1;
325   }
326 }