conformance_test.h 11.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


// This file defines a protocol for running the conformance test suite
// in-process.  In other words, the suite itself will run in the same process as
// the code under test.
//
// For pros and cons of this approach, please see conformance.proto.

#ifndef CONFORMANCE_CONFORMANCE_TEST_H
#define CONFORMANCE_CONFORMANCE_TEST_H

41
#include <functional>
42
#include <string>
43 44

#include <google/protobuf/descriptor.h>
45 46
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/util/type_resolver.h>
47 48
#include <google/protobuf/wire_format_lite.h>

49
#include "conformance.pb.h"
50

51 52 53 54 55
namespace conformance {
class ConformanceRequest;
class ConformanceResponse;
}  // namespace conformance

56 57
namespace protobuf_test_messages {
namespace proto3 {
58
class TestAllTypesProto3;
59 60 61
}  // namespace proto3
}  // namespace protobuf_test_messages

62 63 64
namespace google {
namespace protobuf {

65 66
class ConformanceTestSuite;

67 68
class ConformanceTestRunner {
 public:
69 70
  virtual ~ConformanceTestRunner() {}

71 72 73 74 75 76 77
  // Call to run a single conformance test.
  //
  // "input" is a serialized conformance.ConformanceRequest.
  // "output" should be set to a serialized conformance.ConformanceResponse.
  //
  // If there is any error in running the test itself, set "runtime_error" in
  // the response.
78 79 80
  virtual void RunTest(const std::string& test_name,
                       const std::string& input,
                       std::string* output) = 0;
81 82
};

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
// Test runner that spawns the process being tested and communicates with it
// over a pipe.
class ForkPipeRunner : public ConformanceTestRunner {
 public:
  static int Run(int argc, char *argv[],
                 ConformanceTestSuite* suite);

 private:
  ForkPipeRunner(const std::string &executable)
      : child_pid_(-1), executable_(executable) {}

  virtual ~ForkPipeRunner() {}

  void RunTest(const std::string& test_name,
               const std::string& request,
               std::string* response);

  // TODO(haberman): make this work on Windows, instead of using these
  // UNIX-specific APIs.
  //
  // There is a platform-agnostic API in
  //    src/google/protobuf/compiler/subprocess.h
  //
  // However that API only supports sending a single message to the subprocess.
  // We really want to be able to send messages and receive responses one at a
  // time:
  //
  // 1. Spawning a new process for each test would take way too long for thousands
  //    of tests and subprocesses like java that can take 100ms or more to start
  //    up.
  //
  // 2. Sending all the tests in one big message and receiving all results in one
  //    big message would take away our visibility about which test(s) caused a
  //    crash or other fatal error.  It would also give us only a single failure
  //    instead of all of them.
  void SpawnTestProgram();

  void CheckedWrite(int fd, const void *buf, size_t len);
  bool TryRead(int fd, void *buf, size_t len);
  void CheckedRead(int fd, void *buf, size_t len);

  int write_fd_;
  int read_fd_;
  pid_t child_pid_;
  std::string executable_;
  std::string current_test_name_;
};

131
// Class representing the test suite itself.  To run it, implement your own
132 133 134 135 136 137 138 139 140 141
// class derived from ConformanceTestRunner, class derived from
// ConformanceTestSuite and then write code like:
//
//    class MyConformanceTestSuite : public ConformanceTestSuite {
//     public:
//      void RunSuiteImpl() {
//        // INSERT ACTURAL TESTS.
//      }
//    };
//
142 143
//    class MyConformanceTestRunner : public ConformanceTestRunner {
//     public:
144 145 146 147
//      static int Run(int argc, char *argv[],
//                 ConformanceTestSuite* suite);
//
//     private:
148 149 150 151 152 153
//      virtual void RunTest(...) {
//        // INSERT YOUR FRAMEWORK-SPECIFIC CODE HERE.
//      }
//    };
//
//    int main() {
154 155
//      MyConformanceTestSuite suite;
//      MyConformanceTestRunner::Run(argc, argv, &suite);
156 157 158 159
//    }
//
class ConformanceTestSuite {
 public:
Bo Yang's avatar
Bo Yang committed
160
  ConformanceTestSuite() : verbose_(false), enforce_recommended_(false) {}
161
  virtual ~ConformanceTestSuite() {}
162

163 164
  void SetVerbose(bool verbose) { verbose_ = verbose; }

165 166 167
  // Sets the list of tests that are expected to fail when RunSuite() is called.
  // RunSuite() will fail unless the set of failing tests is exactly the same
  // as this list.
168 169 170 171 172
  //
  // The filename here is *only* used to create/format useful error messages for
  // how to update the failure list.  We do NOT read this file at all.
  void SetFailureList(const std::string& filename,
                      const std::vector<std::string>& failure_list);
173

Bo Yang's avatar
Bo Yang committed
174 175 176 177 178 179 180 181 182 183 184 185
  // Whether to require the testee to pass RECOMMENDED tests. By default failing
  // a RECOMMENDED test case will not fail the entire suite but will only
  // generated a warning. If this flag is set to true, RECOMMENDED tests will
  // be treated the same way as REQUIRED tests and failing a RECOMMENDED test
  // case will cause the entire test suite to fail as well. An implementation
  // can enable this if it wants to be strictly conforming to protobuf spec.
  // See the comments about ConformanceLevel below to learn more about the
  // difference between REQUIRED and RECOMMENDED test cases.
  void SetEnforceRecommended(bool value) {
    enforce_recommended_ = value;
  }

186 187
  // Run all the conformance tests against the given test runner.
  // Test output will be stored in "output".
188 189 190 191 192
  //
  // Returns true if the set of failing tests was exactly the same as the
  // failure list.  If SetFailureList() was not called, returns true if all
  // tests passed.
  bool RunSuite(ConformanceTestRunner* runner, std::string* output);
193

194
 protected:
Bo Yang's avatar
Bo Yang committed
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
  // Test cases are classified into a few categories:
  //   REQUIRED: the test case must be passed for an implementation to be
  //             interoperable with other implementations. For example, a
  //             parser implementaiton must accept both packed and unpacked
  //             form of repeated primitive fields.
  //   RECOMMENDED: the test case is not required for the implementation to
  //                be interoperable with other implementations, but is
  //                recommended for best performance and compatibility. For
  //                example, a proto3 serializer should serialize repeated
  //                primitive fields in packed form, but an implementation
  //                failing to do so will still be able to communicate with
  //                other implementations.
  enum ConformanceLevel {
    REQUIRED = 0,
    RECOMMENDED = 1,
  };
211 212 213 214

