conformance_test.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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 google {
  53. namespace protobuf {
  54. std::set<ConformanceTestSuite*> *conformance_test_suite_set;
  55. GOOGLE_PROTOBUF_DECLARE_ONCE(conformance_test_suite_set_init_);
  56. void DeleteConformanceTestSuiteSet() {
  57. delete conformance_test_suite_set;
  58. }
  59. static void InitConformanceTestSuiteSet() {
  60. conformance_test_suite_set = new std::set<ConformanceTestSuite*>();
  61. internal::OnShutdown(&DeleteConformanceTestSuiteSet);
  62. }
  63. static void InitConformanceTestSuiteSetOnce() {
  64. ::google::protobuf::GoogleOnceInit(
  65. &conformance_test_suite_set_init_,
  66. &InitConformanceTestSuiteSet);
  67. }
  68. void AddTestSuite(ConformanceTestSuite* suite) {
  69. InitConformanceTestSuiteSetOnce();
  70. conformance_test_suite_set->insert(suite);
  71. }
  72. const std::set<ConformanceTestSuite*>& GetTestSuiteSet() {
  73. InitConformanceTestSuiteSetOnce();
  74. return *conformance_test_suite_set;
  75. }
  76. ConformanceTestSuite::ConformanceRequestSetting::ConformanceRequestSetting(
  77. ConformanceLevel level,
  78. conformance::WireFormat input_format,
  79. conformance::WireFormat output_format,
  80. conformance::TestCategory test_category,
  81. const Message& prototype_message,
  82. const string& test_name, const string& input)
  83. : level_(level),
  84. input_format_(input_format),
  85. output_format_(output_format),
  86. prototype_message_(prototype_message),
  87. test_name_(test_name) {
  88. switch (input_format) {
  89. case conformance::PROTOBUF: {
  90. request_.set_protobuf_payload(input);
  91. break;
  92. }
  93. case conformance::JSON: {
  94. request_.set_json_payload(input);
  95. break;
  96. }
  97. default:
  98. GOOGLE_LOG(FATAL) << "Unspecified input format";
  99. }
  100. request_.set_test_category(test_category);
  101. request_.set_message_type(prototype_message.GetDescriptor()->full_name());
  102. request_.set_requested_output_format(output_format);
  103. }
  104. Message* ConformanceTestSuite::ConformanceRequestSetting::
  105. GetTestMessage() const {
  106. return prototype_message_.New();
  107. }
  108. string ConformanceTestSuite::ConformanceRequestSetting::
  109. GetTestName() const {
  110. string rname =
  111. prototype_message_.GetDescriptor()->file()->syntax() ==
  112. FileDescriptor::SYNTAX_PROTO3 ? "Proto3" : "Proto2";
  113. return StrCat(ConformanceLevelToString(level_), ".",
  114. rname, ".",
  115. InputFormatString(input_format_),
  116. ".", test_name_, ".",
  117. OutputFormatString(output_format_));
  118. }
  119. string ConformanceTestSuite::ConformanceRequestSetting::
  120. ConformanceLevelToString(
  121. ConformanceLevel level) const {
  122. switch (level) {
  123. case REQUIRED: return "Required";
  124. case RECOMMENDED: return "Recommended";
  125. }
  126. GOOGLE_LOG(FATAL) << "Unknown value: " << level;
  127. return "";
  128. }
  129. string ConformanceTestSuite::ConformanceRequestSetting::
  130. InputFormatString(conformance::WireFormat format) const {
  131. switch (format) {
  132. case conformance::PROTOBUF:
  133. return "ProtobufInput";
  134. case conformance::JSON:
  135. return "JsonInput";
  136. default:
  137. GOOGLE_LOG(FATAL) << "Unspecified output format";
  138. }
  139. return "";
  140. }
  141. string ConformanceTestSuite::ConformanceRequestSetting::
  142. OutputFormatString(conformance::WireFormat format) const {
  143. switch (format) {
  144. case conformance::PROTOBUF:
  145. return "ProtobufOutput";
  146. case conformance::JSON:
  147. return "JsonOutput";
  148. default:
  149. GOOGLE_LOG(FATAL) << "Unspecified output format";
  150. }
  151. return "";
  152. }
  153. void ConformanceTestSuite::SetFailureList(
  154. const string& filename,
  155. const std::vector<string>& failure_list) {
  156. failure_list_filename_ = filename;
  157. expected_to_fail_.clear();
  158. std::copy(failure_list.begin(), failure_list.end(),
  159. std::inserter(expected_to_fail_, expected_to_fail_.end()));
  160. }
  161. void ConformanceTestSuite::ReportSuccess(const string& test_name) {
  162. if (expected_to_fail_.erase(test_name) != 0) {
  163. StringAppendF(&output_,
  164. "ERROR: test %s is in the failure list, but test succeeded. "
  165. "Remove it from the failure list.\n",
  166. test_name.c_str());
  167. unexpected_succeeding_tests_.insert(test_name);
  168. }
  169. successes_++;
  170. }
  171. void ConformanceTestSuite::ReportFailure(const string& test_name,
  172. ConformanceLevel level,
  173. const ConformanceRequest& request,
  174. const ConformanceResponse& response,
  175. const char* fmt, ...) {
  176. if (expected_to_fail_.erase(test_name) == 1) {
  177. expected_failures_++;
  178. if (!verbose_)
  179. return;
  180. } else if (level == RECOMMENDED && !enforce_recommended_) {
  181. StringAppendF(&output_, "WARNING, test=%s: ", test_name.c_str());
  182. } else {
  183. StringAppendF(&output_, "ERROR, test=%s: ", test_name.c_str());
  184. unexpected_failing_tests_.insert(test_name);
  185. }
  186. va_list args;
  187. va_start(args, fmt);
  188. StringAppendV(&output_, fmt, args);
  189. va_end(args);
  190. StringAppendF(&output_, " request=%s, response=%s\n",
  191. request.ShortDebugString().c_str(),
  192. response.ShortDebugString().c_str());
  193. }
  194. void ConformanceTestSuite::ReportSkip(const string& test_name,
  195. const ConformanceRequest& request,
  196. const ConformanceResponse& response) {
  197. if (verbose_) {
  198. StringAppendF(&output_, "SKIPPED, test=%s request=%s, response=%s\n",
  199. test_name.c_str(), request.ShortDebugString().c_str(),
  200. response.ShortDebugString().c_str());
  201. }
  202. skipped_.insert(test_name);
  203. }
  204. void ConformanceTestSuite::RunValidInputTest(
  205. const ConformanceRequestSetting& setting,
  206. const string& equivalent_text_format) {
  207. Message* reference_message = setting.GetTestMessage();
  208. GOOGLE_CHECK(
  209. TextFormat::ParseFromString(equivalent_text_format, reference_message))
  210. << "Failed to parse data for test case: " << setting.GetTestName()
  211. << ", data: " << equivalent_text_format;
  212. const string equivalent_wire_format = reference_message->SerializeAsString();
  213. RunValidBinaryInputTest(setting, equivalent_wire_format);
  214. }
  215. void ConformanceTestSuite::RunValidBinaryInputTest(
  216. const ConformanceRequestSetting& setting,
  217. const string& equivalent_wire_format) {
  218. const string& test_name = setting.GetTestName();
  219. ConformanceLevel level = setting.GetLevel();
  220. Message* reference_message = setting.GetTestMessage();
  221. GOOGLE_CHECK(
  222. reference_message->ParseFromString(equivalent_wire_format))
  223. << "Failed to parse wire data for test case: " << test_name;
  224. const ConformanceRequest& request = setting.GetRequest();
  225. ConformanceResponse response;
  226. RunTest(test_name, request, &response);
  227. Message* test_message = setting.GetTestMessage();
  228. WireFormat requested_output = request.requested_output_format();
  229. switch (response.result_case()) {
  230. case ConformanceResponse::RESULT_NOT_SET:
  231. ReportFailure(test_name, level, request, response,
  232. "Response didn't have any field in the Response.");
  233. return;
  234. case ConformanceResponse::kParseError:
  235. case ConformanceResponse::kRuntimeError:
  236. case ConformanceResponse::kSerializeError:
  237. ReportFailure(test_name, level, request, response,
  238. "Failed to parse input or produce output.");
  239. return;
  240. case ConformanceResponse::kSkipped:
  241. ReportSkip(test_name, request, response);
  242. return;
  243. case ConformanceResponse::kJsonPayload: {
  244. if (requested_output != conformance::JSON) {
  245. ReportFailure(
  246. test_name, level, request, response,
  247. "Test was asked for protobuf output but provided JSON instead.");
  248. return;
  249. }
  250. string binary_protobuf;
  251. Status status =
  252. JsonToBinaryString(type_resolver_.get(), type_url_,
  253. response.json_payload(), &binary_protobuf);
  254. if (!status.ok()) {
  255. ReportFailure(test_name, level, request, response,
  256. "JSON output we received from test was unparseable.");
  257. return;
  258. }
  259. if (!test_message->ParseFromString(binary_protobuf)) {
  260. ReportFailure(test_name, level, request, response,
  261. "INTERNAL ERROR: internal JSON->protobuf transcode "
  262. "yielded unparseable proto.");
  263. return;
  264. }
  265. break;
  266. }
  267. case ConformanceResponse::kProtobufPayload: {
  268. if (requested_output != conformance::PROTOBUF) {
  269. ReportFailure(
  270. test_name, level, request, response,
  271. "Test was asked for JSON output but provided protobuf instead.");
  272. return;
  273. }
  274. if (!test_message->ParseFromString(response.protobuf_payload())) {
  275. ReportFailure(test_name, level, request, response,
  276. "Protobuf output we received from test was unparseable.");
  277. return;
  278. }
  279. break;
  280. }
  281. default:
  282. GOOGLE_LOG(FATAL) << test_name << ": unknown payload type: "
  283. << response.result_case();
  284. }
  285. MessageDifferencer differencer;
  286. DefaultFieldComparator field_comparator;
  287. field_comparator.set_treat_nan_as_equal(true);
  288. differencer.set_field_comparator(&field_comparator);
  289. string differences;
  290. differencer.ReportDifferencesToString(&differences);
  291. bool check;
  292. check = differencer.Compare(*reference_message, *test_message);
  293. if (check) {
  294. ReportSuccess(test_name);
  295. } else {
  296. ReportFailure(test_name, level, request, response,
  297. "Output was not equivalent to reference message: %s.",
  298. differences.c_str());
  299. }
  300. }
  301. void ConformanceTestSuite::RunTest(const string& test_name,
  302. const ConformanceRequest& request,
  303. ConformanceResponse* response) {
  304. if (test_names_.insert(test_name).second == false) {
  305. GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name;
  306. }
  307. string serialized_request;
  308. string serialized_response;
  309. request.SerializeToString(&serialized_request);
  310. runner_->RunTest(test_name, serialized_request, &serialized_response);
  311. if (!response->ParseFromString(serialized_response)) {
  312. response->Clear();
  313. response->set_runtime_error("response proto could not be parsed.");
  314. }
  315. if (verbose_) {
  316. StringAppendF(&output_,
  317. "conformance test: name=%s, request=%s, response=%s\n",
  318. test_name.c_str(),
  319. request.ShortDebugString().c_str(),
  320. response->ShortDebugString().c_str());
  321. }
  322. }
  323. bool ConformanceTestSuite::CheckSetEmpty(
  324. const std::set<string>& set_to_check,
  325. const std::string& write_to_file,
  326. const std::string& msg) {
  327. if (set_to_check.empty()) {
  328. return true;
  329. } else {
  330. StringAppendF(&output_, "\n");
  331. StringAppendF(&output_, "%s\n\n", msg.c_str());
  332. for (std::set<string>::const_iterator iter = set_to_check.begin();
  333. iter != set_to_check.end(); ++iter) {
  334. StringAppendF(&output_, " %s\n", iter->c_str());
  335. }
  336. StringAppendF(&output_, "\n");
  337. if (!write_to_file.empty()) {
  338. std::ofstream os(write_to_file);
  339. if (os) {
  340. for (std::set<string>::const_iterator iter = set_to_check.begin();
  341. iter != set_to_check.end(); ++iter) {
  342. os << *iter << "\n";
  343. }
  344. } else {
  345. StringAppendF(&output_, "Failed to open file: %s\n",
  346. write_to_file.c_str());
  347. }
  348. }
  349. return false;
  350. }
  351. }
  352. bool ConformanceTestSuite::RunSuite(
  353. ConformanceTestRunner* runner, std::string* output) {
  354. runner_ = runner;
  355. successes_ = 0;
  356. expected_failures_ = 0;
  357. skipped_.clear();
  358. test_names_.clear();
  359. unexpected_failing_tests_.clear();
  360. unexpected_succeeding_tests_.clear();
  361. output_ = "\nCONFORMANCE TEST BEGIN ====================================\n\n";
  362. RunSuiteImpl();
  363. bool ok = true;
  364. if (!CheckSetEmpty(expected_to_fail_, "nonexistent_tests.txt",
  365. "These tests were listed in the failure list, but they "
  366. "don't exist. Remove them from the failure list by "
  367. "running:\n"
  368. " ./update_failure_list.py " + failure_list_filename_ +
  369. " --remove nonexistent_tests.txt")) {
  370. ok = false;
  371. }
  372. if (!CheckSetEmpty(unexpected_failing_tests_, "failing_tests.txt",
  373. "These tests failed. If they can't be fixed right now, "
  374. "you can add them to the failure list so the overall "
  375. "suite can succeed. Add them to the failure list by "
  376. "running:\n"
  377. " ./update_failure_list.py " + failure_list_filename_ +
  378. " --add failing_tests.txt")) {
  379. ok = false;
  380. }
  381. if (!CheckSetEmpty(unexpected_succeeding_tests_, "succeeding_tests.txt",
  382. "These tests succeeded, even though they were listed in "
  383. "the failure list. Remove them from the failure list "
  384. "by running:\n"
  385. " ./update_failure_list.py " + failure_list_filename_ +
  386. " --remove succeeding_tests.txt")) {
  387. ok = false;
  388. }
  389. if (verbose_) {
  390. CheckSetEmpty(skipped_, "",
  391. "These tests were skipped (probably because support for some "
  392. "features is not implemented)");
  393. }
  394. StringAppendF(&output_,
  395. "CONFORMANCE SUITE %s: %d successes, %d skipped, "
  396. "%d expected failures, %d unexpected failures.\n",
  397. ok ? "PASSED" : "FAILED", successes_, skipped_.size(),
  398. expected_failures_, unexpected_failing_tests_.size());
  399. StringAppendF(&output_, "\n");
  400. output->assign(output_);
  401. return ok;
  402. }
  403. } // namespace protobuf
  404. } // namespace google