Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Side by Side Diff: third_party/protobuf/src/google/protobuf/compiler/importer_unittest.cc

Issue 1322483002: Revert https://codereview.chromium.org/1291903002 (protobuf roll). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Protocol Buffers - Google's data interchange format 1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved. 2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/ 3 // http://code.google.com/p/protobuf/
4 // 4 //
5 // Redistribution and use in source and binary forms, with or without 5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are 6 // modification, are permitted provided that the following conditions are
7 // met: 7 // met:
8 // 8 //
9 // * Redistributions of source code must retain the above copyright 9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer. 10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above 11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer 12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the 13 // in the documentation and/or other materials provided with the
(...skipping 12 matching lines...) Expand all
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 30
31 // Author: kenton@google.com (Kenton Varda) 31 // Author: kenton@google.com (Kenton Varda)
32 // Based on original Protocol Buffers design by 32 // Based on original Protocol Buffers design by
33 // Sanjay Ghemawat, Jeff Dean, and others. 33 // Sanjay Ghemawat, Jeff Dean, and others.
34 34
35 #include <google/protobuf/stubs/hash.h> 35 #include <google/protobuf/stubs/hash.h>
36 #include <memory>
37 #ifndef _SHARED_PTR_H
38 #include <google/protobuf/stubs/shared_ptr.h>
39 #endif
40 36
41 #include <google/protobuf/compiler/importer.h> 37 #include <google/protobuf/compiler/importer.h>
42 #include <google/protobuf/descriptor.h> 38 #include <google/protobuf/descriptor.h>
43 #include <google/protobuf/testing/file.h>
44 #include <google/protobuf/io/zero_copy_stream_impl.h> 39 #include <google/protobuf/io/zero_copy_stream_impl.h>
45 40
46 #include <google/protobuf/stubs/map_util.h> 41 #include <google/protobuf/stubs/map-util.h>
47 #include <google/protobuf/stubs/common.h> 42 #include <google/protobuf/stubs/common.h>
48 #include <google/protobuf/testing/file.h> 43 #include <google/protobuf/testing/file.h>
49 #include <google/protobuf/stubs/strutil.h> 44 #include <google/protobuf/stubs/strutil.h>
50 #include <google/protobuf/stubs/substitute.h> 45 #include <google/protobuf/stubs/substitute.h>
51 #include <google/protobuf/testing/googletest.h> 46 #include <google/protobuf/testing/googletest.h>
52 #include <gtest/gtest.h> 47 #include <gtest/gtest.h>
53 48
54 namespace google { 49 namespace google {
55 namespace protobuf { 50 namespace protobuf {
56 namespace compiler { 51 namespace compiler {
57 52
58 namespace { 53 namespace {
59 54
60 bool FileExists(const string& path) {
61 return File::Exists(path);
62 }
63
64 #define EXPECT_SUBSTRING(needle, haystack) \ 55 #define EXPECT_SUBSTRING(needle, haystack) \
65 EXPECT_PRED_FORMAT2(testing::IsSubstring, (needle), (haystack)) 56 EXPECT_PRED_FORMAT2(testing::IsSubstring, (needle), (haystack))
66 57
67 class MockErrorCollector : public MultiFileErrorCollector { 58 class MockErrorCollector : public MultiFileErrorCollector {
68 public: 59 public:
69 MockErrorCollector() {} 60 MockErrorCollector() {}
70 ~MockErrorCollector() {} 61 ~MockErrorCollector() {}
71 62
72 string text_; 63 string text_;
73 64
(...skipping 20 matching lines...) Expand all
94 // implements SourceTree ------------------------------------------- 85 // implements SourceTree -------------------------------------------
95 io::ZeroCopyInputStream* Open(const string& filename) { 86 io::ZeroCopyInputStream* Open(const string& filename) {
96 const char* contents = FindPtrOrNull(files_, filename); 87 const char* contents = FindPtrOrNull(files_, filename);
97 if (contents == NULL) { 88 if (contents == NULL) {
98 return NULL; 89 return NULL;
99 } else { 90 } else {
100 return new io::ArrayInputStream(contents, strlen(contents)); 91 return new io::ArrayInputStream(contents, strlen(contents));
101 } 92 }
102 } 93 }
103 94
104 string GetLastErrorMessage() {
105 return "File not found.";
106 }
107
108 private: 95 private:
109 hash_map<string, const char*> files_; 96 hash_map<string, const char*> files_;
110 }; 97 };
111 98
112 // =================================================================== 99 // ===================================================================
113 100
114 class ImporterTest : public testing::Test { 101 class ImporterTest : public testing::Test {
115 protected: 102 protected:
116 ImporterTest() 103 ImporterTest()
117 : importer_(&source_tree_, &error_collector_) {} 104 : importer_(&source_tree_, &error_collector_) {}
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 EXPECT_EQ( 203 EXPECT_EQ(
217 "recursive1.proto:-1:0: File recursively imports itself: recursive1.proto " 204 "recursive1.proto:-1:0: File recursively imports itself: recursive1.proto "
218 "-> recursive2.proto -> recursive1.proto\n" 205 "-> recursive2.proto -> recursive1.proto\n"
219 "recursive2.proto:-1:0: Import \"recursive1.proto\" was not found " 206 "recursive2.proto:-1:0: Import \"recursive1.proto\" was not found "
220 "or had errors.\n" 207 "or had errors.\n"
221 "recursive1.proto:-1:0: Import \"recursive2.proto\" was not found " 208 "recursive1.proto:-1:0: Import \"recursive2.proto\" was not found "
222 "or had errors.\n", 209 "or had errors.\n",
223 error_collector_.text_); 210 error_collector_.text_);
224 } 211 }
225 212
213 // TODO(sanjay): The MapField tests below more properly belong in
214 // descriptor_unittest, but are more convenient to test here.
215 TEST_F(ImporterTest, MapFieldValid) {
216 AddFile(
217 "map.proto",
218 "syntax = \"proto2\";\n"
219 "message Item {\n"
220 " required string key = 1;\n"
221 "}\n"
222 "message Map {\n"
223 " repeated Item items = 1 [experimental_map_key = \"key\"];\n"
224 "}\n"
225 );
226 const FileDescriptor* file = importer_.Import("map.proto");
227 ASSERT_TRUE(file != NULL) << error_collector_.text_;
228 EXPECT_EQ("", error_collector_.text_);
229
230 // Check that Map::items points to Item::key
231 const Descriptor* item_type = file->FindMessageTypeByName("Item");
232 ASSERT_TRUE(item_type != NULL);
233 const Descriptor* map_type = file->FindMessageTypeByName("Map");
234 ASSERT_TRUE(map_type != NULL);
235 const FieldDescriptor* key_field = item_type->FindFieldByName("key");
236 ASSERT_TRUE(key_field != NULL);
237 const FieldDescriptor* items_field = map_type->FindFieldByName("items");
238 ASSERT_TRUE(items_field != NULL);
239 EXPECT_EQ(items_field->experimental_map_key(), key_field);
240 }
241
242 TEST_F(ImporterTest, MapFieldNotRepeated) {
243 AddFile(
244 "map.proto",
245 "syntax = \"proto2\";\n"
246 "message Item {\n"
247 " required string key = 1;\n"
248 "}\n"
249 "message Map {\n"
250 " required Item items = 1 [experimental_map_key = \"key\"];\n"
251 "}\n"
252 );
253 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
254 EXPECT_SUBSTRING("only allowed for repeated fields", error());
255 }
256
257 TEST_F(ImporterTest, MapFieldNotMessageType) {
258 AddFile(
259 "map.proto",
260 "syntax = \"proto2\";\n"
261 "message Map {\n"
262 " repeated int32 items = 1 [experimental_map_key = \"key\"];\n"
263 "}\n"
264 );
265 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
266 EXPECT_SUBSTRING("only allowed for fields with a message type", error());
267 }
268
269 TEST_F(ImporterTest, MapFieldTypeNotFound) {
270 AddFile(
271 "map.proto",
272 "syntax = \"proto2\";\n"
273 "message Map {\n"
274 " repeated Unknown items = 1 [experimental_map_key = \"key\"];\n"
275 "}\n"
276 );
277 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
278 EXPECT_SUBSTRING("not defined", error());
279 }
280
281 TEST_F(ImporterTest, MapFieldKeyNotFound) {
282 AddFile(
283 "map.proto",
284 "syntax = \"proto2\";\n"
285 "message Item {\n"
286 " required string key = 1;\n"
287 "}\n"
288 "message Map {\n"
289 " repeated Item items = 1 [experimental_map_key = \"badkey\"];\n"
290 "}\n"
291 );
292 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
293 EXPECT_SUBSTRING("Could not find field", error());
294 }
295
296 TEST_F(ImporterTest, MapFieldKeyRepeated) {
297 AddFile(
298 "map.proto",
299 "syntax = \"proto2\";\n"
300 "message Item {\n"
301 " repeated string key = 1;\n"
302 "}\n"
303 "message Map {\n"
304 " repeated Item items = 1 [experimental_map_key = \"key\"];\n"
305 "}\n"
306 );
307 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
308 EXPECT_SUBSTRING("must not name a repeated field", error());
309 }
310
311 TEST_F(ImporterTest, MapFieldKeyNotScalar) {
312 AddFile(
313 "map.proto",
314 "syntax = \"proto2\";\n"
315 "message ItemKey { }\n"
316 "message Item {\n"
317 " required ItemKey key = 1;\n"
318 "}\n"
319 "message Map {\n"
320 " repeated Item items = 1 [experimental_map_key = \"key\"];\n"
321 "}\n"
322 );
323 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
324 EXPECT_SUBSTRING("must name a scalar or string", error());
325 }
226 326
227 // =================================================================== 327 // ===================================================================
228 328
229 class DiskSourceTreeTest : public testing::Test { 329 class DiskSourceTreeTest : public testing::Test {
230 protected: 330 protected:
231 virtual void SetUp() { 331 virtual void SetUp() {
232 dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_1"); 332 dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_1");
233 dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_2"); 333 dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_2");
234 334
235 for (int i = 0; i < dirnames_.size(); i++) { 335 for (int i = 0; i < dirnames_.size(); i++) {
236 if (FileExists(dirnames_[i])) { 336 if (File::Exists(dirnames_[i])) {
237 File::DeleteRecursively(dirnames_[i], NULL, NULL); 337 File::DeleteRecursively(dirnames_[i], NULL, NULL);
238 } 338 }
239 GOOGLE_CHECK_OK(File::CreateDir(dirnames_[i], 0777)); 339 GOOGLE_CHECK(File::CreateDir(dirnames_[i].c_str(), DEFAULT_FILE_MODE));
240 } 340 }
241 } 341 }
242 342
243 virtual void TearDown() { 343 virtual void TearDown() {
244 for (int i = 0; i < dirnames_.size(); i++) { 344 for (int i = 0; i < dirnames_.size(); i++) {
245 if (FileExists(dirnames_[i])) { 345 File::DeleteRecursively(dirnames_[i], NULL, NULL);
246 File::DeleteRecursively(dirnames_[i], NULL, NULL);
247 }
248 } 346 }
249 } 347 }
250 348
251 void AddFile(const string& filename, const char* contents) { 349 void AddFile(const string& filename, const char* contents) {
252 GOOGLE_CHECK_OK(File::SetContents(filename, contents, true)); 350 File::WriteStringToFileOrDie(contents, filename);
253 } 351 }
254 352
255 void AddSubdir(const string& dirname) { 353 void AddSubdir(const string& dirname) {
256 GOOGLE_CHECK_OK(File::CreateDir(dirname, 0777)); 354 GOOGLE_CHECK(File::CreateDir(dirname.c_str(), DEFAULT_FILE_MODE));
257 } 355 }
258 356
259 void ExpectFileContents(const string& filename, 357 void ExpectFileContents(const string& filename,
260 const char* expected_contents) { 358 const char* expected_contents) {
261 google::protobuf::scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Ope n(filename)); 359 scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Open(filename));
262 360
263 ASSERT_FALSE(input == NULL); 361 ASSERT_FALSE(input == NULL);
264 362
265 // Read all the data from the file. 363 // Read all the data from the file.
266 string file_contents; 364 string file_contents;
267 const void* data; 365 const void* data;
268 int size; 366 int size;
269 while (input->Next(&data, &size)) { 367 while (input->Next(&data, &size)) {
270 file_contents.append(reinterpret_cast<const char*>(data), size); 368 file_contents.append(reinterpret_cast<const char*>(data), size);
271 } 369 }
272 370
273 EXPECT_EQ(expected_contents, file_contents); 371 EXPECT_EQ(expected_contents, file_contents);
274 } 372 }
275 373
276 void ExpectCannotOpenFile(const string& filename, 374 void ExpectFileNotFound(const string& filename) {
277 const string& error_message) { 375 scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Open(filename));
278 google::protobuf::scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Ope n(filename));
279 EXPECT_TRUE(input == NULL); 376 EXPECT_TRUE(input == NULL);
280 EXPECT_EQ(error_message, source_tree_.GetLastErrorMessage());
281 } 377 }
282 378
283 DiskSourceTree source_tree_; 379 DiskSourceTree source_tree_;
284 380
285 // Paths of two on-disk directories to use during the test. 381 // Paths of two on-disk directories to use during the test.
286 vector<string> dirnames_; 382 vector<string> dirnames_;
287 }; 383 };
288 384
289 TEST_F(DiskSourceTreeTest, MapRoot) { 385 TEST_F(DiskSourceTreeTest, MapRoot) {
290 // Test opening a file in a directory that is mapped to the root of the 386 // Test opening a file in a directory that is mapped to the root of the
291 // source tree. 387 // source tree.
292 AddFile(dirnames_[0] + "/foo", "Hello World!"); 388 AddFile(dirnames_[0] + "/foo", "Hello World!");
293 source_tree_.MapPath("", dirnames_[0]); 389 source_tree_.MapPath("", dirnames_[0]);
294 390
295 ExpectFileContents("foo", "Hello World!"); 391 ExpectFileContents("foo", "Hello World!");
296 ExpectCannotOpenFile("bar", "File not found."); 392 ExpectFileNotFound("bar");
297 } 393 }
298 394
299 TEST_F(DiskSourceTreeTest, MapDirectory) { 395 TEST_F(DiskSourceTreeTest, MapDirectory) {
300 // Test opening a file in a directory that is mapped to somewhere other 396 // Test opening a file in a directory that is mapped to somewhere other
301 // than the root of the source tree. 397 // than the root of the source tree.
302 398
303 AddFile(dirnames_[0] + "/foo", "Hello World!"); 399 AddFile(dirnames_[0] + "/foo", "Hello World!");
304 source_tree_.MapPath("baz", dirnames_[0]); 400 source_tree_.MapPath("baz", dirnames_[0]);
305 401
306 ExpectFileContents("baz/foo", "Hello World!"); 402 ExpectFileContents("baz/foo", "Hello World!");
307 ExpectCannotOpenFile("baz/bar", "File not found."); 403 ExpectFileNotFound("baz/bar");
308 ExpectCannotOpenFile("foo", "File not found."); 404 ExpectFileNotFound("foo");
309 ExpectCannotOpenFile("bar", "File not found."); 405 ExpectFileNotFound("bar");
310 406
311 // Non-canonical file names should not work. 407 // Non-canonical file names should not work.
312 ExpectCannotOpenFile("baz//foo", 408 ExpectFileNotFound("baz//foo");
313 "Backslashes, consecutive slashes, \".\", or \"..\" are " 409 ExpectFileNotFound("baz/../baz/foo");
314 "not allowed in the virtual path"); 410 ExpectFileNotFound("baz/./foo");
315 ExpectCannotOpenFile("baz/../baz/foo", 411 ExpectFileNotFound("baz/foo/");
316 "Backslashes, consecutive slashes, \".\", or \"..\" are "
317 "not allowed in the virtual path");
318 ExpectCannotOpenFile("baz/./foo",
319 "Backslashes, consecutive slashes, \".\", or \"..\" are "
320 "not allowed in the virtual path");
321 ExpectCannotOpenFile("baz/foo/", "File not found.");
322 } 412 }
323 413
324 TEST_F(DiskSourceTreeTest, NoParent) { 414 TEST_F(DiskSourceTreeTest, NoParent) {
325 // Test that we cannot open files in a parent of a mapped directory. 415 // Test that we cannot open files in a parent of a mapped directory.
326 416
327 AddFile(dirnames_[0] + "/foo", "Hello World!"); 417 AddFile(dirnames_[0] + "/foo", "Hello World!");
328 AddSubdir(dirnames_[0] + "/bar"); 418 AddSubdir(dirnames_[0] + "/bar");
329 AddFile(dirnames_[0] + "/bar/baz", "Blah."); 419 AddFile(dirnames_[0] + "/bar/baz", "Blah.");
330 source_tree_.MapPath("", dirnames_[0] + "/bar"); 420 source_tree_.MapPath("", dirnames_[0] + "/bar");
331 421
332 ExpectFileContents("baz", "Blah."); 422 ExpectFileContents("baz", "Blah.");
333 ExpectCannotOpenFile("../foo", 423 ExpectFileNotFound("../foo");
334 "Backslashes, consecutive slashes, \".\", or \"..\" are " 424 ExpectFileNotFound("../bar/baz");
335 "not allowed in the virtual path");
336 ExpectCannotOpenFile("../bar/baz",
337 "Backslashes, consecutive slashes, \".\", or \"..\" are "
338 "not allowed in the virtual path");
339 } 425 }
340 426
341 TEST_F(DiskSourceTreeTest, MapFile) { 427 TEST_F(DiskSourceTreeTest, MapFile) {
342 // Test opening a file that is mapped directly into the source tree. 428 // Test opening a file that is mapped directly into the source tree.
343 429
344 AddFile(dirnames_[0] + "/foo", "Hello World!"); 430 AddFile(dirnames_[0] + "/foo", "Hello World!");
345 source_tree_.MapPath("foo", dirnames_[0] + "/foo"); 431 source_tree_.MapPath("foo", dirnames_[0] + "/foo");
346 432
347 ExpectFileContents("foo", "Hello World!"); 433 ExpectFileContents("foo", "Hello World!");
348 ExpectCannotOpenFile("bar", "File not found."); 434 ExpectFileNotFound("bar");
349 } 435 }
350 436
351 TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) { 437 TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) {
352 // Test mapping and searching multiple directories. 438 // Test mapping and searching multiple directories.
353 439
354 AddFile(dirnames_[0] + "/foo", "Hello World!"); 440 AddFile(dirnames_[0] + "/foo", "Hello World!");
355 AddFile(dirnames_[1] + "/foo", "This file should be hidden."); 441 AddFile(dirnames_[1] + "/foo", "This file should be hidden.");
356 AddFile(dirnames_[1] + "/bar", "Goodbye World!"); 442 AddFile(dirnames_[1] + "/bar", "Goodbye World!");
357 source_tree_.MapPath("", dirnames_[0]); 443 source_tree_.MapPath("", dirnames_[0]);
358 source_tree_.MapPath("", dirnames_[1]); 444 source_tree_.MapPath("", dirnames_[1]);
359 445
360 ExpectFileContents("foo", "Hello World!"); 446 ExpectFileContents("foo", "Hello World!");
361 ExpectFileContents("bar", "Goodbye World!"); 447 ExpectFileContents("bar", "Goodbye World!");
362 ExpectCannotOpenFile("baz", "File not found."); 448 ExpectFileNotFound("baz");
363 } 449 }
364 450
365 TEST_F(DiskSourceTreeTest, OrderingTrumpsSpecificity) { 451 TEST_F(DiskSourceTreeTest, OrderingTrumpsSpecificity) {
366 // Test that directories are always searched in order, even when a latter 452 // Test that directories are always searched in order, even when a latter
367 // directory is more-specific than a former one. 453 // directory is more-specific than a former one.
368 454
369 // Create the "bar" directory so we can put a file in it. 455 // Create the "bar" directory so we can put a file in it.
370 GOOGLE_CHECK_OK(File::CreateDir(dirnames_[0] + "/bar", 0777)); 456 ASSERT_TRUE(File::CreateDir((dirnames_[0] + "/bar").c_str(),
457 DEFAULT_FILE_MODE));
371 458
372 // Add files and map paths. 459 // Add files and map paths.
373 AddFile(dirnames_[0] + "/bar/foo", "Hello World!"); 460 AddFile(dirnames_[0] + "/bar/foo", "Hello World!");
374 AddFile(dirnames_[1] + "/foo", "This file should be hidden."); 461 AddFile(dirnames_[1] + "/foo", "This file should be hidden.");
375 source_tree_.MapPath("", dirnames_[0]); 462 source_tree_.MapPath("", dirnames_[0]);
376 source_tree_.MapPath("bar", dirnames_[1]); 463 source_tree_.MapPath("bar", dirnames_[1]);
377 464
378 // Check. 465 // Check.
379 ExpectFileContents("bar/foo", "Hello World!"); 466 ExpectFileContents("bar/foo", "Hello World!");
380 } 467 }
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 // Accept NULL as output parameter. 591 // Accept NULL as output parameter.
505 EXPECT_TRUE(source_tree_.VirtualFileToDiskFile("bar/foo", NULL)); 592 EXPECT_TRUE(source_tree_.VirtualFileToDiskFile("bar/foo", NULL));
506 EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("baz/foo", NULL)); 593 EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("baz/foo", NULL));
507 } 594 }
508 595
509 } // namespace 596 } // namespace
510 597
511 } // namespace compiler 598 } // namespace compiler
512 } // namespace protobuf 599 } // namespace protobuf
513 } // namespace google 600 } // namespace google
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698