blob: c78f9ea8aba1115f630e23edab7b5ab49e8b2af2 [file] [log] [blame]
Josh Haberman4e63b522015-04-14 13:45:39 -07001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
Josh Haberman4e63b522015-04-14 13:45:39 -07003//
Joshua Haberman8a4bfd62023-09-08 19:26:10 -07004// 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
Josh Haberman4e63b522015-04-14 13:45:39 -07007
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
Mike Kruskal39752aa2023-10-17 16:58:29 -070017#include <cstddef>
18#include <memory>
Rafi Kamal4f02f052019-08-22 16:14:22 -070019#include <string>
20#include <vector>
21
Mike Kruskal28e573e2023-10-17 17:26:08 -070022#include "google/protobuf/descriptor.pb.h"
Mike Kruskal15954172022-09-09 10:42:19 -070023#include "google/protobuf/util/type_resolver.h"
Mike Kruskal2166cce2022-12-21 20:38:13 -080024#include "absl/container/btree_set.h"
25#include "absl/container/flat_hash_set.h"
Mike Kruskal39752aa2023-10-17 16:58:29 -070026#include "absl/strings/string_view.h"
Mike Kruskalca4b0632022-08-11 20:55:01 -070027#include "conformance/conformance.pb.h"
Protobuf Team Bot0287cf42023-08-22 07:26:58 -070028#include "google/protobuf/descriptor.h"
Mike Kruskal60a84c32022-11-08 09:37:29 -080029#include "google/protobuf/wire_format_lite.h"
Feng Xiaoe841bac2015-12-11 17:09:20 -080030
Josh Haberman4e63b522015-04-14 13:45:39 -070031namespace conformance {
32class ConformanceRequest;
33class ConformanceResponse;
34} // namespace conformance
35
Joshua Habermanf1ce60e2016-12-03 11:51:25 -050036namespace protobuf_test_messages {
37namespace proto3 {
Yilun Chong3adb0542017-06-30 17:22:32 -070038class TestAllTypesProto3;
Joshua Habermanf1ce60e2016-12-03 11:51:25 -050039} // namespace proto3
40} // namespace protobuf_test_messages
41
Josh Haberman4e63b522015-04-14 13:45:39 -070042namespace google {
43namespace protobuf {
44
Paul Yang5aeee3d2018-09-06 15:02:04 -070045class ConformanceTestSuite;
46
Josh Haberman4e63b522015-04-14 13:45:39 -070047class ConformanceTestRunner {
48 public:
Feng Xiaoe841bac2015-12-11 17:09:20 -080049 virtual ~ConformanceTestRunner() {}
50
Josh Haberman4e63b522015-04-14 13:45:39 -070051 // Call to run a single conformance test.
52 //
53 // "input" is a serialized conformance.ConformanceRequest.
54 // "output" should be set to a serialized conformance.ConformanceResponse.
55 //
56 // If there is any error in running the test itself, set "runtime_error" in
57 // the response.
Joshua Haberman8a4bfd62023-09-08 19:26:10 -070058 virtual void RunTest(const std::string& test_name, const std::string& input,
Feng Xiaoe841bac2015-12-11 17:09:20 -080059 std::string* output) = 0;
Josh Haberman4e63b522015-04-14 13:45:39 -070060};
61
Paul Yang5aeee3d2018-09-06 15:02:04 -070062// Test runner that spawns the process being tested and communicates with it
63// over a pipe.
64class ForkPipeRunner : public ConformanceTestRunner {
65 public:
Yilun Chongd8c25012019-02-22 18:13:33 +080066 // Note: Run() doesn't take ownership of the pointers inside suites.
Joshua Haberman8a4bfd62023-09-08 19:26:10 -070067 static int Run(int argc, char* argv[],
Yilun Chongd8c25012019-02-22 18:13:33 +080068 const std::vector<ConformanceTestSuite*>& suites);
Paul Yang5aeee3d2018-09-06 15:02:04 -070069
Rafi Kamal4f02f052019-08-22 16:14:22 -070070 ForkPipeRunner(const std::string& executable,
Mike Kruskal32bea522022-10-06 16:48:39 -070071 const std::vector<std::string>& executable_args,
72 bool performance)
Rafi Kamal4f02f052019-08-22 16:14:22 -070073 : child_pid_(-1),
74 executable_(executable),
Mike Kruskal32bea522022-10-06 16:48:39 -070075 executable_args_(executable_args),
76 performance_(performance) {}
Rafi Kamal4f02f052019-08-22 16:14:22 -070077
78 explicit ForkPipeRunner(const std::string& executable)
Paul Yang5aeee3d2018-09-06 15:02:04 -070079 : child_pid_(-1), executable_(executable) {}
80
81 virtual ~ForkPipeRunner() {}
82
Joshua Haberman8a4bfd62023-09-08 19:26:10 -070083 void RunTest(const std::string& test_name, const std::string& request,
Paul Yang5aeee3d2018-09-06 15:02:04 -070084 std::string* response);
85
Paul Yangcecba292018-12-14 16:05:03 -080086 private:
Paul Yang5aeee3d2018-09-06 15:02:04 -070087 void SpawnTestProgram();
88
Joshua Haberman8a4bfd62023-09-08 19:26:10 -070089 void CheckedWrite(int fd, const void* buf, size_t len);
90 bool TryRead(int fd, void* buf, size_t len);
91 void CheckedRead(int fd, void* buf, size_t len);
Paul Yang5aeee3d2018-09-06 15:02:04 -070092
93 int write_fd_;
94 int read_fd_;
95 pid_t child_pid_;
96 std::string executable_;
Joshua Haberman32e5deb2020-04-28 08:40:38 -070097 const std::vector<std::string> executable_args_;
Mike Kruskal32bea522022-10-06 16:48:39 -070098 bool performance_;
Paul Yang5aeee3d2018-09-06 15:02:04 -070099 std::string current_test_name_;
100};
101
Josh Haberman4e63b522015-04-14 13:45:39 -0700102// Class representing the test suite itself. To run it, implement your own
Feng Xiao6bbe1972018-08-08 17:00:41 -0700103// class derived from ConformanceTestRunner, class derived from
104// ConformanceTestSuite and then write code like:
105//
106// class MyConformanceTestSuite : public ConformanceTestSuite {
107// public:
108// void RunSuiteImpl() {
Mike Kruskal701dd832022-08-20 14:22:08 -0700109// // INSERT ACTUAL TESTS.
Feng Xiao6bbe1972018-08-08 17:00:41 -0700110// }
111// };
112//
Josh Haberman4e63b522015-04-14 13:45:39 -0700113// class MyConformanceTestRunner : public ConformanceTestRunner {
114// public:
Paul Yang5aeee3d2018-09-06 15:02:04 -0700115// static int Run(int argc, char *argv[],
116// ConformanceTestSuite* suite);
117//
118// private:
Josh Haberman4e63b522015-04-14 13:45:39 -0700119// virtual void RunTest(...) {
120// // INSERT YOUR FRAMEWORK-SPECIFIC CODE HERE.
121// }
122// };
123//
124// int main() {
Paul Yang5aeee3d2018-09-06 15:02:04 -0700125// MyConformanceTestSuite suite;
126// MyConformanceTestRunner::Run(argc, argv, &suite);
Josh Haberman4e63b522015-04-14 13:45:39 -0700127// }
128//
129class ConformanceTestSuite {
130 public:
Yilun Chongd8c25012019-02-22 18:13:33 +0800131 ConformanceTestSuite()
132 : verbose_(false),
Mike Kruskal32bea522022-10-06 16:48:39 -0700133 performance_(false),
Yilun Chongd8c25012019-02-22 18:13:33 +0800134 enforce_recommended_(false),
Mike Kruskala2ba8bc2023-10-18 13:12:07 -0700135 maximum_edition_(Edition::EDITION_PROTO3),
Yilun Chongd8c25012019-02-22 18:13:33 +0800136 failure_list_flag_name_("--failure_list") {}
Feng Xiao6bbe1972018-08-08 17:00:41 -0700137 virtual ~ConformanceTestSuite() {}
Josh Haberman4e63b522015-04-14 13:45:39 -0700138
Mike Kruskal32bea522022-10-06 16:48:39 -0700139 void SetPerformance(bool performance) { performance_ = performance; }
Josh Haberman181c7f22015-07-15 11:05:10 -0700140 void SetVerbose(bool verbose) { verbose_ = verbose; }
141
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700142 // Whether to require the testee to pass RECOMMENDED tests. By default failing
143 // a RECOMMENDED test case will not fail the entire suite but will only
144 // generated a warning. If this flag is set to true, RECOMMENDED tests will
145 // be treated the same way as REQUIRED tests and failing a RECOMMENDED test
146 // case will cause the entire test suite to fail as well. An implementation
147 // can enable this if it wants to be strictly conforming to protobuf spec.
148 // See the comments about ConformanceLevel below to learn more about the
149 // difference between REQUIRED and RECOMMENDED test cases.
Joshua Haberman8a4bfd62023-09-08 19:26:10 -0700150 void SetEnforceRecommended(bool value) { enforce_recommended_ = value; }
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700151
Mike Kruskala2ba8bc2023-10-18 13:12:07 -0700152 // Sets the maximum edition (inclusive) that should be tests for conformance.
153 void SetMaximumEdition(Edition edition) { maximum_edition_ = edition; }
154
Yilun Chongd8c25012019-02-22 18:13:33 +0800155 // Gets the flag name to the failure list file.
156 // By default, this would return --failure_list
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700157 std::string GetFailureListFlagName() { return failure_list_flag_name_; }
Yilun Chongd8c25012019-02-22 18:13:33 +0800158
159 void SetFailureListFlagName(const std::string& failure_list_flag_name) {
160 failure_list_flag_name_ = failure_list_flag_name;
161 }
162
Joshua Habermandce403a2022-02-08 20:53:31 -0800163 // Sets the path of the output directory.
Joshua Haberman8a4bfd62023-09-08 19:26:10 -0700164 void SetOutputDir(const char* output_dir) { output_dir_ = output_dir; }
Joshua Habermandce403a2022-02-08 20:53:31 -0800165
Josh Haberman4e63b522015-04-14 13:45:39 -0700166 // Run all the conformance tests against the given test runner.
167 // Test output will be stored in "output".
Josh Habermand2b67382015-06-03 12:04:35 -0700168 //
169 // Returns true if the set of failing tests was exactly the same as the
Yilun Chong0adb74c2019-01-08 15:06:30 -0800170 // failure list.
171 // The filename here is *only* used to create/format useful error messages for
172 // how to update the failure list. We do NOT read this file at all.
173 bool RunSuite(ConformanceTestRunner* runner, std::string* output,
174 const std::string& filename,
175 conformance::FailureSet* failure_list);
Josh Haberman4e63b522015-04-14 13:45:39 -0700176
Feng Xiao6bbe1972018-08-08 17:00:41 -0700177 protected:
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700178 // Test cases are classified into a few categories:
179 // REQUIRED: the test case must be passed for an implementation to be
180 // interoperable with other implementations. For example, a
Brian Wignalla104dff2020-01-08 13:18:20 -0500181 // parser implementation must accept both packed and unpacked
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700182 // form of repeated primitive fields.
183 // RECOMMENDED: the test case is not required for the implementation to
184 // be interoperable with other implementations, but is
185 // recommended for best performance and compatibility. For
186 // example, a proto3 serializer should serialize repeated
187 // primitive fields in packed form, but an implementation
188 // failing to do so will still be able to communicate with
189 // other implementations.
190 enum ConformanceLevel {
191 REQUIRED = 0,
192 RECOMMENDED = 1,
193 };
Paul Yang26eeec92018-07-09 14:29:23 -0700194
195 class ConformanceRequestSetting {
196 public:
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700197 ConformanceRequestSetting(ConformanceLevel level,
198 conformance::WireFormat input_format,
199 conformance::WireFormat output_format,
200 conformance::TestCategory test_category,
201 const Message& prototype_message,
202 const std::string& test_name,
203 const std::string& input);
Feng Xiao6bbe1972018-08-08 17:00:41 -0700204 virtual ~ConformanceRequestSetting() {}
Paul Yang26eeec92018-07-09 14:29:23 -0700205
Rafi Kamal58d44202019-11-11 17:06:56 -0800206 std::unique_ptr<Message> NewTestMessage() const;
Paul Yang26eeec92018-07-09 14:29:23 -0700207
Mike Kruskal28e573e2023-10-17 17:26:08 -0700208 std::string GetSyntaxIdentifier() const;
209
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700210 std::string GetTestName() const;
Paul Yang26eeec92018-07-09 14:29:23 -0700211
Feng Xiao6bbe1972018-08-08 17:00:41 -0700212 const conformance::ConformanceRequest& GetRequest() const {
213 return request_;
214 }
Paul Yang26eeec92018-07-09 14:29:23 -0700215
Mike Kruskal39752aa2023-10-17 16:58:29 -0700216 ConformanceLevel GetLevel() const { return level_; }
Paul Yang26eeec92018-07-09 14:29:23 -0700217
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700218 std::string ConformanceLevelToString(ConformanceLevel level) const;
Feng Xiao6bbe1972018-08-08 17:00:41 -0700219
Hao Nguyen2f864fd2019-03-20 11:45:01 -0700220 void SetPrintUnknownFields(bool print_unknown_fields) {
221 request_.set_print_unknown_fields(true);
222 }
223
224 void SetPrototypeMessageForCompare(const Message& message) {
225 prototype_message_for_compare_.reset(message.New());
226 }
227
Feng Xiao6bbe1972018-08-08 17:00:41 -0700228 protected:
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700229 virtual std::string InputFormatString(conformance::WireFormat format) const;
230 virtual std::string OutputFormatString(
231 conformance::WireFormat format) const;
Paul Yangcecba292018-12-14 16:05:03 -0800232 conformance::ConformanceRequest request_;
Feng Xiao6bbe1972018-08-08 17:00:41 -0700233
Paul Yang26eeec92018-07-09 14:29:23 -0700234 private:
235 ConformanceLevel level_;
Feng Xiao6bbe1972018-08-08 17:00:41 -0700236 ::conformance::WireFormat input_format_;
237 ::conformance::WireFormat output_format_;
238 const Message& prototype_message_;
Hao Nguyen2f864fd2019-03-20 11:45:01 -0700239 std::unique_ptr<Message> prototype_message_for_compare_;
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700240 std::string test_name_;
Paul Yang26eeec92018-07-09 14:29:23 -0700241 };
242
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700243 std::string WireFormatToString(conformance::WireFormat wire_format);
Paul Yangcecba292018-12-14 16:05:03 -0800244
245 // Parse payload in the response to the given message. Returns true on
246 // success.
Joshua Haberman8a4bfd62023-09-08 19:26:10 -0700247 virtual bool ParseResponse(const conformance::ConformanceResponse& response,
248 const ConformanceRequestSetting& setting,
249 Message* test_message) = 0;
Paul Yangcecba292018-12-14 16:05:03 -0800250
Rafi Kamal4f02f052019-08-22 16:14:22 -0700251 void VerifyResponse(const ConformanceRequestSetting& setting,
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700252 const std::string& equivalent_wire_format,
Rafi Kamal4f02f052019-08-22 16:14:22 -0700253 const conformance::ConformanceResponse& response,
254 bool need_report_success, bool require_same_wire_format);
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700255
Mike Kruskal32bea522022-10-06 16:48:39 -0700256 void TruncateDebugPayload(std::string* payload);
Mike Kruskal39752aa2023-10-17 16:58:29 -0700257 conformance::ConformanceRequest TruncateRequest(
Mike Kruskal32bea522022-10-06 16:48:39 -0700258 const conformance::ConformanceRequest& request);
Mike Kruskal39752aa2023-10-17 16:58:29 -0700259 conformance::ConformanceResponse TruncateResponse(
Mike Kruskal32bea522022-10-06 16:48:39 -0700260 const conformance::ConformanceResponse& response);
261
Josh Habermand2b67382015-06-03 12:04:35 -0700262 void ReportSuccess(const std::string& test_name);
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700263 void ReportFailure(const std::string& test_name, ConformanceLevel level,
Josh Habermanb0500b32015-07-10 16:36:59 -0700264 const conformance::ConformanceRequest& request,
265 const conformance::ConformanceResponse& response,
Mike Kruskale99b2812022-09-23 14:27:46 -0700266 absl::string_view message);
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700267 void ReportSkip(const std::string& test_name,
Josh Habermanb0500b32015-07-10 16:36:59 -0700268 const conformance::ConformanceRequest& request,
269 const conformance::ConformanceResponse& response);
Feng Xiao6bbe1972018-08-08 17:00:41 -0700270
Paul Yang26eeec92018-07-09 14:29:23 -0700271 void RunValidInputTest(const ConformanceRequestSetting& setting,
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700272 const std::string& equivalent_text_format);
Paul Yang26eeec92018-07-09 14:29:23 -0700273 void RunValidBinaryInputTest(const ConformanceRequestSetting& setting,
Joshua Haberman32e5deb2020-04-28 08:40:38 -0700274 const std::string& equivalent_wire_format,
Paul Yanga9bb6562019-07-26 10:05:14 -0700275 bool require_same_wire_format = false);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800276
Feng Xiao6bbe1972018-08-08 17:00:41 -0700277 void RunTest(const std::string& test_name,
278 const conformance::ConformanceRequest& request,
279 conformance::ConformanceResponse* response);
280
Yilun Chongd8c25012019-02-22 18:13:33 +0800281 void AddExpectedFailedTest(const std::string& test_name);
282
Feng Xiao6bbe1972018-08-08 17:00:41 -0700283 virtual void RunSuiteImpl() = 0;
284
Josh Haberman4e63b522015-04-14 13:45:39 -0700285 ConformanceTestRunner* runner_;
286 int successes_;
Josh Habermanb0500b32015-07-10 16:36:59 -0700287 int expected_failures_;
Josh Haberman4e63b522015-04-14 13:45:39 -0700288 bool verbose_;
Mike Kruskal32bea522022-10-06 16:48:39 -0700289 bool performance_;
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700290 bool enforce_recommended_;
Mike Kruskala2ba8bc2023-10-18 13:12:07 -0700291 Edition maximum_edition_;
Josh Haberman4e63b522015-04-14 13:45:39 -0700292 std::string output_;
Joshua Habermandce403a2022-02-08 20:53:31 -0800293 std::string output_dir_;
Yilun Chongd8c25012019-02-22 18:13:33 +0800294 std::string failure_list_flag_name_;
Josh Habermanef7894e2016-05-16 12:45:02 -0700295 std::string failure_list_filename_;
Josh Habermand2b67382015-06-03 12:04:35 -0700296
297 // The set of test names that are expected to fail in this run, but haven't
298 // failed yet.
Mike Kruskal2166cce2022-12-21 20:38:13 -0800299 absl::btree_set<std::string> expected_to_fail_;
Josh Habermand2b67382015-06-03 12:04:35 -0700300
301 // The set of test names that have been run. Used to ensure that there are no
302 // duplicate names in the suite.
Mike Kruskal2166cce2022-12-21 20:38:13 -0800303 absl::flat_hash_set<std::string> test_names_;
Josh Habermand2b67382015-06-03 12:04:35 -0700304
305 // The set of tests that failed, but weren't expected to.
Mike Kruskal2166cce2022-12-21 20:38:13 -0800306 absl::btree_set<std::string> unexpected_failing_tests_;
Josh Habermand2b67382015-06-03 12:04:35 -0700307
308 // The set of tests that succeeded, but weren't expected to.
Mike Kruskal2166cce2022-12-21 20:38:13 -0800309 absl::btree_set<std::string> unexpected_succeeding_tests_;
Josh Habermanb0500b32015-07-10 16:36:59 -0700310
311 // The set of tests that the testee opted out of;
Mike Kruskal2166cce2022-12-21 20:38:13 -0800312 absl::btree_set<std::string> skipped_;
Josh Haberman4e63b522015-04-14 13:45:39 -0700313};
314
Josh Haberman4e63b522015-04-14 13:45:39 -0700315} // namespace protobuf
316} // namespace google
317
318#endif // CONFORMANCE_CONFORMANCE_TEST_H