• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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