1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <folly/portability/GTest.h>
18
19 #include <thrift/lib/cpp2/frozen/FrozenTestUtil.h>
20 #include <thrift/lib/cpp2/frozen/FrozenUtil.h>
21 #include <thrift/lib/cpp2/protocol/Serializer.h>
22
23 using namespace apache::thrift;
24 using namespace frozen;
25 using namespace util;
26
TEST(FrozenUtil,FreezeAndUse)27 TEST(FrozenUtil, FreezeAndUse) {
28 auto file = freezeToTempFile(std::string("hello"));
29 MappedFrozen<std::string> mapped;
30 mapped = mapFrozen<std::string>(folly::File(file.fd()));
31 EXPECT_EQ(folly::StringPiece(mapped), "hello");
32 }
33
TEST(FrozenUtil,FreezeAndMap)34 TEST(FrozenUtil, FreezeAndMap) {
35 auto original = std::vector<std::string>{"hello", "world"};
36 folly::test::TemporaryFile tmp;
37
38 freezeToFile(original, folly::File(tmp.fd()));
39
40 MappedFrozen<std::vector<std::string>> mapped;
41 EXPECT_FALSE(mapped);
42 mapped = mapFrozen<std::vector<std::string>>(folly::File(tmp.fd()));
43 EXPECT_TRUE(mapped);
44
45 auto thawed = mapped.thaw();
46 EXPECT_EQ(original, thawed);
47 original.push_back("different");
48 EXPECT_NE(original, thawed);
49 }
50
TEST(FrozenUtil,FutureVersion)51 TEST(FrozenUtil, FutureVersion) {
52 folly::test::TemporaryFile tmp;
53
54 {
55 schema::Schema schema;
56 *schema.fileVersion_ref() = 1000;
57 schema.fileVersion_ref().ensure();
58
59 std::string schemaStr;
60 CompactSerializer::serialize(schema, &schemaStr);
61 write(tmp.fd(), schemaStr.data(), schemaStr.size());
62 }
63
64 EXPECT_THROW(
65 mapFrozen<std::string>(folly::File(tmp.fd())),
66 FrozenFileForwardIncompatible);
67 }
68
TEST(FrozenUtil,FileSize)69 TEST(FrozenUtil, FileSize) {
70 auto original = std::vector<std::string>{"hello", "world"};
71 folly::test::TemporaryFile tmp;
72 freezeToFile(original, folly::File(tmp.fd()));
73 struct stat stats;
74 fstat(tmp.fd(), &stats);
75 EXPECT_LT(stats.st_size, 500); // most of this is the schema
76 }
77
TEST(FrozenUtil,FreezeToString)78 TEST(FrozenUtil, FreezeToString) {
79 // multiplication tables for first three primes
80 using TestType = std::map<int, std::map<int, int>>;
81 TestType m{
82 {2, {{2, 4}, {3, 6}, {5, 10}}},
83 {3, {{2, 6}, {3, 9}, {5, 15}}},
84 {5, {{2, 10}, {3, 15}, {5, 25}}},
85 };
86 MappedFrozen<TestType> frozen;
87 MappedFrozen<TestType> frozen2;
88 {
89 std::string store;
90 freezeToString(m, store);
91 std::string store2 = freezeToString(m);
92 // In this example, the schema is 101 bytes and the data is only 17 bytes!
93 // By default, this is stripped out by this overload.
94 frozen = mapFrozen<TestType>(std::move(store));
95 frozen2 = mapFrozen<TestType>(std::move(store2));
96 }
97 EXPECT_EQ(frozen.at(3).at(5), 15);
98 EXPECT_EQ(frozen2.at(3).at(5), 15);
99 {
100 std::string store;
101 freezeToString(m, store);
102 std::string store2 = freezeToString(m);
103 // false = don't trim the space for the schema
104 frozen = mapFrozen<TestType>(std::move(store), false);
105 frozen2 = mapFrozen<TestType>(std::move(store2), false);
106 }
107 EXPECT_EQ(frozen.at(3).at(5), 15);
108 EXPECT_EQ(frozen2.at(3).at(5), 15);
109 {
110 std::string store;
111 freezeToString(m, store);
112 std::string store2;
113 freezeToStringMalloc(m, store2);
114 // TODO(T44041774): Changes in alignment are leading to differences in these
115 // two freezes. They shouldn't differ in this case.
116 EXPECT_NEAR(store.size(), store2.size(), 8);
117 // EXPECT_EQ(store, store2);
118 // false = don't trim the space for the schema
119 frozen = mapFrozen<TestType>(std::move(store), false);
120 }
121 EXPECT_EQ(frozen.at(3).at(5), 15);
122 }
123
TEST(Frozen,WorstCasePadding)124 TEST(Frozen, WorstCasePadding) {
125 using Doubles = std::vector<double>;
126 using Entry = std::pair<Doubles, std::string>;
127 using Table = std::vector<Entry>;
128 Table table;
129 for (int i = 0; i < 10; ++i) {
130 table.emplace_back();
131 auto& e = table.back();
132 e.first = {1.1, 2.2};
133 e.second.resize(1, 'a' + i);
134 }
135 std::string str;
136 freezeToString(table, str);
137 }
138
TEST(Frozen,TailPadding)139 TEST(Frozen, TailPadding) {
140 using Entry = std::pair<size_t, size_t>;
141 using Table = std::vector<Entry>;
142 Table table;
143 for (int i = 0; i < 1000; ++i) {
144 table.emplace_back();
145 auto& e = table.back();
146 e.first = 3;
147 e.second = 7;
148 std::string str;
149 freezeToString(table, str);
150 auto view = mapFrozen<Table>(std::move(str), true);
151 EXPECT_EQ(view.back().second(), 7);
152 }
153 }
154
TEST(Frozen,ConstLayout)155 TEST(Frozen, ConstLayout) {
156 using Table = std::vector<std::string>;
157 Layout<Table> layout;
158 LayoutRoot::layout(Table{"x"}, layout);
159 EXPECT_EQ(11, frozenSize(Table{"y"}, layout));
160 EXPECT_THROW(frozenSize(Table{"hello", "world"}, layout), LayoutException);
161 LayoutRoot::layout(Table{"hello", "world"}, layout);
162 EXPECT_EQ(21, frozenSize(Table{"hello", "world"}, layout));
163 }
164
TEST(Frozen,FreezeToStringMalloc)165 TEST(Frozen, FreezeToStringMalloc) {
166 using Table = std::vector<std::string>;
167 std::string x, y;
168 Table value{"hello", "world"};
169 freezeToString(value, x);
170 freezeToStringMalloc(value, y);
171 EXPECT_EQ(x, y);
172 auto fx = mapFrozen<Table>(std::move(x));
173 auto fy = mapFrozen<Table>(std::move(y));
174 EXPECT_EQ(fx[0], fy[0]);
175 EXPECT_EQ(fx[1], fy[1]);
176 }
177
TEST(Frozen,FreezeDataToString)178 TEST(Frozen, FreezeDataToString) {
179 using Table = std::vector<std::string>;
180 Layout<Table> layout;
181 LayoutRoot::layout(Table{"xxx", "yyy", "www"}, layout);
182 const Layout<Table> fixedLayout = layout;
183 EXPECT_THROW(
184 freezeDataToString(Table{"hello", "world"}, fixedLayout),
185 LayoutException);
186 auto str = freezeDataToString(Table{"abc", "123", "xyz"}, fixedLayout);
187 auto view = fixedLayout.view({reinterpret_cast<byte*>(&str[0]), 0});
188 EXPECT_EQ(view.size(), 3);
189 EXPECT_EQ(view[0], "abc");
190 EXPECT_EQ(view[1], "123");
191 EXPECT_EQ(view[2], "xyz");
192 }
193