/* * JSON schema validator for JSON for modern C++ * * Copyright (c) 2016-2019 Patrick Boettcher . * * SPDX-License-Identifier: MIT * */ #include "nlohmann/json-schema.hpp" #include #include #include using nlohmann::json; using nlohmann::json_uri; using nlohmann::json_schema::json_validator; static void loader(const json_uri &uri, json &schema) { if (uri.location() == "http://json-schema.org/draft-07/schema") { schema = nlohmann::json_schema::draft7_schema_builtin; return; } std::string fn = JSON_SCHEMA_TEST_SUITE_PATH; fn += "/remotes"; fn += uri.path(); std::cerr << fn << "\n"; std::fstream s(fn.c_str()); if (!s.good()) throw std::invalid_argument("could not open " + uri.url() + " for schema loading\n"); try { s >> schema; } catch (std::exception &e) { throw e; } } // from here // https://stackoverflow.com/a/34571089/880584 static std::string base64_decode(const std::string &in) { std::string out; std::vector T(256, -1); for (int i = 0; i < 64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; unsigned val = 0; int valb = -8; for (uint8_t c : in) { if (c == '=') break; if (T[c] == -1) { throw std::invalid_argument("base64-decode: unexpected character in encode string: '" + std::string(1, c) + "'"); } val = (val << 6) + T[c]; valb += 6; if (valb >= 0) { out.push_back(char((val >> valb) & 0xFF)); valb -= 8; } } return out; } static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance) { std::string content = instance; if (contentEncoding == "base64") content = base64_decode(instance); else if (contentEncoding != "") throw std::invalid_argument("unable to check for contentEncoding '" + contentEncoding + "'"); if (contentMediaType == "application/json") auto dummy = json::parse(content); // throws if conversion fails else if (contentMediaType != "") throw std::invalid_argument("unable to check for contentMediaType '" + contentMediaType + "'"); } int main(void) { json validation; // a validation case following the JSON-test-suite-schema try { std::cin >> validation; } catch (std::exception &e) { std::cout << e.what() << "\n"; return EXIT_FAILURE; } size_t total_failed = 0, total = 0; for (auto &test_group : validation) { size_t group_failed = 0, group_total = 0; std::cout << "Testing Group " << test_group["description"] << "\n"; const auto &schema = test_group["schema"]; json_validator validator(loader, nlohmann::json_schema::default_string_format_check, content); validator.set_root_schema(schema); for (auto &test_case : test_group["tests"]) { std::cout << " Testing Case " << test_case["description"] << "\n"; bool valid = true; try { validator.validate(test_case["data"]); } catch (const std::out_of_range &e) { valid = false; std::cout << " Test Case Exception (out of range): " << e.what() << "\n"; } catch (const std::invalid_argument &e) { valid = false; std::cout << " Test Case Exception (invalid argument): " << e.what() << "\n"; } catch (const std::logic_error &e) { valid = !test_case["valid"]; /* force test-case failure */ std::cout << " Not yet implemented: " << e.what() << "\n"; } if (valid == test_case["valid"]) std::cout << " --> Test Case exited with " << valid << " as expected.\n"; else { group_failed++; std::cout << " --> Test Case exited with " << valid << " NOT expected.\n"; } group_total++; std::cout << "\n"; } total_failed += group_failed; total += group_total; std::cout << "Group RESULT: " << test_group["description"] << " " << (group_total - group_failed) << " of " << group_total << " have succeeded - " << group_failed << " failed\n"; std::cout << "-------------\n"; } std::cout << "Total RESULT: " << (total - total_failed) << " of " << total << " have succeeded - " << total_failed << " failed\n"; return total_failed; }