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

Side by Side Diff: third_party/protobuf/src/google/protobuf/arena_unittest.cc

Issue 2495533002: third_party/protobuf: Update to HEAD (83d681ee2c) (Closed)
Patch Set: Make chrome settings proto generated file a component Created 4 years 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 // https://developers.google.com/protocol-buffers/
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.
(...skipping 24 matching lines...) Expand all
35 #include <memory> 35 #include <memory>
36 #ifndef _SHARED_PTR_H 36 #ifndef _SHARED_PTR_H
37 #include <google/protobuf/stubs/shared_ptr.h> 37 #include <google/protobuf/stubs/shared_ptr.h>
38 #endif 38 #endif
39 #include <string> 39 #include <string>
40 #include <typeinfo> 40 #include <typeinfo>
41 #include <vector> 41 #include <vector>
42 42
43 #include <google/protobuf/stubs/logging.h> 43 #include <google/protobuf/stubs/logging.h>
44 #include <google/protobuf/stubs/common.h> 44 #include <google/protobuf/stubs/common.h>
45 #include <google/protobuf/stubs/scoped_ptr.h>
46 #include <google/protobuf/arena_test_util.h> 45 #include <google/protobuf/arena_test_util.h>
47 #include <google/protobuf/test_util.h> 46 #include <google/protobuf/test_util.h>
48 #include <google/protobuf/unittest.pb.h> 47 #include <google/protobuf/unittest.pb.h>
49 #include <google/protobuf/unittest_arena.pb.h> 48 #include <google/protobuf/unittest_arena.pb.h>
50 #include <google/protobuf/unittest_no_arena.pb.h> 49 #include <google/protobuf/unittest_no_arena.pb.h>
51 #include <google/protobuf/io/coded_stream.h> 50 #include <google/protobuf/io/coded_stream.h>
52 #include <google/protobuf/io/zero_copy_stream_impl_lite.h> 51 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
53 #include <google/protobuf/descriptor.h> 52 #include <google/protobuf/descriptor.h>
54 #include <google/protobuf/extension_set.h> 53 #include <google/protobuf/extension_set.h>
55 #include <google/protobuf/message.h> 54 #include <google/protobuf/message.h>
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 const PleaseDontCopyMe* four_; 144 const PleaseDontCopyMe* four_;
146 int five_; 145 int five_;
147 const char* const six_; 146 const char* const six_;
148 string seven_; 147 string seven_;
149 string eight_; 148 string eight_;
150 149
151 private: 150 private:
152 GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MustBeConstructedWithOneThroughEight); 151 GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MustBeConstructedWithOneThroughEight);
153 }; 152 };
154 153
155 } // namespace
156
157 TEST(ArenaTest, ArenaConstructable) { 154 TEST(ArenaTest, ArenaConstructable) {
158 EXPECT_TRUE(Arena::is_arena_constructable<TestAllTypes>::type::value); 155 EXPECT_TRUE(Arena::is_arena_constructable<TestAllTypes>::type::value);
159 EXPECT_TRUE(Arena::is_arena_constructable<const TestAllTypes>::type::value); 156 EXPECT_TRUE(Arena::is_arena_constructable<const TestAllTypes>::type::value);
160 EXPECT_FALSE(Arena::is_arena_constructable<Arena>::type::value); 157 EXPECT_FALSE(Arena::is_arena_constructable<Arena>::type::value);
161 } 158 }
162 159
163 TEST(ArenaTest, BasicCreate) { 160 TEST(ArenaTest, BasicCreate) {
164 Arena arena; 161 Arena arena;
165 EXPECT_TRUE(Arena::Create<int32>(&arena) != NULL); 162 EXPECT_TRUE(Arena::Create<int32>(&arena) != NULL);
166 EXPECT_TRUE(Arena::Create<int64>(&arena) != NULL); 163 EXPECT_TRUE(Arena::Create<int64>(&arena) != NULL);
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 TEST(ArenaTest, Parsing) { 242 TEST(ArenaTest, Parsing) {
246 TestAllTypes original; 243 TestAllTypes original;
247 TestUtil::SetAllFields(&original); 244 TestUtil::SetAllFields(&original);
248 245
249 // Test memory leak. 246 // Test memory leak.
250 Arena arena; 247 Arena arena;
251 TestAllTypes* arena_message = Arena::CreateMessage<TestAllTypes>(&arena); 248 TestAllTypes* arena_message = Arena::CreateMessage<TestAllTypes>(&arena);
252 arena_message->ParseFromString(original.SerializeAsString()); 249 arena_message->ParseFromString(original.SerializeAsString());
253 TestUtil::ExpectAllFieldsSet(*arena_message); 250 TestUtil::ExpectAllFieldsSet(*arena_message);
254 251
255 // Test that string fields have null terminator bytes (earlier bug). 252 // Test that string fields have nul terminator bytes (earlier bug).
256 EXPECT_EQ(strlen(original.optional_string().c_str()), 253 EXPECT_EQ(strlen(original.optional_string().c_str()),
257 strlen(arena_message->optional_string().c_str())); 254 strlen(arena_message->optional_string().c_str()));
258 } 255 }
259 256
260 TEST(ArenaTest, UnknownFields) { 257 TEST(ArenaTest, UnknownFields) {
261 TestAllTypes original; 258 TestAllTypes original;
262 TestUtil::SetAllFields(&original); 259 TestUtil::SetAllFields(&original);
263 260
264 // Test basic parsing into (populating) and reading out of unknown fields on 261 // Test basic parsing into (populating) and reading out of unknown fields on
265 // an arena. 262 // an arena.
(...skipping 621 matching lines...) Expand 10 before | Expand all | Expand 10 after
887 884
888 s = new string("test string"); 885 s = new string("test string");
889 message->unsafe_arena_set_allocated_oneof_string(s); 886 message->unsafe_arena_set_allocated_oneof_string(s);
890 EXPECT_TRUE(message->has_oneof_string()); 887 EXPECT_TRUE(message->has_oneof_string());
891 EXPECT_EQ("test string", message->oneof_string()); 888 EXPECT_EQ("test string", message->oneof_string());
892 s = message->unsafe_arena_release_oneof_string(); 889 s = message->unsafe_arena_release_oneof_string();
893 EXPECT_FALSE(message->has_oneof_string()); 890 EXPECT_FALSE(message->has_oneof_string());
894 delete s; 891 delete s;
895 } 892 }
896 893
894 TEST(ArenaTest, OneofMerge) {
895 Arena arena;
896 TestAllTypes* message0 = Arena::CreateMessage<TestAllTypes>(&arena);
897 TestAllTypes* message1 = Arena::CreateMessage<TestAllTypes>(&arena);
898
899 message0->unsafe_arena_set_allocated_oneof_string(new string("x"));
900 ASSERT_TRUE(message0->has_oneof_string());
901 message1->unsafe_arena_set_allocated_oneof_string(new string("y"));
902 ASSERT_TRUE(message1->has_oneof_string());
903 EXPECT_EQ("x", message0->oneof_string());
904 EXPECT_EQ("y", message1->oneof_string());
905 message0->MergeFrom(*message1);
906 EXPECT_EQ("y", message0->oneof_string());
907 EXPECT_EQ("y", message1->oneof_string());
908 delete message0->unsafe_arena_release_oneof_string();
909 delete message1->unsafe_arena_release_oneof_string();
910 }
911
897 TEST(ArenaTest, ArenaOneofReflection) { 912 TEST(ArenaTest, ArenaOneofReflection) {
898 Arena arena; 913 Arena arena;
899 TestAllTypes* message = Arena::CreateMessage<TestAllTypes>(&arena); 914 TestAllTypes* message = Arena::CreateMessage<TestAllTypes>(&arena);
900 const Descriptor* desc = message->GetDescriptor(); 915 const Descriptor* desc = message->GetDescriptor();
901 const Reflection* refl = message->GetReflection(); 916 const Reflection* refl = message->GetReflection();
902 917
903 const FieldDescriptor* string_field = desc->FindFieldByName( 918 const FieldDescriptor* string_field = desc->FindFieldByName(
904 "oneof_string"); 919 "oneof_string");
905 const FieldDescriptor* msg_field = desc->FindFieldByName( 920 const FieldDescriptor* msg_field = desc->FindFieldByName(
906 "oneof_nested_message"); 921 "oneof_nested_message");
(...skipping 10 matching lines...) Expand all
917 refl->ClearOneof(message, oneof); 932 refl->ClearOneof(message, oneof);
918 EXPECT_FALSE(refl->HasOneof(*message, oneof)); 933 EXPECT_FALSE(refl->HasOneof(*message, oneof));
919 refl->MutableMessage(message, msg_field); 934 refl->MutableMessage(message, msg_field);
920 EXPECT_TRUE(refl->HasOneof(*message, oneof)); 935 EXPECT_TRUE(refl->HasOneof(*message, oneof));
921 submsg = refl->ReleaseMessage(message, msg_field); 936 submsg = refl->ReleaseMessage(message, msg_field);
922 EXPECT_FALSE(refl->HasOneof(*message, oneof)); 937 EXPECT_FALSE(refl->HasOneof(*message, oneof));
923 EXPECT_TRUE(submsg->GetArena() == NULL); 938 EXPECT_TRUE(submsg->GetArena() == NULL);
924 delete submsg; 939 delete submsg;
925 } 940 }
926 941
927 namespace {
928 void TestSwapRepeatedField(Arena* arena1, Arena* arena2) { 942 void TestSwapRepeatedField(Arena* arena1, Arena* arena2) {
929 // Test "safe" (copying) semantics for direct Swap() on RepeatedPtrField 943 // Test "safe" (copying) semantics for direct Swap() on RepeatedPtrField
930 // between arenas. 944 // between arenas.
931 RepeatedPtrField<TestAllTypes> field1(arena1); 945 RepeatedPtrField<TestAllTypes> field1(arena1);
932 RepeatedPtrField<TestAllTypes> field2(arena2); 946 RepeatedPtrField<TestAllTypes> field2(arena2);
933 for (int i = 0; i < 10; i++) { 947 for (int i = 0; i < 10; i++) {
934 TestAllTypes* t = Arena::CreateMessage<TestAllTypes>(arena1); 948 TestAllTypes* t = Arena::CreateMessage<TestAllTypes>(arena1);
935 t->set_optional_string("field1"); 949 t->set_optional_string("field1");
936 t->set_optional_int32(i); 950 t->set_optional_int32(i);
937 if (arena1 != NULL) { 951 if (arena1 != NULL) {
(...skipping 18 matching lines...) Expand all
956 EXPECT_TRUE(string("field1") == field2.Get(0).optional_string()); 970 EXPECT_TRUE(string("field1") == field2.Get(0).optional_string());
957 EXPECT_TRUE(string("field2") == field1.Get(0).optional_string()); 971 EXPECT_TRUE(string("field2") == field1.Get(0).optional_string());
958 // Ensure that fields retained their original order: 972 // Ensure that fields retained their original order:
959 for (int i = 0; i < field1.size(); i++) { 973 for (int i = 0; i < field1.size(); i++) {
960 EXPECT_EQ(i, field1.Get(i).optional_int32()); 974 EXPECT_EQ(i, field1.Get(i).optional_int32());
961 } 975 }
962 for (int i = 0; i < field2.size(); i++) { 976 for (int i = 0; i < field2.size(); i++) {
963 EXPECT_EQ(i, field2.Get(i).optional_int32()); 977 EXPECT_EQ(i, field2.Get(i).optional_int32());
964 } 978 }
965 } 979 }
966 } // namespace
967 980
968 TEST(ArenaTest, SwapRepeatedField) { 981 TEST(ArenaTest, SwapRepeatedField) {
969 Arena arena; 982 Arena arena;
970 TestSwapRepeatedField(&arena, &arena); 983 TestSwapRepeatedField(&arena, &arena);
971 } 984 }
972 985
973 TEST(ArenaTest, SwapRepeatedFieldWithDifferentArenas) { 986 TEST(ArenaTest, SwapRepeatedFieldWithDifferentArenas) {
974 Arena arena1; 987 Arena arena1;
975 Arena arena2; 988 Arena arena2;
976 TestSwapRepeatedField(&arena1, &arena2); 989 TestSwapRepeatedField(&arena1, &arena2);
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
1098 submessage = static_cast<TestAllTypes::NestedMessage*>( 1111 submessage = static_cast<TestAllTypes::NestedMessage*>(
1099 r->MutableMessage(message, oneof_field)); 1112 r->MutableMessage(message, oneof_field));
1100 submessage_expected = message->mutable_oneof_nested_message(); 1113 submessage_expected = message->mutable_oneof_nested_message();
1101 1114
1102 EXPECT_EQ(submessage_expected, submessage); 1115 EXPECT_EQ(submessage_expected, submessage);
1103 EXPECT_EQ(&arena, submessage->GetArena()); 1116 EXPECT_EQ(&arena, submessage->GetArena());
1104 } 1117 }
1105 #endif // !GOOGLE_PROTOBUF_NO_RTTI 1118 #endif // !GOOGLE_PROTOBUF_NO_RTTI
1106 1119
1107 1120
1108 namespace {
1109
1110 void FillArenaAwareFields(TestAllTypes* message) { 1121 void FillArenaAwareFields(TestAllTypes* message) {
1111 string test_string = "hello world"; 1122 string test_string = "hello world";
1112 message->set_optional_int32(42); 1123 message->set_optional_int32(42);
1113 message->set_optional_string(test_string); 1124 message->set_optional_string(test_string);
1114 message->set_optional_bytes(test_string); 1125 message->set_optional_bytes(test_string);
1115 message->mutable_optional_nested_message()->set_bb(42); 1126 message->mutable_optional_nested_message()->set_bb(42);
1116 1127
1117 message->set_oneof_uint32(42); 1128 message->set_oneof_uint32(42);
1118 message->mutable_oneof_nested_message()->set_bb(42); 1129 message->mutable_oneof_nested_message()->set_bb(42);
1119 message->set_oneof_string(test_string); 1130 message->set_oneof_string(test_string);
1120 message->set_oneof_bytes(test_string); 1131 message->set_oneof_bytes(test_string);
1121 1132
1122 message->add_repeated_int32(42); 1133 message->add_repeated_int32(42);
1123 // No repeated string: not yet arena-aware. 1134 // No repeated string: not yet arena-aware.
1124 message->add_repeated_nested_message()->set_bb(42); 1135 message->add_repeated_nested_message()->set_bb(42);
1125 message->mutable_optional_lazy_message()->set_bb(42); 1136 message->mutable_optional_lazy_message()->set_bb(42);
1126 } 1137 }
1127 1138
1128 }
1129
1130 // Test: no allocations occur on heap while touching all supported field types. 1139 // Test: no allocations occur on heap while touching all supported field types.
1131 TEST(ArenaTest, NoHeapAllocationsTest) { 1140 TEST(ArenaTest, NoHeapAllocationsTest) {
1132 // Allocate a large initial block to avoid mallocs during hooked test. 1141 // Allocate a large initial block to avoid mallocs during hooked test.
1133 std::vector<char> arena_block(128 * 1024); 1142 std::vector<char> arena_block(128 * 1024);
1134 ArenaOptions options; 1143 ArenaOptions options;
1135 options.initial_block = &arena_block[0]; 1144 options.initial_block = &arena_block[0];
1136 options.initial_block_size = arena_block.size(); 1145 options.initial_block_size = arena_block.size();
1137 Arena arena(options); 1146 Arena arena(options);
1138 1147
1139 { 1148 {
1140 1149
1141 TestAllTypes* message = Arena::CreateMessage<TestAllTypes>(&arena); 1150 TestAllTypes* message = Arena::CreateMessage<TestAllTypes>(&arena);
1142 FillArenaAwareFields(message); 1151 FillArenaAwareFields(message);
1143 } 1152 }
1144 1153
1145 arena.Reset(); 1154 arena.Reset();
1146 } 1155 }
1147 1156
1157 TEST(ArenaTest, ParseCorruptedString) {
1158 TestAllTypes message;
1159 TestUtil::SetAllFields(&message);
1160 TestParseCorruptedString<TestAllTypes, true>(message);
1161 TestParseCorruptedString<TestAllTypes, false>(message);
1162 }
1163
1148 #ifndef GOOGLE_PROTOBUF_NO_RTTI 1164 #ifndef GOOGLE_PROTOBUF_NO_RTTI
1149 // Test construction on an arena via generic MessageLite interface. We should be 1165 // Test construction on an arena via generic MessageLite interface. We should be
1150 // able to successfully deserialize on the arena without incurring heap 1166 // able to successfully deserialize on the arena without incurring heap
1151 // allocations, i.e., everything should still be arena-allocation-aware. 1167 // allocations, i.e., everything should still be arena-allocation-aware.
1152 TEST(ArenaTest, MessageLiteOnArena) { 1168 TEST(ArenaTest, MessageLiteOnArena) {
1153 std::vector<char> arena_block(128 * 1024); 1169 std::vector<char> arena_block(128 * 1024);
1154 ArenaOptions options; 1170 ArenaOptions options;
1155 options.initial_block = &arena_block[0]; 1171 options.initial_block = &arena_block[0];
1156 options.initial_block_size = arena_block.size(); 1172 options.initial_block_size = arena_block.size();
1157 Arena arena(options); 1173 Arena arena(options);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1190 { 1206 {
1191 Arena arena; 1207 Arena arena;
1192 RepeatedField<string> field_on_arena(&arena); 1208 RepeatedField<string> field_on_arena(&arena);
1193 for (int i = 0; i < 100; i++) { 1209 for (int i = 0; i < 100; i++) {
1194 *field_on_arena.Add() = "test string long enough to exceed inline buffer"; 1210 *field_on_arena.Add() = "test string long enough to exceed inline buffer";
1195 } 1211 }
1196 } 1212 }
1197 } 1213 }
1198 1214
1199 // Align n to next multiple of 8 1215 // Align n to next multiple of 8
1200 namespace {
1201 uint64 Align8(uint64 n) { return (n + 7) & -8; } 1216 uint64 Align8(uint64 n) { return (n + 7) & -8; }
1202 } // namespace
1203 1217
1204 TEST(ArenaTest, SpaceAllocated_and_Used) { 1218 TEST(ArenaTest, SpaceAllocated_and_Used) {
1205 ArenaOptions options; 1219 ArenaOptions options;
1206 options.start_block_size = 256; 1220 options.start_block_size = 256;
1207 options.max_block_size = 8192; 1221 options.max_block_size = 8192;
1208 Arena arena_1(options); 1222 Arena arena_1(options);
1209 EXPECT_EQ(0, arena_1.SpaceAllocated()); 1223 EXPECT_EQ(0, arena_1.SpaceAllocated());
1210 EXPECT_EQ(0, arena_1.SpaceUsed()); 1224 EXPECT_EQ(0, arena_1.SpaceUsed());
1211 EXPECT_EQ(0, arena_1.Reset()); 1225 EXPECT_EQ(0, arena_1.Reset());
1212 ::google::protobuf::Arena::CreateArray<char>(&arena_1, 320); 1226 ::google::protobuf::Arena::CreateArray<char>(&arena_1, 320);
(...skipping 30 matching lines...) Expand all
1243 } 1257 }
1244 1258
1245 TEST(ArenaTest, Alignment) { 1259 TEST(ArenaTest, Alignment) {
1246 ::google::protobuf::Arena arena; 1260 ::google::protobuf::Arena arena;
1247 for (int i = 0; i < 200; i++) { 1261 for (int i = 0; i < 200; i++) {
1248 void* p = ::google::protobuf::Arena::CreateArray<char>(&arena, i); 1262 void* p = ::google::protobuf::Arena::CreateArray<char>(&arena, i);
1249 GOOGLE_CHECK_EQ(reinterpret_cast<uintptr_t>(p) % 8, 0) << i << ": " << p; 1263 GOOGLE_CHECK_EQ(reinterpret_cast<uintptr_t>(p) % 8, 0) << i << ": " << p;
1250 } 1264 }
1251 } 1265 }
1252 1266
1267 TEST(ArenaTest, BlockSizeSmallerThanAllocation) {
1268 for (size_t i = 0; i <= 8; ++i) {
1269 ::google::protobuf::ArenaOptions opt;
1270 opt.start_block_size = opt.max_block_size = i;
1271 ::google::protobuf::Arena arena(opt);
1272
1273 *::google::protobuf::Arena::Create<int64>(&arena) = 42;
1274 EXPECT_GE(arena.SpaceAllocated(), 8);
1275 EXPECT_EQ(8, arena.SpaceUsed());
1276
1277 *::google::protobuf::Arena::Create<int64>(&arena) = 42;
1278 EXPECT_GE(arena.SpaceAllocated(), 16);
1279 EXPECT_EQ(16, arena.SpaceUsed());
1280 }
1281 }
1282
1253 TEST(ArenaTest, GetArenaShouldReturnTheArenaForArenaAllocatedMessages) { 1283 TEST(ArenaTest, GetArenaShouldReturnTheArenaForArenaAllocatedMessages) {
1254 ::google::protobuf::Arena arena; 1284 ::google::protobuf::Arena arena;
1255 ArenaMessage* message = Arena::CreateMessage<ArenaMessage>(&arena); 1285 ArenaMessage* message = Arena::CreateMessage<ArenaMessage>(&arena);
1256 const ArenaMessage* const_pointer_to_message = message; 1286 const ArenaMessage* const_pointer_to_message = message;
1257 EXPECT_EQ(&arena, Arena::GetArena(message)); 1287 EXPECT_EQ(&arena, Arena::GetArena(message));
1258 EXPECT_EQ(&arena, Arena::GetArena(const_pointer_to_message)); 1288 EXPECT_EQ(&arena, Arena::GetArena(const_pointer_to_message));
1259 } 1289 }
1260 1290
1261 TEST(ArenaTest, GetArenaShouldReturnNullForNonArenaAllocatedMessages) { 1291 TEST(ArenaTest, GetArenaShouldReturnNullForNonArenaAllocatedMessages) {
1262 ArenaMessage message; 1292 ArenaMessage message;
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
1343 EXPECT_EQ(2, ArenaHooksTestUtil::num_allocations); 1373 EXPECT_EQ(2, ArenaHooksTestUtil::num_allocations);
1344 } 1374 }
1345 arena.Reset(); 1375 arena.Reset();
1346 arena.Reset(); 1376 arena.Reset();
1347 EXPECT_EQ(2, ArenaHooksTestUtil::num_reset); 1377 EXPECT_EQ(2, ArenaHooksTestUtil::num_reset);
1348 } 1378 }
1349 EXPECT_EQ(3, ArenaHooksTestUtil::num_reset); 1379 EXPECT_EQ(3, ArenaHooksTestUtil::num_reset);
1350 EXPECT_EQ(1, ArenaHooksTestUtil::num_destruct); 1380 EXPECT_EQ(1, ArenaHooksTestUtil::num_destruct);
1351 } 1381 }
1352 1382
1383
1384 } // namespace
1353 } // namespace protobuf 1385 } // namespace protobuf
1354 } // namespace google 1386 } // namespace google
OLDNEW
« no previous file with comments | « third_party/protobuf/src/google/protobuf/arena_test_util.h ('k') | third_party/protobuf/src/google/protobuf/arenastring.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698