| #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 "conformance/binary_wireformat.h" |
| #include "conformance/conformance.pb.h" |
| #include "conformance/test_runner.h" |
| #include "google/protobuf/descriptor.h" |
| |
| // The APIs conformance tests use to interact with a testee. They are |
| // deliberately decoupled from the runner/testee protocol that implements |
| // them. That keeps them flexible for tests. |
| // |
| // Tests should never need to name any of these types directly. A test |
| // obtains a Test object for the global testee from Testee() (see |
| // test_fixture.h), chains operations on it and passes the final |
| // TestResult to Yields() (see matchers.h): |
| // |
| // EXPECT_THAT(Testee() |
| // .ParseBinary(TestAllTypesProto2::descriptor(), input) |
| // .SerializeBinary(), |
| // Yields(WhenParsed(EqualsBinaryProto(input)))); |
| |
| // 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 { |
| |
| // How important it is that an implementation passes a test. kP0 is the |
| // baseline every implementation must pass. Every priority is enforced |
| // unless the enforcement level (see below) leaves some out. A failing kP1 |
| // test is then counted but not failed, unless it is in the failure list (see |
| // matchers.h). Test names still spell kP0 as "Required" and kP1 as |
| // "Recommended" (see PriorityLevelName()). |
| // |
| // A suite declares its priority with ConformanceTest::DefaultPriority(). A |
| // single test overrides it with Testee(priority); see test_fixture.h. |
| // TODO: b/564550230 - rename the levels in test names to P0/P1 once every |
| // suite has been triaged. |
| enum class TestPriority { kP0 = 0, kP1 = 1 }; |
| |
| // The priorities, spelled the way suites write them: Testee(kP1). |
| inline constexpr TestPriority kP0 = TestPriority::kP0; |
| inline constexpr TestPriority kP1 = TestPriority::kP1; |
| |
| // The lowest priority there is. An enforcement level (see TestManager in |
| // test_manager.h) is the lowest priority whose unlisted failures fail the run; |
| // kLowestPriority, the default, enforces every priority. |
| inline constexpr TestPriority kLowestPriority = kP1; |
| |
| // The name of a priority: "P0" or "P1". |
| absl::string_view PriorityName(TestPriority priority); |
| |
| // The level a priority is named with in test names, until the rename (see |
| // TestPriority): "Required" for kP0, "Recommended" for kP1. |
| absl::string_view PriorityLevelName(TestPriority priority); |
| |
| namespace internal { |
| |
| // 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 priority of the test (see TestPriority). Yields() tolerates a |
| // failing kP1 test only if the enforcement level leaves kP1 out and the |
| // test isn't listed. |
| TestPriority priority() const { return priority_; } |
| |
| // 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, TestPriority priority, |
| const Descriptor* type, ::conformance::WireFormat format, |
| ::conformance::ConformanceResponse response) |
| : test_name_(test_name), |
| priority_(priority), |
| type_(type), |
| format_(format), |
| response_(std::move(response)) {} |
| friend class InMemoryMessage; |
| |
| std::string test_name_; |
| TestPriority priority_; |
| 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, |
| TestPriority priority, const Descriptor* type, |
| ::conformance::ConformanceRequest request) |
| : testee_(testee), |
| name_(name), |
| priority_(priority), |
| type_(type), |
| request_(std::move(request)) {} |
| friend class Test; |
| |
| TestResult SerializeImpl(::conformance::WireFormat format); |
| |
| class Testee* testee_; |
| std::string name_; |
| TestPriority priority_; |
| 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, TestPriority priority) |
| : testee_(testee), name_(name), priority_(priority) {} |
| friend class Testee; |
| |
| class Testee* testee_; |
| std::string name_; |
| TestPriority priority_; |
| }; |
| |
| // 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, TestPriority priority) { |
| return Test(this, name, priority); |
| } |
| |
| 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__ |