Merge pull request #136 from cdunn2001/test-both-styled-writers

Test both styled writers

Not only does this now test StyledStreamWriter the same way as StyledWriter, but it also makes the former work more like the latter, indenting separate lines of a comment before a value. Might break some user tests (as `operator<<()` uses `StyledStreamWriter`) but basically a harmless improvement.

All tests pass.
This commit is contained in:
Christopher Dunn 2015-01-23 13:55:45 -06:00
commit ddb4ff7dec
3 changed files with 138 additions and 73 deletions

View File

@ -8,12 +8,22 @@
#include <json/json.h> #include <json/json.h>
#include <algorithm> // sort #include <algorithm> // sort
#include <sstream>
#include <stdio.h> #include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER >= 1310 #if defined(_MSC_VER) && _MSC_VER >= 1310
#pragma warning(disable : 4996) // disable fopen deprecation warning #pragma warning(disable : 4996) // disable fopen deprecation warning
#endif #endif
struct Options
{
std::string path;
Json::Features features;
bool parseOnly;
typedef std::string (*writeFuncType)(Json::Value const&);
writeFuncType write;
};
static std::string normalizeFloatingPointStr(double value) { static std::string normalizeFloatingPointStr(double value) {
char buffer[32]; char buffer[32];
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
@ -129,11 +139,12 @@ printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") {
static int parseAndSaveValueTree(const std::string& input, static int parseAndSaveValueTree(const std::string& input,
const std::string& actual, const std::string& actual,
const std::string& kind, const std::string& kind,
Json::Value& root,
const Json::Features& features, const Json::Features& features,
bool parseOnly) { bool parseOnly,
Json::Value* root)
{
Json::Reader reader(features); Json::Reader reader(features);
bool parsingSuccessful = reader.parse(input, root); bool parsingSuccessful = reader.parse(input, *root);
if (!parsingSuccessful) { if (!parsingSuccessful) {
printf("Failed to parse %s file: \n%s\n", printf("Failed to parse %s file: \n%s\n",
kind.c_str(), kind.c_str(),
@ -147,25 +158,43 @@ static int parseAndSaveValueTree(const std::string& input,
printf("Failed to create %s actual file.\n", kind.c_str()); printf("Failed to create %s actual file.\n", kind.c_str());
return 2; return 2;
} }
printValueTree(factual, root); printValueTree(factual, *root);
fclose(factual); fclose(factual);
} }
return 0; return 0;
} }
// static std::string useFastWriter(Json::Value const& root) {
static int rewriteValueTree(const std::string& rewritePath, // Json::FastWriter writer;
const Json::Value& root, // writer.enableYAMLCompatibility();
std::string& rewrite) { // return writer.write(root);
// Json::FastWriter writer; // }
// writer.enableYAMLCompatibility(); static std::string useStyledWriter(
Json::Value const& root)
{
Json::StyledWriter writer; Json::StyledWriter writer;
rewrite = writer.write(root); return writer.write(root);
}
static std::string useStyledStreamWriter(
Json::Value const& root)
{
Json::StyledStreamWriter writer;
std::ostringstream sout;
writer.write(sout, root);
return sout.str();
}
static int rewriteValueTree(
const std::string& rewritePath,
const Json::Value& root,
Options::writeFuncType write,
std::string* rewrite)
{
*rewrite = write(root);
FILE* fout = fopen(rewritePath.c_str(), "wt"); FILE* fout = fopen(rewritePath.c_str(), "wt");
if (!fout) { if (!fout) {
printf("Failed to create rewrite file: %s\n", rewritePath.c_str()); printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
return 2; return 2;
} }
fprintf(fout, "%s\n", rewrite.c_str()); fprintf(fout, "%s\n", rewrite->c_str());
fclose(fout); fclose(fout);
return 0; return 0;
} }
@ -194,84 +223,96 @@ static int printUsage(const char* argv[]) {
return 3; return 3;
} }
int parseCommandLine(int argc, static int parseCommandLine(
const char* argv[], int argc, const char* argv[], Options* opts)
Json::Features& features, {
std::string& path, opts->parseOnly = false;
bool& parseOnly) { opts->write = &useStyledWriter;
parseOnly = false;
if (argc < 2) { if (argc < 2) {
return printUsage(argv); return printUsage(argv);
} }
int index = 1; int index = 1;
if (std::string(argv[1]) == "--json-checker") { if (std::string(argv[index]) == "--json-checker") {
features = Json::Features::strictMode(); opts->features = Json::Features::strictMode();
parseOnly = true; opts->parseOnly = true;
++index; ++index;
} }
if (std::string(argv[index]) == "--json-config") {
if (std::string(argv[1]) == "--json-config") {
printConfig(); printConfig();
return 3; return 3;
} }
if (std::string(argv[index]) == "--json-writer") {
++index;
std::string const writerName(argv[index++]);
if (writerName == "StyledWriter") {
opts->write = &useStyledWriter;
} else if (writerName == "StyledStreamWriter") {
opts->write = &useStyledStreamWriter;
} else {
printf("Unknown '--json-writer %s'\n", writerName.c_str());
return 4;
}
}
if (index == argc || index + 1 < argc) { if (index == argc || index + 1 < argc) {
return printUsage(argv); return printUsage(argv);
} }
opts->path = argv[index];
path = argv[index];
return 0; return 0;
} }
static int runTest(Options const& opts)
{
int exitCode = 0;
int main(int argc, const char* argv[]) { std::string input = readInputTestFile(opts.path.c_str());
std::string path;
Json::Features features;
bool parseOnly;
int exitCode = parseCommandLine(argc, argv, features, path, parseOnly);
if (exitCode != 0) {
return exitCode;
}
try {
std::string input = readInputTestFile(path.c_str());
if (input.empty()) { if (input.empty()) {
printf("Failed to read input or empty input: %s\n", path.c_str()); printf("Failed to read input or empty input: %s\n", opts.path.c_str());
return 3; return 3;
} }
std::string basePath = removeSuffix(argv[1], ".json"); std::string basePath = removeSuffix(opts.path, ".json");
if (!parseOnly && basePath.empty()) { if (!opts.parseOnly && basePath.empty()) {
printf("Bad input path. Path does not end with '.expected':\n%s\n", printf("Bad input path. Path does not end with '.expected':\n%s\n",
path.c_str()); opts.path.c_str());
return 3; return 3;
} }
std::string actualPath = basePath + ".actual"; std::string const actualPath = basePath + ".actual";
std::string rewritePath = basePath + ".rewrite"; std::string const rewritePath = basePath + ".rewrite";
std::string rewriteActualPath = basePath + ".actual-rewrite"; std::string const rewriteActualPath = basePath + ".actual-rewrite";
Json::Value root; Json::Value root;
exitCode = parseAndSaveValueTree( exitCode = parseAndSaveValueTree(
input, actualPath, "input", root, features, parseOnly); input, actualPath, "input",
if (exitCode == 0 && !parseOnly) { opts.features, opts.parseOnly, &root);
if (exitCode || opts.parseOnly) {
return exitCode;
}
std::string rewrite; std::string rewrite;
exitCode = rewriteValueTree(rewritePath, root, rewrite); exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
if (exitCode == 0) { if (exitCode) {
return exitCode;
}
Json::Value rewriteRoot; Json::Value rewriteRoot;
exitCode = parseAndSaveValueTree(rewrite, exitCode = parseAndSaveValueTree(
rewriteActualPath, rewrite, rewriteActualPath, "rewrite",
"rewrite", opts.features, opts.parseOnly, &rewriteRoot);
rewriteRoot, if (exitCode) {
features, return exitCode;
parseOnly);
} }
return 0;
}
int main(int argc, const char* argv[]) {
Options opts;
int exitCode = parseCommandLine(argc, argv, &opts);
if (exitCode != 0) {
printf("Failed to parse command-line.");
return exitCode;
} }
try {
return runTest(opts);
} }
catch (const std::exception& e) { catch (const std::exception& e) {
printf("Unhandled exception:\n%s\n", e.what()); printf("Unhandled exception:\n%s\n", e.what());
exitCode = 1; return 1;
} }
return exitCode;
} }

View File

@ -628,7 +628,20 @@ void StyledStreamWriter::unindent() {
void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
if (!root.hasComment(commentBefore)) if (!root.hasComment(commentBefore))
return; return;
*document_ << root.getComment(commentBefore);
*document_ << "\n";
writeIndent();
const std::string& comment = root.getComment(commentBefore);
std::string::const_iterator iter = comment.begin();
while (iter != comment.end()) {
*document_ << *iter;
if (*iter == '\n' &&
(iter != comment.end() && *(iter + 1) == '/'))
writeIndent();
++iter;
}
// Comments are stripped of trailing newlines, so add one here
*document_ << "\n"; *document_ << "\n";
} }

