conformance_test.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // https://developers.google.com/protocol-buffers/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. #include <set>
  31. #include <stdarg.h>
  32. #include <string>
  33. #include <fstream>
  34. #include "conformance.pb.h"
  35. #include "conformance_test.h"
  36. #include <google/protobuf/stubs/stringprintf.h>
  37. #include <google/protobuf/stubs/strutil.h>
  38. #include <google/protobuf/message.h>
  39. #include <google/protobuf/text_format.h>
  40. #include <google/protobuf/util/field_comparator.h>
  41. #include <google/protobuf/util/json_util.h>
  42. #include <google/protobuf/util/message_differencer.h>
  43. using conformance::ConformanceRequest;
  44. using conformance::ConformanceResponse;
  45. using conformance::WireFormat;
  46. using google::protobuf::TextFormat;
  47. using google::protobuf::util::DefaultFieldComparator;
  48. using google::protobuf::util::JsonToBinaryString;
  49. using google::protobuf::util::MessageDifferencer;
  50. using google::protobuf::util::Status;
  51. using std::string;
  52. namespace {
  53. static string ToOctString(const string& binary_string) {
  54. string oct_string;
  55. for (size_t i = 0; i < binary_string.size(); i++) {
  56. uint8_t c = binary_string.at(i);
  57. uint8_t high = c / 64;
  58. uint8_t mid = (c % 64) / 8;
  59. uint8_t low = c % 8;
  60. oct_string.push_back('\\');
  61. oct_string.push_back('0' + high);
  62. oct_string.push_back('0' + mid);
  63. oct_string.push_back('0' + low);
  64. }
  65. return oct_string;
  66. }
  67. } // namespace
  68. namespace google {
  69. namespace protobuf {
  70. ConformanceTestSuite::ConformanceRequestSetting::ConformanceRequestSetting(
  71. ConformanceLevel level,
  72. conformance::WireFormat input_format,
  73. conformance::WireFormat output_format,
  74. conformance::TestCategory test_category,
  75. const Message& prototype_message,
  76. const string& test_name, const string& input)
  77. : level_(level),
  78. input_format_(input_format),
  79. output_format_(output_format),
  80. prototype_message_(prototype_message),
  81. prototype_message_for_compare_(prototype_message.New()),
  82. test_name_(test_name) {
  83. switch (input_format) {
  84. case conformance::PROTOBUF: {
  85. request_.set_protobuf_payload(input);
  86. break;
  87. }
  88. case conformance::JSON: {
  89. request_.set_json_payload(input);
  90. break;
  91. }
  92. case conformance::JSPB: {
  93. request_.set_jspb_payload(input);
  94. break;
  95. }
  96. case conformance::TEXT_FORMAT: {
  97. request_.set_text_payload(input);
  98. break;
  99. }
  100. default:
  101. GOOGLE_LOG(FATAL) << "Unspecified input format";
  102. }
  103. request_.set_test_category(test_category);
  104. request_.set_message_type(prototype_message.GetDescriptor()->full_name());
  105. request_.set_requested_output_format(output_format);
  106. }
  107. Message* ConformanceTestSuite::ConformanceRequestSetting::
  108. GetTestMessage() const {
  109. return prototype_message_for_compare_->New();
  110. }
  111. string ConformanceTestSuite::ConformanceRequestSetting::
  112. GetTestName() const {
  113. string rname =
  114. prototype_message_.GetDescriptor()->file()->syntax() ==
  115. FileDescriptor::SYNTAX_PROTO3 ? "Proto3" : "Proto2";
  116. return StrCat(ConformanceLevelToString(level_), ".",
  117. rname, ".",
  118. InputFormatString(input_format_),
  119. ".", test_name_, ".",
  120. OutputFormatString(output_format_));
  121. }
  122. string ConformanceTestSuite::ConformanceRequestSetting::
  123. ConformanceLevelToString(
  124. ConformanceLevel level) const {
  125. switch (level) {
  126. case REQUIRED: return "Required";
  127. case RECOMMENDED: return "Recommended";
  128. }
  129. GOOGLE_LOG(FATAL) << "Unknown value: " << level;
  130. return "";
  131. }
  132. string ConformanceTestSuite::ConformanceRequestSetting::
  133. InputFormatString(conformance::WireFormat format) const {
  134. switch (format) {
  135. case conformance::PROTOBUF:
  136. return "ProtobufInput";
  137. case conformance::JSON:
  138. return "JsonInput";
  139. case conformance::TEXT_FORMAT:
  140. return "TextFormatInput";
  141. default:
  142. GOOGLE_LOG(FATAL) << "Unspecified output format";
  143. }
  144. return "";
  145. }
  146. string ConformanceTestSuite::ConformanceRequestSetting::
  147. OutputFormatString(conformance::WireFormat format) const {
  148. switch (format) {
  149. case conformance::PROTOBUF:
  150. return "ProtobufOutput";
  151. case conformance::JSON:
  152. return "JsonOutput";
  153. case conformance::TEXT_FORMAT:
  154. return "TextFormatOutput";
  155. default:
  156. GOOGLE_LOG(FATAL) << "Unspecified output format";
  157. }
  158. return "";
  159. }
  160. void ConformanceTestSuite::ReportSuccess(const string& test_name) {
  161. if (expected_to_fail_.erase(test_name) != 0) {
  162. StringAppendF(&output_,
  163. "ERROR: test %s is in the failure list, but test succeeded. "
  164. "Remove it from the failure list.\n",
  165. test_name.c_str());
  166. unexpected_succeeding_tests_.insert(test_name);
  167. }
  168. successes_++;
  169. }
  170. void ConformanceTestSuite::ReportFailure(const string& test_name,
  171. ConformanceLevel level,
  172. const ConformanceRequest& request,
  173. const ConformanceResponse& response,
  174. const char* fmt, ...) {
  175. if (expected_to_fail_.erase(test_name) == 1) {
  176. expected_failures_++;
  177. if (!verbose_)
  178. return;
  179. } else if (level == RECOMMENDED && !enforce_recommended_) {
  180. StringAppendF(&output_, "WARNING, test=%s: ", test_name.c_str());
  181. } else {
  182. StringAppendF(&output_, "ERROR, test=%s: ", test_name.c_str());
  183. unexpected_failing_tests_.insert(test_name);
  184. }
  185. va_list args;
  186. va_start(args, fmt);
  187. StringAppendV(&output_, fmt, args);
  188. va_end(args);
  189. StringAppendF(&output_, " request=%s, response=%s\n",
  190. request.ShortDebugString().c_str(),
  191. response.ShortDebugString().c_str());
  192. }
  193. void ConformanceTestSuite::ReportSkip(const string& test_name,
  194. const ConformanceRequest& request,
  195. const ConformanceResponse& response) {
  196. if (verbose_) {
  197. StringAppendF(&output_, "SKIPPED, test=%s request=%s, response=%s\n",
  198. test_name.c_str(), request.ShortDebugString().c_str(),
  199. response.ShortDebugString().c_str());
  200. }
  201. skipped_.insert(test_name);
  202. }
  203. void ConformanceTestSuite::RunValidInputTest(
  204. const ConformanceRequestSetting& setting,
  205. const string& equivalent_text_format) {
  206. Message* reference_message = setting.GetTestMessage();
  207. GOOGLE_CHECK(
  208. TextFormat::ParseFromString(equivalent_text_format, reference_message))
  209. << "Failed to parse data for test case: " << setting.GetTestName()
  210. << ", data: " << equivalent_text_format;
  211. const string equivalent_wire_format = reference_message->SerializeAsString();
  212. RunValidBinaryInputTest(setting, equivalent_wire_format);
  213. }
  214. void ConformanceTestSuite::RunValidBinaryInputTest(
  215. const ConformanceRequestSetting& setting,
  216. const string& equivalent_wire_format, bool require_same_wire_format) {
  217. const ConformanceRequest& request = setting.GetRequest();
  218. ConformanceResponse response;
  219. RunTest(setting.GetTestName(), request, &response);
  220. VerifyResponse(setting, equivalent_wire_format, response, true,
  221. require_same_wire_format);
  222. }
  223. void ConformanceTestSuite::VerifyResponse(
  224. const ConformanceRequestSetting& setting,
  225. const string& equivalent_wire_format, const ConformanceResponse& response,
  226. bool need_report_success, bool require_same_wire_format) {
  227. Message* test_message = setting.GetTestMessage();
  228. const ConformanceRequest& request = setting.GetRequest();
  229. const string& test_name = setting.GetTestName();
  230. ConformanceLevel level = setting.GetLevel();
  231. Message* reference_message = setting.GetTestMessage();
  232. GOOGLE_CHECK(
  233. reference_message->ParseFromString(equivalent_wire_format))
  234. << "Failed to parse wire data for test case: " << test_name;
  235. switch (response.result_case()) {
  236. case ConformanceResponse::RESULT_NOT_SET:
  237. ReportFailure(test_name, level, request, response,
  238. "Response didn't have any field in the Response.");
  239. return;
  240. case ConformanceResponse::kParseError:
  241. case ConformanceResponse::kRuntimeError:
  242. case ConformanceResponse::kSerializeError:
  243. ReportFailure(test_name, level, request, response,
  244. "Failed to parse input or produce output.");
  245. return;
  246. case ConformanceResponse::kSkipped:
  247. ReportSkip(test_name, request, response);
  248. return;
  249. default:
  250. if (!ParseResponse(response, setting, test_message)) return;
  251. }
  252. MessageDifferencer differencer;
  253. DefaultFieldComparator field_comparator;
  254. field_comparator.set_treat_nan_as_equal(true);
  255. differencer.set_field_comparator(&field_comparator);
  256. string differences;
  257. differencer.ReportDifferencesToString(&differences);
  258. bool check = false;
  259. if (require_same_wire_format) {
  260. GOOGLE_DCHECK_EQ(response.result_case(),
  261. ConformanceResponse::kProtobufPayload);
  262. const string& protobuf_payload = response.protobuf_payload();
  263. check = equivalent_wire_format == protobuf_payload;
  264. differences = StrCat("Expect: ", ToOctString(equivalent_wire_format),
  265. ", but got: ", ToOctString(protobuf_payload));
  266. } else {
  267. check = differencer.Compare(*reference_message, *test_message);
  268. }
  269. if (check) {
  270. if (need_report_success) {
  271. ReportSuccess(test_name);
  272. }
  273. } else {
  274. ReportFailure(test_name, level, request, response,
  275. "Output was not equivalent to reference message: %s.",
  276. differences.c_str());
  277. }
  278. }
  279. void ConformanceTestSuite::RunTest(const string& test_name,
  280. const ConformanceRequest& request,
  281. ConformanceResponse* response) {
  282. if (test_names_.insert(test_name).second == false) {
  283. GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name;
  284. }
  285. string serialized_request;
  286. string serialized_response;
  287. request.SerializeToString(&serialized_request);
  288. runner_->RunTest(test_name, serialized_request, &serialized_response);
  289. if (!response->ParseFromString(serialized_response)) {
  290. response->Clear();
  291. response->set_runtime_error("response proto could not be parsed.");
  292. }
  293. if (verbose_) {
  294. StringAppendF(&output_,
  295. "conformance test: name=%s, request=%s, response=%s\n",
  296. test_name.c_str(),
  297. request.ShortDebugString().c_str(),
  298. response->ShortDebugString().c_str());
  299. }
  300. }
  301. bool ConformanceTestSuite::CheckSetEmpty(
  302. const std::set<string>& set_to_check,
  303. const std::string& write_to_file,
  304. const std::string& msg) {
  305. if (set_to_check.empty()) {
  306. return true;
  307. } else {
  308. StringAppendF(&output_, "\n");
  309. StringAppendF(&output_, "%s\n\n", msg.c_str());
  310. for (std::set<string>::const_iterator iter = set_to_check.begin();
  311. iter != set_to_check.end(); ++iter) {
  312. StringAppendF(&output_, " %s\n", iter->c_str());
  313. }
  314. StringAppendF(&output_, "\n");
  315. if (!write_to_file.empty()) {
  316. std::ofstream os(write_to_file);
  317. if (os) {
  318. for (std::set<string>::const_iterator iter = set_to_check.begin();
  319. iter != set_to_check.end(); ++iter) {
  320. os << *iter << "\n";
  321. }
  322. } else {
  323. StringAppendF(&output_, "Failed to open file: %s\n",
  324. write_to_file.c_str());
  325. }
  326. }
  327. return false;
  328. }
  329. }
  330. string ConformanceTestSuite::WireFormatToString(
  331. WireFormat wire_format) {
  332. switch (wire_format) {
  333. case conformance::PROTOBUF:
  334. return "PROTOBUF";
  335. case conformance::JSON:
  336. return "JSON";
  337. case conformance::JSPB:
  338. return "JSPB";
  339. case conformance::TEXT_FORMAT:
  340. return "TEXT_FORMAT";
  341. case conformance::UNSPECIFIED:
  342. return "UNSPECIFIED";
  343. default:
  344. GOOGLE_LOG(FATAL) << "unknown wire type: "
  345. << wire_format;
  346. }
  347. return "";
  348. }
  349. void ConformanceTestSuite::AddExpectedFailedTest(const std::string& test_name) {
  350. expected_to_fail_.insert(test_name);
  351. }
  352. bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
  353. std::string* output, const string& filename,
  354. conformance::FailureSet* failure_list) {
  355. runner_ = runner;
  356. successes_ = 0;
  357. expected_failures_ = 0;
  358. skipped_.clear();
  359. test_names_.clear();
  360. unexpected_failing_tests_.clear();
  361. unexpected_succeeding_tests_.clear();
  362. output_ = "\nCONFORMANCE TEST BEGIN ====================================\n\n";
  363. failure_list_filename_ = filename;
  364. expected_to_fail_.clear();
  365. for (const string& failure : failure_list->failure()) {
  366. AddExpectedFailedTest(failure);
  367. }
  368. RunSuiteImpl();
  369. bool ok = true;
  370. if (!CheckSetEmpty(expected_to_fail_, "nonexistent_tests.txt",
  371. "These tests were listed in the failure list, but they "
  372. "don't exist. Remove them from the failure list by "
  373. "running:\n"
  374. " ./update_failure_list.py " + failure_list_filename_ +
  375. " --remove nonexistent_tests.txt")) {
  376. ok = false;
  377. }
  378. if (!CheckSetEmpty(unexpected_failing_tests_, "failing_tests.txt",
  379. "These tests failed. If they can't be fixed right now, "
  380. "you can add them to the failure list so the overall "
  381. "suite can succeed. Add them to the failure list by "
  382. "running:\n"
  383. " ./update_failure_list.py " + failure_list_filename_ +
  384. " --add failing_tests.txt")) {
  385. ok = false;
  386. }
  387. if (!CheckSetEmpty(unexpected_succeeding_tests_, "succeeding_tests.txt",
  388. "These tests succeeded, even though they were listed in "
  389. "the failure list. Remove them from the failure list "
  390. "by running:\n"
  391. " ./update_failure_list.py " + failure_list_filename_ +
  392. " --remove succeeding_tests.txt")) {
  393. ok = false;
  394. }
  395. if (verbose_) {
  396. CheckSetEmpty(skipped_, "",
  397. "These tests were skipped (probably because support for some "
  398. "features is not implemented)");
  399. }
  400. StringAppendF(&output_,
  401. "CONFORMANCE SUITE %s: %d successes, %d skipped, "
  402. "%d expected failures, %d unexpected failures.\n",
  403. ok ? "PASSED" : "FAILED", successes_, skipped_.size(),
  404. expected_failures_, unexpected_failing_tests_.size());
  405. StringAppendF(&output_, "\n");
  406. output->assign(output_);
  407. return ok;
  408. }
  409. } // namespace protobuf
  410. } // namespace google