Add Bazel tests (#1601)

* Expose JSON_USE_EXCEPTION and JSON_HAS_INT64 as Bazel config flags with defaults that match the existing Bazel build.
Switch //:jsoncpp from using copts ro defines for JSON_USE_EXCEPTION and JSON_HAS_INT64 so that rules that depend on it get the same config.
Make src/test_lib_json/fuzz.cpp respect JSON_USE_EXCEPTION.

* #ifdef stuff that should only be used with JSON_USE_EXCEPTION.

* Modify runjsontests.py to allow passing a single test case file.

* Add tests to the Bazel builds.

* Reverse the polarity to fix a bug.

---------

Co-authored-by: Jordan Bayles <[email protected]>
diff --git a/src/jsontestrunner/BUILD.bazel b/src/jsontestrunner/BUILD.bazel
new file mode 100644
index 0000000..543bc5d
--- /dev/null
+++ b/src/jsontestrunner/BUILD.bazel
@@ -0,0 +1,6 @@
+cc_binary(
+    name = "jsontestrunner",
+    srcs = ["main.cpp"],
+    deps = ["//:jsoncpp"],
+    visibility = ["//test:__pkg__"],
+)
diff --git a/src/test_lib_json/BUILD.bazel b/src/test_lib_json/BUILD.bazel
new file mode 100644
index 0000000..7e83f52
--- /dev/null
+++ b/src/test_lib_json/BUILD.bazel
@@ -0,0 +1,11 @@
+cc_test(
+    name = "jsoncpp_test",
+    srcs = [
+        "jsontest.cpp",
+        "jsontest.h",
+        "main.cpp",
+        "fuzz.h",
+        "fuzz.cpp",
+        ],
+    deps = ["//:jsoncpp"],
+)
diff --git a/test/BUILD.bazel b/test/BUILD.bazel
new file mode 100644
index 0000000..269cd86
--- /dev/null
+++ b/test/BUILD.bazel
@@ -0,0 +1,20 @@
+filegroup(
+    name = "expected",
+    srcs = glob(["data/**", "jsonchecker/**"], exclude=["**/*.json"]),
+)
+
+[py_test(
+    name = "runjson_%s_test" % "_".join(f.split("/")),
+    srcs = ["runjsontests.py"],
+    main = "runjsontests.py",
+    args = [
+        "--with-json-checker",
+        "$(location //src/jsontestrunner:jsontestrunner)",
+        "$(location :%s)" % f,
+    ],
+    data = [
+        "//src/jsontestrunner:jsontestrunner",
+        ":expected",
+        ":%s" % f,
+    ],
+) for f in glob(["**/*.json"])]
diff --git a/test/runjsontests.py b/test/runjsontests.py
index 49cc7a9..14275ec 100644
--- a/test/runjsontests.py
+++ b/test/runjsontests.py
@@ -66,38 +66,51 @@
     def __init__(self, msg):
         super(Exception, self).__init__(msg)
 
-def runAllTests(jsontest_executable_path, input_dir = None,
+def runAllTests(jsontest_executable_path, input_path = None,
                  use_valgrind=False, with_json_checker=False,
                  writerClass='StyledWriter'):
-    if not input_dir:
-        input_dir = os.path.join(os.getcwd(), 'data')
-    tests = glob(os.path.join(input_dir, '*.json'))
-    if with_json_checker:
-        all_tests = glob(os.path.join(input_dir, '../jsonchecker', '*.json'))
-        # These tests fail with strict json support, but pass with JsonCPP's
-        # extra leniency features. When adding a new exclusion to this list,
-        # remember to add the test's number and reasoning here:
-        known = ["fail{}.json".format(n) for n in [
-            4, 9, # fail because we allow trailing commas
-            7,    # fails because we allow commas after close
-            8,    # fails because we allow extra close
-            10,   # fails because we allow extra values after close
-            13,   # fails because we allow leading zeroes in numbers
-            18,   # fails because we allow deeply nested values
-            25,   # fails because we allow tab characters in strings
-            27,   # fails because we allow string line breaks
-        ]]
-        test_jsonchecker = [ test for test in all_tests
-                             if os.path.basename(test) not in known]
+    if not input_path:
+        input_path = os.path.join(os.getcwd(), 'data')
 
+    if os.path.isdir(input_path):
+        tests = [
+            os.path.normpath(os.path.abspath(test))
+            for test in glob(os.path.join(input_path, '*.json'))
+        ]
+
+        if with_json_checker:
+            tests += [
+                os.path.normpath(os.path.abspath(test))
+                for test in glob(os.path.join(input_path, '../jsonchecker', '*.json'))
+            ]
     else:
-        test_jsonchecker = []
+        tests = [input_path]
+
+    # These tests fail with strict json support, but pass with JsonCPP's
+    # extra leniency features. When adding a new exclusion to this list,
+    # remember to add the test's number and reasoning here:
+    known = ["fail{}.json".format(n) for n in [
+        4, 9, # fail because we allow trailing commas
+        7,    # fails because we allow commas after close
+        8,    # fails because we allow extra close
+        10,   # fails because we allow extra values after close
+        13,   # fails because we allow leading zeroes in numbers
+        18,   # fails because we allow deeply nested values
+        25,   # fails because we allow tab characters in strings
+        27,   # fails because we allow string line breaks
+    ]]
+
+    tests = [
+        test for test in tests
+        if os.path.basename(test) not in known or
+            os.path.basename(os.path.dirname(test)) != "jsonchecker"
+    ]
 
     failed_tests = []
     valgrind_path = use_valgrind and VALGRIND_CMD or ''
-    for input_path in tests + test_jsonchecker:
+    for input_path in tests:
         expect_failure = os.path.basename(input_path).startswith('fail')
-        is_json_checker_test = input_path in test_jsonchecker
+        is_json_checker_test = os.path.basename(os.path.dirname(input_path)) == "jsonchecker"
         is_parse_only = is_json_checker_test or expect_failure
         is_strict_test = ('_strict_' in os.path.basename(input_path)) or is_json_checker_test
         print('TESTING:', input_path, end=' ')