  class ConformanceRequestSetting {
   public:
    ConformanceRequestSetting(
215 216 217 218 219
        ConformanceLevel level,
        conformance::WireFormat input_format,
        conformance::WireFormat output_format,
        conformance::TestCategory test_category,
        const Message& prototype_message,
220
        const string& test_name, const string& input);
221 222 223
    virtual ~ConformanceRequestSetting() {}

    Message* GetTestMessage() const;
224

225
    string GetTestName() const;
226

227 228 229
    const conformance::ConformanceRequest& GetRequest() const {
      return request_;
    }
230

231 232 233
    const ConformanceLevel GetLevel() const {
      return level_;
    }
234

235 236 237 238 239
    string ConformanceLevelToString(ConformanceLevel level) const;

   protected:
    virtual string InputFormatString(conformance::WireFormat format) const;
    virtual string OutputFormatString(conformance::WireFormat format) const;
240 241 242

   private:
    ConformanceLevel level_;
243 244 245
    ::conformance::WireFormat input_format_;
    ::conformance::WireFormat output_format_;
    const Message& prototype_message_;
246 247 248 249
    string test_name_;
    conformance::ConformanceRequest request_;
  };

250 251
  bool CheckSetEmpty(const std::set<string>& set_to_check,
                     const std::string& write_to_file, const std::string& msg);
Bo Yang's avatar
Bo Yang committed
252

253
  void ReportSuccess(const std::string& test_name);
254
  void ReportFailure(const string& test_name,
Bo Yang's avatar
Bo Yang committed
255
                     ConformanceLevel level,
256 257 258 259 260 261
                     const conformance::ConformanceRequest& request,
                     const conformance::ConformanceResponse& response,
                     const char* fmt, ...);
  void ReportSkip(const string& test_name,
                  const conformance::ConformanceRequest& request,
                  const conformance::ConformanceResponse& response);
262

263 264 265 266
  void RunValidInputTest(const ConformanceRequestSetting& setting,
                         const string& equivalent_text_format);
  void RunValidBinaryInputTest(const ConformanceRequestSetting& setting,
                               const string& equivalent_wire_format);
267 268 269 270 271 272 273

  void RunTest(const std::string& test_name,
               const conformance::ConformanceRequest& request,
               conformance::ConformanceResponse* response);

  virtual void RunSuiteImpl() = 0;

274 275
  ConformanceTestRunner* runner_;
  int successes_;
276
  int expected_failures_;
277
  bool verbose_;
Bo Yang's avatar
Bo Yang committed
278
  bool enforce_recommended_;
279
  std::string output_;
280
  std::string failure_list_filename_;
281 282 283 284 285 286 287 288 289 290 291 292 293 294

  // The set of test names that are expected to fail in this run, but haven't
  // failed yet.
  std::set<std::string> expected_to_fail_;

  // The set of test names that have been run.  Used to ensure that there are no
  // duplicate names in the suite.
  std::set<std::string> test_names_;

  // The set of tests that failed, but weren't expected to.
  std::set<std::string> unexpected_failing_tests_;

  // The set of tests that succeeded, but weren't expected to.
  std::set<std::string> unexpected_succeeding_tests_;
295 296 297 298

  // The set of tests that the testee opted out of;
  std::set<std::string> skipped_;

299 300
  std::unique_ptr<google::protobuf::util::TypeResolver>
      type_resolver_;
301
  std::string type_url_;
302 303 304 305 306 307
};

}  // namespace protobuf
}  // namespace google

#endif  // CONFORMANCE_CONFORMANCE_TEST_H