RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 /* openrtb_bid_request_test.cc 00002 Jeremy Barnes, 19 February 2013 00003 Copyright (c) 2013 Datacratic Inc. All rights reserved. 00004 00005 Test cases for the OpenRTB bid request parser. 00006 */ 00007 00008 00009 #define BOOST_TEST_MAIN 00010 #define BOOST_TEST_DYN_LINK 00011 00012 #include <boost/test/unit_test.hpp> 00013 #include "rtbkit/plugins/bid_request/openrtb_bid_request.h" 00014 #include "soa/types/json_parsing.h" 00015 #include "openrtb/openrtb_parsing.h" 00016 #include "jml/utils/filter_streams.h" 00017 00018 using namespace std; 00019 using namespace ML; 00020 using namespace Datacratic; 00021 using namespace RTBKIT; 00022 00023 vector<string> samples = { 00024 "rtbkit/plugins/bid_request/testing/openrtb1_req.json", 00025 "rtbkit/plugins/bid_request/testing/openrtb2_req.json", 00026 "rtbkit/plugins/bid_request/testing/openrtb_banner.json", 00027 "rtbkit/plugins/bid_request/testing/openrtb_expandable_creative.json", 00028 "rtbkit/plugins/bid_request/testing/openrtb_mobile.json", 00029 "rtbkit/plugins/bid_request/testing/openrtb_video.json", 00030 "rtbkit/plugins/bid_request/testing/rubicon_banner1.json", 00031 "rtbkit/plugins/bid_request/testing/rubicon_banner2.json", 00032 "rtbkit/plugins/bid_request/testing/rubicon_banner3.json", 00033 "rtbkit/plugins/bid_request/testing/rubicon_banner4.json", 00034 "rtbkit/plugins/bid_request/testing/rubicon_desktop.json", 00035 "rtbkit/plugins/bid_request/testing/rubicon_mobile_app.json", 00036 "rtbkit/plugins/bid_request/testing/rubicon_mobile_web.json", 00037 "rtbkit/plugins/bid_request/testing/rubicon_test1.json" 00038 }; 00039 00040 std::string loadFile(const std::string & filename) 00041 { 00042 ML::filter_istream stream(filename); 00043 00044 string result; 00045 00046 while (stream) { 00047 string line; 00048 getline(stream, line); 00049 result += line + "\n"; 00050 } 00051 00052 return result; 00053 } 00054 00055 void parseBidRequest(const std::string & filename) 00056 { 00057 cerr << endl << "loading " << filename << endl; 00058 StreamingJsonParsingContext context; 00059 context.init(filename); 00060 00061 OpenRTB::BidRequest req; 00062 DefaultDescription<OpenRTB::BidRequest> desc; 00063 desc.parseJson(&req, context); 00064 00065 if (!req.unparseable.isNull()) 00066 cerr << "unparseable:" << req.unparseable << endl; 00067 00068 StreamJsonPrintingContext printContext(cout); 00069 desc.printJson(&req, printContext); 00070 } 00071 00072 BOOST_AUTO_TEST_CASE( test_parse_openrtb_sample_requests ) 00073 { 00074 for (auto req: samples) 00075 parseBidRequest(req); 00076 } 00077 00078 void testBidRequest(const std::string & filename) 00079 { 00080 cerr << endl << "loading " << filename << endl; 00081 00082 ML::Parse_Context context(filename); 00083 00084 auto res = OpenRtbBidRequestParser::parseBidRequest(context, "test", "test"); 00085 00086 cerr << res->toJson() << endl; 00087 00088 } 00089 00090 BOOST_AUTO_TEST_CASE( test_openrtb_sample_requests ) 00091 { 00092 for (auto s: samples) 00093 testBidRequest(s); 00094 } 00095 00096 bool jsonDiff(const Json::Value & v1, const Json::Value & v2, 00097 bool oneOnly = false, 00098 string path = "") 00099 { 00100 bool result = true; 00101 00102 if (v1.type() != v2.type()) { 00103 cerr << path << ": different types: " 00104 << v1.toString() << " versus " 00105 << v2.toString() << endl; 00106 return false; 00107 } 00108 00109 switch (v1.type()) { 00110 case Json::arrayValue: 00111 if (v1.size() != v2.size()) { 00112 cerr << path << ": differing lengths: " << v1.size() << " versus " 00113 << v2.size() << endl; 00114 result = false; 00115 if (oneOnly) return false; 00116 } 00117 for (unsigned i = 0; i < v1.size(); ++i) { 00118 if (!jsonDiff(v1[i], v2[i], oneOnly, 00119 path + ML::format("[%d]", i))) { 00120 result = false; 00121 if (oneOnly) return false; 00122 } 00123 } 00124 return result; 00125 00126 case Json::objectValue: { 00127 auto m1 = v1.getMemberNames(); 00128 auto m2 = v2.getMemberNames(); 00129 00130 std::sort(m1.begin(), m1.end()); 00131 std::sort(m2.begin(), m2.end()); 00132 00133 vector<string> intersection, only1, only2; 00134 std::set_intersection(m1.begin(), m1.end(), 00135 m2.begin(), m2.end(), 00136 back_inserter(intersection)); 00137 std::set_difference(m1.begin(), m1.end(), 00138 m2.begin(), m2.end(), 00139 back_inserter(only1)); 00140 std::set_difference(m2.begin(), m2.end(), 00141 m1.begin(), m1.end(), 00142 back_inserter(only2)); 00143 00144 if (!only1.empty()) { 00145 cerr << path << ": keys only present in first field: " 00146 << only1 << endl; 00147 result = false; 00148 if (oneOnly) return false; 00149 } 00150 if (!only2.empty()) { 00151 cerr << path << ": keys only present in second field: " 00152 << only1 << endl; 00153 result = false; 00154 if (oneOnly) return false; 00155 } 00156 00157 for (auto f: intersection) { 00158 if (!jsonDiff(v1[f], v2[f], oneOnly, path + "." + f)) { 00159 result = false; 00160 if (oneOnly) return false; 00161 } 00162 } 00163 return result; 00164 } 00165 00166 default: 00167 if (v1.toString() != v2.toString()) { 00168 cerr << path << ": difference: " << v1 << " vs " << v2 << endl; 00169 result = false; 00170 if (oneOnly) return false; 00171 } 00172 } 00173 return result; 00174 } 00175 00176 void testBidRequestRoundTrip(const std::string & filename, 00177 const std::string & reqStr) 00178 { 00179 // Take an OpenRTB bid request; convert to Datacratic format; 00180 // serialize that to JSON; reconstitute from JSON back into the 00181 // original format 00182 00183 static DefaultDescription<OpenRTB::BidRequest> desc; 00184 00185 OpenRTB::BidRequest req; 00186 00187 { 00188 StreamingJsonParsingContext context; 00189 context.init(filename, reqStr.c_str(), reqStr.size()); 00190 desc.parseJson(&req, context); 00191 } 00192 00193 string printed; 00194 { 00195 std::ostringstream stream; 00196 StreamJsonPrintingContext printContext(stream); 00197 desc.printJson(&req, printContext); 00198 printed = stream.str(); 00199 } 00200 00201 OpenRTB::BidRequest req2; 00202 00203 { 00204 StreamingJsonParsingContext context; 00205 context.init(filename, reqStr.c_str(), reqStr.size()); 00206 desc.parseJson(&req2, context); 00207 } 00208 00209 string printed2; 00210 { 00211 std::ostringstream stream; 00212 StreamJsonPrintingContext printContext(stream); 00213 desc.printJson(&req2, printContext); 00214 printed2 = stream.str(); 00215 } 00216 00217 BOOST_CHECK_EQUAL(printed, printed2); 00218 00219 // Convert to a standard bid request 00220 00221 std::unique_ptr<BidRequest> br(fromOpenRtb(std::move(req), "openrtb", "openrtb")); 00222 00223 // Convert it to JSON 00224 string s1 = br->toJsonStr(); 00225 00226 std::unique_ptr<BidRequest> br2(BidRequest::parse("rtbkit", s1)); 00227 00228 string s2 = br2->toJsonStr(); 00229 00230 if (s1 != s2) { 00231 return; 00232 Json::Value j = br->toJson(); 00233 Json::Value j2 = br2->toJson(); 00234 BOOST_CHECK(jsonDiff(j, j2)); 00235 } 00236 } 00237 00238 BOOST_AUTO_TEST_CASE( test_openrtb_round_trip ) 00239 { 00240 vector<string> reqs; 00241 00242 for (auto s: samples) 00243 reqs.push_back(loadFile(s)); 00244 00245 for (unsigned i = 0; i < reqs.size(); ++i) 00246 testBidRequestRoundTrip(samples[i], reqs[i]); 00247 } 00248 00249 BOOST_AUTO_TEST_CASE( benchmark_openrtb_round_trip ) 00250 { 00251 vector<string> reqs; 00252 00253 for (auto s: samples) 00254 reqs.push_back(loadFile(s)); 00255 00256 int done = 0; 00257 00258 Date before = Date::now(); 00259 00260 for (unsigned i = 0; i < 1000; ++i) { 00261 00262 for (unsigned i = 0; i < reqs.size(); ++i, ++done) 00263 testBidRequestRoundTrip(samples[i], reqs[i]); 00264 } 00265 00266 double elapsed = Date::now().secondsSince(before); 00267 00268 cerr << "did " << done << " in " << elapsed << "s at " 00269 << done / elapsed << "/s" << endl; 00270 } 00271 00272 BOOST_AUTO_TEST_CASE( benchmark_openrtb_parsing ) 00273 { 00274 cerr << "benchmarking OpenRTB parsing" << endl; 00275 00276 vector<string> reqs; 00277 00278 for (auto s: samples) 00279 reqs.push_back(loadFile(s)); 00280 00281 int done = 0; 00282 00283 Date before = Date::now(); 00284 00285 DefaultDescription<OpenRTB::BidRequest> desc; 00286 00287 for (unsigned i = 0; i < 1000; ++i) { 00288 00289 for (unsigned i = 0; i < reqs.size(); ++i, ++done) { 00290 00291 00292 OpenRTB::BidRequest req; 00293 00294 { 00295 StreamingJsonParsingContext context; 00296 context.init(samples[i], reqs[i].c_str(), reqs[i].size()); 00297 desc.parseJson(&req, context); 00298 } 00299 } 00300 } 00301 00302 double elapsed = Date::now().secondsSince(before); 00303 00304 cerr << "did " << done << " in " << elapsed << "s at " 00305 << done / elapsed << "/s" << endl; 00306 } 00307 00308 BOOST_AUTO_TEST_CASE( benchmark_openrtb_conversion ) 00309 { 00310 cerr << "benchmarking OpenRTB parsing and conversion" << endl; 00311 00312 vector<string> reqs; 00313 00314 for (auto s: samples) 00315 reqs.push_back(loadFile(s)); 00316 00317 int done = 0; 00318 00319 Date before = Date::now(); 00320 00321 DefaultDescription<OpenRTB::BidRequest> desc; 00322 00323 for (unsigned i = 0; i < 1000; ++i) { 00324 00325 for (unsigned i = 0; i < reqs.size(); ++i, ++done) { 00326 00327 00328 OpenRTB::BidRequest req; 00329 00330 { 00331 StreamingJsonParsingContext context; 00332 context.init(samples[i], reqs[i].c_str(), reqs[i].size()); 00333 desc.parseJson(&req, context); 00334 } 00335 00336 std::unique_ptr<BidRequest> br(fromOpenRtb(std::move(req), "openrtb", "openrtb")); 00337 } 00338 } 00339 00340 double elapsed = Date::now().secondsSince(before); 00341 00342 cerr << "did " << done << " in " << elapsed << "s at " 00343 << done / elapsed << "/s" << endl; 00344 } 00345 00346 BOOST_AUTO_TEST_CASE( benchmark_canonical_parsing ) 00347 { 00348 cerr << "benchmarking canonical parsing of OpenRTB-derived bid requests" << endl; 00349 00350 DefaultDescription<OpenRTB::BidRequest> desc; 00351 00352 vector<string> reqs; 00353 00354 for (auto s: samples) { 00355 OpenRTB::BidRequest req; 00356 { 00357 StreamingJsonParsingContext context; 00358 context.init(s); 00359 desc.parseJson(&req, context); 00360 } 00361 00362 std::unique_ptr<BidRequest> br(fromOpenRtb(std::move(req), "openrtb", "openrtb")); 00363 00364 reqs.push_back(br->toJsonStr()); 00365 } 00366 00367 int done = 0; 00368 00369 Date before = Date::now(); 00370 00371 for (unsigned i = 0; i < 1000; ++i) { 00372 00373 for (unsigned i = 0; i < reqs.size(); ++i, ++done) { 00374 std::unique_ptr<BidRequest> br2(BidRequest::parse("rtbkit", reqs[i])); 00375 } 00376 } 00377 00378 double elapsed = Date::now().secondsSince(before); 00379 00380 cerr << "did " << done << " in " << elapsed << "s at " 00381 << done / elapsed << "/s" << endl; 00382 }