| #ifndef GOOGLE_PROTOBUF_CONFORMANCE_TESTEE_H__ | |
| #define GOOGLE_PROTOBUF_CONFORMANCE_TESTEE_H__ | |
| #include <string> | |
| #include <utility> | |
| #include "absl/container/flat_hash_set.h" | |
| #include "absl/strings/string_view.h" | |
| #include "binary_wireformat.h" | |
| #include "conformance/conformance.pb.h" | |
| #include "test_runner.h" | |
| #include "google/protobuf/descriptor.h" | |
| // This file defines the APIs used by conformance tests to interact with | |
| // testees. The structure of these APIs are intentionally decoupled from the | |
| // runner/testee protocol (which are used to implement them), in order to | |
| // maximize their flexibility in tests. | |
| // | |
| // Tests should not ever need to name any of these types directly, but will | |
| // obtain a Test object pointing to the global testee and pass the final | |
| // TestResult to one of our matchers. | |
| // | |
| // Example: | |
| // | |
| // EXPECT_THAT(RequiredTest() | |
| // .ParseBinary(Wire(LengthPrefixedField(1, "foo")) | |
| // .SerializeText({.print_unknown_fields = true}), | |
| // ParsedPayload(EqualsProto("pb(1: "foo")pb"))); | |
| // TODO Possible future APIs to expand conformance coverage: | |
| // - Add ClearUnknownFields() to InMemoryMessage | |
| // - Add MergeFrom() method to InMemoryMessage to merge raw binary | |
| // - Remove && qualifiers on Parse* and add InMemoryMessage::Merge that merges | |
| // two parsed messages | |
| // - Add ConstructEmpty methods on Test | |
| // - Add reflection methods to InMemoryMessage (e.g. Get/Set/Add, and a Has that | |
| // returns TestResult) | |
| // - Add a SerializeIntoMemory method that allows further action on the results | |
| // of serialization instead of immediately returning it | |
| namespace google { | |
| namespace protobuf { | |
| namespace conformance { | |
| namespace internal { | |
| // The strictness of a test. Required tests will fail the test suite if they | |
| // fail. Recommended tests will not fail the test suite if they fail, but will | |
| // be reported as a warning. | |
| enum class TestStrictness { | |
| kRequired = 0, | |
| kRecommended = 1, | |
| }; | |
| // The final result of a conformance test, to be processed by a matcher. | |
| class TestResult { | |
| public: | |
| // The name of the test that was run, useful for failure matching and | |
| // reporting. | |
| absl::string_view name() const { return test_name_; } | |
| // The strictness of the test. | |
| TestStrictness strictness() const { return strictness_; } | |
| // The type of the message that was tested, needed for parsing. | |
| const Descriptor* type() const { return type_; } | |
| // The format of the output that was requested. | |
| ::conformance::WireFormat format() const { return format_; } | |
| // The conformance response that was returned from the testee. This will | |
| // contain either the resulting payload or an error message. | |
| const ::conformance::ConformanceResponse& response() const { | |
| return response_; | |
| } | |
| private: | |
| TestResult(absl::string_view test_name, TestStrictness strictness, | |
| const Descriptor* type, ::conformance::WireFormat format, | |
| ::conformance::ConformanceResponse response) | |
| : test_name_(test_name), | |
| strictness_(strictness), | |
| type_(type), | |
| format_(format), | |
| response_(std::move(response)) {} | |
| friend class InMemoryMessage; | |
| std::string test_name_; | |
| TestStrictness strictness_; | |
| const Descriptor* type_; | |
| ::conformance::WireFormat format_; | |
| ::conformance::ConformanceResponse response_; | |
| }; | |
| // Options for serializing text format. | |
| struct TextSerializationOptions { | |
| bool print_unknown_fields = false; | |
| }; | |
| // This class represents a message held in memory by the testee that can be | |
| // manipulated in various ways. | |
| class InMemoryMessage { | |
| public: | |
| ~InMemoryMessage() = default; | |
| // Serialize the message back in any of our supported formats. These all | |
| // consume the message. | |
| TestResult SerializeBinary() &&; | |
| TestResult SerializeText(TextSerializationOptions options = {}) &&; | |
| TestResult SerializeJson() &&; | |
| private: | |
| InMemoryMessage(class Testee* testee, absl::string_view name, | |
| TestStrictness strictness, const Descriptor* type, | |
| ::conformance::ConformanceRequest request) | |
| : testee_(testee), | |
| name_(name), | |
| strictness_(strictness), | |
| type_(type), | |
| request_(std::move(request)) {} | |
| friend class Test; | |
| TestResult SerializeImpl(::conformance::WireFormat format); | |
| class Testee* testee_; | |
| std::string name_; | |
| TestStrictness strictness_; | |
| const Descriptor* type_; | |
| ::conformance::ConformanceRequest request_; | |
| }; | |
| // Options for parsing JSON. | |
| struct JsonParseOptions { | |
| bool ignore_unknown_fields = false; | |
| }; | |
| // This class represents a single test case representing some interaction with | |
| // the testee. The end result of a test should be a single TestResult. | |
| class Test { | |
| public: | |
| ~Test() = default; | |
| // Parse the message from one of our supported formats into an in-memory | |
| // message for further processing. | |
| InMemoryMessage ParseBinary(const Descriptor* type, Wire input) &&; | |
| InMemoryMessage ParseText(const Descriptor* type, absl::string_view input) &&; | |
| InMemoryMessage ParseJson(const Descriptor* type, absl::string_view input, | |
| JsonParseOptions options = {}) &&; | |
| private: | |
| Test(class Testee* testee, absl::string_view name, TestStrictness strictness) | |
| : testee_(testee), name_(name), strictness_(strictness) {} | |
| friend class Testee; | |
| class Testee* testee_; | |
| std::string name_; | |
| TestStrictness strictness_; | |
| }; | |
| // This class represents an abstraction of the testee. It is used to | |
| // create Test objects that can be used to interact further for testing. | |
| class Testee { | |
| public: | |
| explicit Testee(ConformanceTestRunner* runner) : runner_(runner) {} | |
| Test CreateTest(absl::string_view name, TestStrictness strictness) { | |
| return Test(this, name, strictness); | |
| } | |
| private: | |
| ::conformance::ConformanceResponse Run( | |
| absl::string_view test_name, | |
| const ::conformance::ConformanceRequest& request); | |
| friend class InMemoryMessage; | |
| ConformanceTestRunner* runner_; | |
| absl::flat_hash_set<std::string> test_names_ran_; | |
| }; | |
| } // namespace internal | |
| } // namespace conformance | |
| } // namespace protobuf | |
| } // namespace google | |
| #endif // GOOGLE_PROTOBUF_CONFORMANCE_TESTEE_H__ |