1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 // This file defines a protocol for running the conformance test suite 9 // in-process. In other words, the suite itself will run in the same process as 10 // the code under test. 11 // 12 // For pros and cons of this approach, please see conformance.proto. 13 14 #ifndef CONFORMANCE_CONFORMANCE_TEST_H 15 #define CONFORMANCE_CONFORMANCE_TEST_H 16 17 #include <cstddef> 18 #include <cstdint> 19 #include <memory> 20 #include <string> 21 #include <utility> 22 #include <vector> 23 24 #include "google/protobuf/descriptor.pb.h" 25 #include "absl/container/btree_map.h" 26 #include "absl/container/flat_hash_set.h" 27 #include "conformance/conformance.pb.h" 28 #include "failure_list_trie_node.h" 29 #include "google/protobuf/descriptor.h" 30 31 namespace conformance { 32 class ConformanceRequest; 33 class ConformanceResponse; 34 } // namespace conformance 35 36 namespace protobuf_test_messages { 37 namespace proto3 { 38 class TestAllTypesProto3; 39 } // namespace proto3 40 } // namespace protobuf_test_messages 41 42 namespace google { 43 namespace protobuf { 44 45 class ConformanceTestSuite; 46 47 class ConformanceTestRunner { 48 public: 49 virtual ~ConformanceTestRunner() = default; 50 51 // Call to run a single conformance test. 52 // 53 // "len" is the byte length of a serialized conformance.ConformanceRequest. 54 // "input" is a serialized conformance.ConformanceRequest. 55 // "output" should be set to a serialized conformance.ConformanceResponse. 56 // 57 // If there is any error in running the test itself, set "runtime_error" in 58 // the response. 59 virtual void RunTest(const std::string& test_name, uint32_t len, 60 const std::string& input, std::string* output) = 0; 61 }; 62 63 // Test runner that spawns the process being tested and communicates with it 64 // over a pipe. 65 class ForkPipeRunner : public ConformanceTestRunner { 66 public: 67 // Note: Run() doesn't take ownership of the pointers inside suites. 68 static int Run(int argc, char* argv[], 69 const std::vector<ConformanceTestSuite*>& suites); 70 ForkPipeRunner(const std::string & executable,const std::vector<std::string> & executable_args,bool performance)71 ForkPipeRunner(const std::string& executable, 72 const std::vector<std::string>& executable_args, 73 bool performance) 74 : child_pid_(-1), 75 executable_(executable), 76 executable_args_(executable_args), 77 performance_(performance) {} 78 ForkPipeRunner(const std::string & executable)79 explicit ForkPipeRunner(const std::string& executable) 80 : child_pid_(-1), executable_(executable) {} 81 82 ~ForkPipeRunner() override = default; 83 84 void RunTest(const std::string& test_name, uint32_t len, 85 const std::string& request, std::string* response) override; 86 87 private: 88 void SpawnTestProgram(); 89 90 void CheckedWrite(int fd, const void* buf, size_t len); 91 bool TryRead(int fd, void* buf, size_t len); 92 void CheckedRead(int fd, void* buf, size_t len); 93 94 int write_fd_; 95 int read_fd_; 96 pid_t child_pid_; 97 std::string executable_; 98 const std::vector<std::string> executable_args_; 99 bool performance_; 100 std::string current_test_name_; 101 }; 102 103 // Class representing the test suite itself. To run it, implement your own 104 // class derived from ConformanceTestRunner, class derived from 105 // ConformanceTestSuite and then write code like: 106 // 107 // class MyConformanceTestSuite : public ConformanceTestSuite { 108 // public: 109 // void RunSuiteImpl() { 110 // // INSERT ACTUAL TESTS. 111 // } 112 // }; 113 // 114 // class MyConformanceTestRunner : public ConformanceTestRunner { 115 // public: 116 // static int Run(int argc, char *argv[], 117 // ConformanceTestSuite* suite); 118 // 119 // private: 120 // virtual void RunTest(...) { 121 // // INSERT YOUR FRAMEWORK-SPECIFIC CODE HERE. 122 // } 123 // }; 124 // 125 // int main() { 126 // MyConformanceTestSuite suite; 127 // MyConformanceTestRunner::Run(argc, argv, &suite); 128 // } 129 // 130 class ConformanceTestSuite { 131 public: 132 ConformanceTestSuite() = default; 133 virtual ~ConformanceTestSuite() = default; 134 SetPerformance(bool performance)135 void SetPerformance(bool performance) { performance_ = performance; } SetVerbose(bool verbose)136 void SetVerbose(bool verbose) { verbose_ = verbose; } 137 138 // Whether to require the testee to pass RECOMMENDED tests. By default failing 139 // a RECOMMENDED test case will not fail the entire suite but will only 140 // generated a warning. If this flag is set to true, RECOMMENDED tests will 141 // be treated the same way as REQUIRED tests and failing a RECOMMENDED test 142 // case will cause the entire test suite to fail as well. An implementation 143 // can enable this if it wants to be strictly conforming to protobuf spec. 144 // See the comments about ConformanceLevel below to learn more about the 145 // difference between REQUIRED and RECOMMENDED test cases. SetEnforceRecommended(bool value)146 void SetEnforceRecommended(bool value) { enforce_recommended_ = value; } 147 148 // Sets the maximum edition (inclusive) that should be tests for conformance. SetMaximumEdition(Edition edition)149 void SetMaximumEdition(Edition edition) { maximum_edition_ = edition; } 150 151 // Gets the flag name to the failure list file. 152 // By default, this would return --failure_list GetFailureListFlagName()153 std::string GetFailureListFlagName() { return failure_list_flag_name_; } 154 SetFailureListFlagName(const std::string & failure_list_flag_name)155 void SetFailureListFlagName(const std::string& failure_list_flag_name) { 156 failure_list_flag_name_ = failure_list_flag_name; 157 } 158 159 // Sets the path of the output directory. SetOutputDir(const std::string & output_dir)160 void SetOutputDir(const std::string& output_dir) { output_dir_ = output_dir; } 161 162 // Sets if we are running the test in debug mode. SetDebug(bool debug)163 void SetDebug(bool debug) { debug_ = debug; } 164 165 // Sets if we are running ONLY the tests provided in the 'names_to_test_' set. SetIsolated(bool isolated)166 void SetIsolated(bool isolated) { isolated_ = isolated; } 167 168 // Sets the file path of the testee. SetTestee(const std::string & testee)169 void SetTestee(const std::string& testee) { testee_ = testee; } 170 171 // Sets the names of tests to ONLY be run isolated from all the others. SetNamesToTest(absl::flat_hash_set<std::string> names_to_test)172 void SetNamesToTest(absl::flat_hash_set<std::string> names_to_test) { 173 names_to_test_ = std::move(names_to_test); 174 } 175 GetExpectedTestsNotRun()176 absl::flat_hash_set<std::string> GetExpectedTestsNotRun() { 177 return names_to_test_; 178 } 179 180 // Run all the conformance tests against the given test runner. 181 // Test output will be stored in "output". 182 // 183 // Returns true if the set of failing tests was exactly the same as the 184 // failure list. 185 // The filename here is *only* used to create/format useful error messages for 186 // how to update the failure list. We do NOT read this file at all. 187 188 bool RunSuite(ConformanceTestRunner* runner, std::string* output, 189 const std::string& filename, 190 conformance::FailureSet* failure_list); 191 192 protected: 193 // Test cases are classified into a few categories: 194 // REQUIRED: the test case must be passed for an implementation to be 195 // interoperable with other implementations. For example, a 196 // parser implementation must accept both packed and unpacked 197 // form of repeated primitive fields. 198 // RECOMMENDED: the test case is not required for the implementation to 199 // be interoperable with other implementations, but is 200 // recommended for best performance and compatibility. For 201 // example, a proto3 serializer should serialize repeated 202 // primitive fields in packed form, but an implementation 203 // failing to do so will still be able to communicate with 204 // other implementations. 205 enum ConformanceLevel { 206 REQUIRED = 0, 207 RECOMMENDED = 1, 208 }; 209 210 class ConformanceRequestSetting { 211 public: 212 ConformanceRequestSetting(ConformanceLevel level, 213 conformance::WireFormat input_format, 214 conformance::WireFormat output_format, 215 conformance::TestCategory test_category, 216 const Message& prototype_message, 217 const std::string& test_name, 218 const std::string& input); 219 virtual ~ConformanceRequestSetting() = default; 220 221 std::unique_ptr<Message> NewTestMessage() const; 222 223 std::string GetSyntaxIdentifier() const; 224 225 std::string GetTestName() const; 226 GetRequest()227 const conformance::ConformanceRequest& GetRequest() const { 228 return request_; 229 } 230 GetLevel()231 ConformanceLevel GetLevel() const { return level_; } 232 233 std::string ConformanceLevelToString(ConformanceLevel level) const; 234 SetPrintUnknownFields(bool print_unknown_fields)235 void SetPrintUnknownFields(bool print_unknown_fields) { 236 request_.set_print_unknown_fields(true); 237 } 238 SetPrototypeMessageForCompare(const Message & message)239 void SetPrototypeMessageForCompare(const Message& message) { 240 prototype_message_for_compare_.reset(message.New()); 241 } 242 243 protected: 244 virtual std::string InputFormatString(conformance::WireFormat format) const; 245 virtual std::string OutputFormatString( 246 conformance::WireFormat format) const; 247 conformance::ConformanceRequest request_; 248 249 private: 250 ConformanceLevel level_; 251 ::conformance::WireFormat input_format_; 252 ::conformance::WireFormat output_format_; 253 const Message& prototype_message_; 254 std::unique_ptr<Message> prototype_message_for_compare_; 255 std::string test_name_; 256 }; 257 258 std::string WireFormatToString(conformance::WireFormat wire_format); 259 260 // Parse payload in the response to the given message. Returns true on 261 // success. 262 virtual bool ParseResponse(const conformance::ConformanceResponse& response, 263 const ConformanceRequestSetting& setting, 264 Message* test_message) = 0; 265 266 void VerifyResponse(const ConformanceRequestSetting& setting, 267 const std::string& equivalent_wire_format, 268 const conformance::ConformanceResponse& response, 269 bool need_report_success, bool require_same_wire_format); 270 271 void TruncateDebugPayload(std::string* payload); 272 conformance::ConformanceRequest TruncateRequest( 273 const conformance::ConformanceRequest& request); 274 conformance::ConformanceResponse TruncateResponse( 275 const conformance::ConformanceResponse& response); 276 277 void ReportSuccess(const conformance::TestStatus& test); 278 void ReportFailure(conformance::TestStatus& test, ConformanceLevel level, 279 const conformance::ConformanceRequest& request, 280 const conformance::ConformanceResponse& response); 281 void ReportSkip(const conformance::TestStatus& test, 282 const conformance::ConformanceRequest& request, 283 const conformance::ConformanceResponse& response); 284 285 void RunValidInputTest(const ConformanceRequestSetting& setting, 286 const std::string& equivalent_text_format); 287 void RunValidBinaryInputTest(const ConformanceRequestSetting& setting, 288 const std::string& equivalent_wire_format, 289 bool require_same_wire_format = false); 290 291 // Returns true if our runner_ ran the test and false if it did not. 292 bool RunTest(const std::string& test_name, 293 const conformance::ConformanceRequest& request, 294 conformance::ConformanceResponse* response); 295 296 // Will return false if an entry from the failure list was either a 297 // duplicate of an already added one to the trie or it contained invalid 298 // wildcards; otherwise, returns true. 299 bool AddExpectedFailedTest(const conformance::TestStatus& failure); 300 301 virtual void RunSuiteImpl() = 0; 302 303 ConformanceTestRunner* runner_; 304 FailureListTrieNode failure_list_root_; 305 std::string testee_; 306 int successes_; 307 int expected_failures_; 308 bool verbose_ = false; 309 bool performance_ = false; 310 bool enforce_recommended_ = false; 311 Edition maximum_edition_ = Edition::EDITION_PROTO3; 312 std::string output_; 313 std::string output_dir_; 314 std::string failure_list_flag_name_ = "--failure_list"; 315 std::string failure_list_filename_; 316 absl::flat_hash_set<std::string> names_to_test_; 317 bool debug_ = false; 318 // If names were given for names_to_test_, only those tests 319 // will be run and this bool will be set to true. 320 bool isolated_ = false; 321 322 // The set of test names (expanded from wildcard(s) and non-expanded) that are 323 // expected to fail in this run, but haven't failed yet. 324 absl::btree_map<std::string, conformance::TestStatus> expected_to_fail_; 325 326 // The set of tests that failed because their failure message did not match 327 // the actual failure message. These are failure messages that may need to be 328 // removed from our failure lists. 329 absl::btree_map<std::string, conformance::TestStatus> 330 expected_failure_messages_; 331 332 // The set of test names that have been run. Used to ensure that there are no 333 // duplicate names in the suite. 334 absl::flat_hash_set<std::string> test_names_ran_; 335 336 // The set of tests that failed, but weren't expected to: They weren't 337 // present in our failure lists. 338 absl::btree_map<std::string, conformance::TestStatus> 339 unexpected_failing_tests_; 340 341 // The set of tests that succeeded, but weren't expected to: They were present 342 // in our failure lists, but managed to succeed. 343 absl::btree_map<std::string, conformance::TestStatus> 344 unexpected_succeeding_tests_; 345 346 // The set of tests that failed because their failure message did not match 347 // the actual failure message. These are failure messages that may need to be 348 // added to our failure lists. 349 absl::btree_map<std::string, conformance::TestStatus> 350 unexpected_failure_messages_; 351 352 // The set of test names (wildcarded or not) from the failure list that did 353 // not match any actual test name. 354 absl::btree_map<std::string, conformance::TestStatus> unmatched_; 355 356 // The set of tests that the testee opted out of; 357 absl::btree_map<std::string, conformance::TestStatus> skipped_; 358 359 // Allows us to remove from unmatched_. 360 absl::btree_map<std::string, std::string> saved_failure_messages_; 361 362 // If a failure list entry served as a match for more than 'max_matches_', 363 // those will be added here for removal. 364 absl::btree_map<std::string, conformance::TestStatus> exceeded_max_matches_; 365 366 // Keeps track of how many tests matched to each failure list entry. 367 absl::btree_map<std::string, int> number_of_matches_; 368 }; 369 370 } // namespace protobuf 371 } // namespace google 372 373 #endif // CONFORMANCE_CONFORMANCE_TEST_H 374