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