1 #include "perftest.h"
2 
3 #if TEST_RAPIDJSON
4 
5 #include "rapidjson/schema.h"
6 #include <ctime>
7 #include <string>
8 #include <vector>
9 
10 #define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0])
11 
12 using namespace rapidjson;
13 
14 template <typename Allocator>
ReadFile(const char * filename,Allocator & allocator)15 static char* ReadFile(const char* filename, Allocator& allocator) {
16     const char *paths[] = {
17         "",
18         "bin/",
19         "../bin/",
20         "../../bin/",
21         "../../../bin/"
22     };
23     char buffer[1024];
24     FILE *fp = 0;
25     for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
26         sprintf(buffer, "%s%s", paths[i], filename);
27         fp = fopen(buffer, "rb");
28         if (fp)
29             break;
30     }
31 
32     if (!fp)
33         return 0;
34 
35     fseek(fp, 0, SEEK_END);
36     size_t length = static_cast<size_t>(ftell(fp));
37     fseek(fp, 0, SEEK_SET);
38     char* json = reinterpret_cast<char*>(allocator.Malloc(length + 1));
39     size_t readLength = fread(json, 1, length, fp);
40     json[readLength] = '\0';
41     fclose(fp);
42     return json;
43 }
44 
45 class Schema : public PerfTest {
46 public:
Schema()47     Schema() {}
48 
SetUp()49     virtual void SetUp() {
50         PerfTest::SetUp();
51 
52         const char* filenames[] = {
53             "additionalItems.json",
54             "additionalProperties.json",
55             "allOf.json",
56             "anyOf.json",
57             "default.json",
58             "definitions.json",
59             "dependencies.json",
60             "enum.json",
61             "items.json",
62             "maximum.json",
63             "maxItems.json",
64             "maxLength.json",
65             "maxProperties.json",
66             "minimum.json",
67             "minItems.json",
68             "minLength.json",
69             "minProperties.json",
70             "multipleOf.json",
71             "not.json",
72             "oneOf.json",
73             "pattern.json",
74             "patternProperties.json",
75             "properties.json",
76             "ref.json",
77             "refRemote.json",
78             "required.json",
79             "type.json",
80             "uniqueItems.json"
81         };
82 
83         char jsonBuffer[65536];
84         MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer));
85 
86         for (size_t i = 0; i < ARRAY_SIZE(filenames); i++) {
87             char filename[FILENAME_MAX];
88             sprintf(filename, "jsonschema/tests/draft4/%s", filenames[i]);
89             char* json = ReadFile(filename, jsonAllocator);
90             if (!json) {
91                 printf("json test suite file %s not found", filename);
92                 return;
93             }
94 
95             Document d;
96             d.Parse(json);
97             if (d.HasParseError()) {
98                 printf("json test suite file %s has parse error", filename);
99                 return;
100             }
101 
102             for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) {
103                 std::string schemaDescription = (*schemaItr)["description"].GetString();
104                 if (IsExcludeTestSuite(schemaDescription))
105                     continue;
106 
107                 TestSuite* ts = new TestSuite;
108                 ts->schema = new SchemaDocument((*schemaItr)["schema"]);
109 
110                 const Value& tests = (*schemaItr)["tests"];
111                 for (Value::ConstValueIterator testItr = tests.Begin(); testItr != tests.End(); ++testItr) {
112                     if (IsExcludeTest(schemaDescription + ", " + (*testItr)["description"].GetString()))
113                         continue;
114 
115                     Document* d2 = new Document;
116                     d2->CopyFrom((*testItr)["data"], d2->GetAllocator());
117                     ts->tests.push_back(d2);
118                 }
119                 testSuites.push_back(ts);
120             }
121         }
122     }
123 
TearDown()124     virtual void TearDown() {
125         PerfTest::TearDown();
126         for (TestSuiteList::const_iterator itr = testSuites.begin(); itr != testSuites.end(); ++itr)
127             delete *itr;
128         testSuites.clear();
129     }
130 
131 private:
132     // Using the same exclusion in https://github.com/json-schema/JSON-Schema-Test-Suite
IsExcludeTestSuite(const std::string & description)133     static bool IsExcludeTestSuite(const std::string& description) {
134         const char* excludeTestSuites[] = {
135             //lost failing these tests
136             "remote ref",
137             "remote ref, containing refs itself",
138             "fragment within remote ref",
139             "ref within remote ref",
140             "change resolution scope",
141             // these below were added to get jsck in the benchmarks)
142             "uniqueItems validation",
143             "valid definition",
144             "invalid definition"
145         };
146 
147         for (size_t i = 0; i < ARRAY_SIZE(excludeTestSuites); i++)
148             if (excludeTestSuites[i] == description)
149                 return true;
150         return false;
151     }
152 
153     // Using the same exclusion in https://github.com/json-schema/JSON-Schema-Test-Suite
IsExcludeTest(const std::string & description)154     static bool IsExcludeTest(const std::string& description) {
155         const char* excludeTests[] = {
156             //lots of validators fail these
157             "invalid definition, invalid definition schema",
158             "maxLength validation, two supplementary Unicode code points is long enough",
159             "minLength validation, one supplementary Unicode code point is not long enough",
160             //this is to get tv4 in the benchmarks
161             "heterogeneous enum validation, something else is invalid"
162         };
163 
164         for (size_t i = 0; i < ARRAY_SIZE(excludeTests); i++)
165             if (excludeTests[i] == description)
166                 return true;
167         return false;
168     }
169 
170     Schema(const Schema&);
171     Schema& operator=(const Schema&);
172 
173 protected:
174     typedef std::vector<Document*> DocumentList;
175 
176     struct TestSuite {
TestSuiteSchema::TestSuite177         TestSuite() : schema() {}
~TestSuiteSchema::TestSuite178         ~TestSuite() {
179             delete schema;
180             for (DocumentList::iterator itr = tests.begin(); itr != tests.end(); ++itr)
181                 delete *itr;
182         }
183         SchemaDocument* schema;
184         DocumentList tests;
185     };
186 
187     typedef std::vector<TestSuite* > TestSuiteList;
188     TestSuiteList testSuites;
189 };
190 
TEST_F(Schema,TestSuite)191 TEST_F(Schema, TestSuite) {
192     char validatorBuffer[65536];
193     MemoryPoolAllocator<> validatorAllocator(validatorBuffer, sizeof(validatorBuffer));
194 
195     const int trialCount = 100000;
196     int testCount = 0;
197     clock_t start = clock();
198     for (int i = 0; i < trialCount; i++) {
199         for (TestSuiteList::const_iterator itr = testSuites.begin(); itr != testSuites.end(); ++itr) {
200             const TestSuite& ts = **itr;
201             GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> >  validator(*ts.schema, &validatorAllocator);
202             for (DocumentList::const_iterator testItr = ts.tests.begin(); testItr != ts.tests.end(); ++testItr) {
203                 validator.Reset();
204                 (*testItr)->Accept(validator);
205                 testCount++;
206             }
207             validatorAllocator.Clear();
208         }
209     }
210     clock_t end = clock();
211     double duration = double(end - start) / CLOCKS_PER_SEC;
212     printf("%d trials in %f s -> %f trials per sec\n", trialCount, duration, trialCount / duration);
213     printf("%d tests per trial\n", testCount / trialCount);
214 }
215 
216 #endif
217