View File

@ -14,6 +14,7 @@ def getStatusOutput(cmd):
Return int, unicode (for both Python 2 and 3). Return int, unicode (for both Python 2 and 3).
Note: os.popen().close() would return None for 0. Note: os.popen().close() would return None for 0.
""" """
print(cmd, file=sys.stderr)
pipe = os.popen(cmd) pipe = os.popen(cmd)
process_output = pipe.read() process_output = pipe.read()
try: try:
@ -57,7 +58,8 @@ def safeReadFile( path ):
return '<File "%s" is missing: %s>' % (path,e) return '<File "%s" is missing: %s>' % (path,e)
def runAllTests( jsontest_executable_path, input_dir = None, def runAllTests( jsontest_executable_path, input_dir = None,
use_valgrind=False, with_json_checker=False ): use_valgrind=False, with_json_checker=False,
writerClass='StyledWriter'):
if not input_dir: if not input_dir:
input_dir = os.path.join( os.getcwd(), 'data' ) input_dir = os.path.join( os.getcwd(), 'data' )
tests = glob( os.path.join( input_dir, '*.json' ) ) tests = glob( os.path.join( input_dir, '*.json' ) )
@ -72,6 +74,7 @@ def runAllTests( jsontest_executable_path, input_dir = None,
is_json_checker_test = (input_path in test_jsonchecker) or expect_failure is_json_checker_test = (input_path in test_jsonchecker) or expect_failure
print('TESTING:', input_path, end=' ') print('TESTING:', input_path, end=' ')
options = is_json_checker_test and '--json-checker' or '' options = is_json_checker_test and '--json-checker' or ''
options += ' --json-writer %s'%writerClass
cmd = '%s%s %s "%s"' % ( cmd = '%s%s %s "%s"' % (
valgrind_path, jsontest_executable_path, options, valgrind_path, jsontest_executable_path, options,
input_path) input_path)
@ -145,7 +148,15 @@ def main():
else: else:
input_path = None input_path = None
status = runAllTests( jsontest_executable_path, input_path, status = runAllTests( jsontest_executable_path, input_path,
use_valgrind=options.valgrind, with_json_checker=options.with_json_checker ) use_valgrind=options.valgrind,
with_json_checker=options.with_json_checker,
writerClass='StyledWriter')
if status:
sys.exit( status )
status = runAllTests( jsontest_executable_path, input_path,
use_valgrind=options.valgrind,
with_json_checker=options.with_json_checker,
writerClass='StyledStreamWriter')
sys.exit( status ) sys.exit( status )
if __name__ == '__main__': if __name__ == '__main__':