Coverage Report

Created: 2024-02-25 06:31

/proc/self/cwd/test/test_common.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2016 Google Inc. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
//
15
////////////////////////////////////////////////////////////////////////////////
16
//
17
#include "test_common.h"
18
19
#include <ctype.h>
20
#include <fstream>
21
#include <string>
22
#include <vector>
23
24
#include "google/protobuf/io/zero_copy_stream.h"
25
#include "google/protobuf/struct.pb.h"
26
#include "google/protobuf/text_format.h"
27
#include "google/protobuf/util/json_util.h"
28
#include "google/protobuf/util/message_differencer.h"
29
#include "google/protobuf/util/type_resolver_util.h"
30
#include "google/protobuf/util/type_resolver_util.h"
31
#include "gtest/gtest.h"
32
33
namespace google {
34
namespace grpc {
35
36
namespace transcoding {
37
namespace testing {
38
39
namespace pb = ::google::protobuf;
40
namespace pbutil = ::google::protobuf::util;
41
42
TestZeroCopyInputStream::TestZeroCopyInputStream()
43
615
    : finished_(false), position_(0) {}
Unexecuted instantiation: google::grpc::transcoding::testing::TestZeroCopyInputStream::TestZeroCopyInputStream()
google::grpc::transcoding::testing::TestZeroCopyInputStream::TestZeroCopyInputStream()
Line
Count
Source
43
615
    : finished_(false), position_(0) {}
44
45
1.16M
void TestZeroCopyInputStream::AddChunk(std::string chunk) {
46
  // Some of our tests generate 0-sized chunks. Let's not add them to the flow
47
  // here because the tests explicitly handle 'out of input' and 'there is now
48
  // new input' situations explicitly and empty chunks in the input would
49
  // interfere with the test driver logic.
50
1.16M
  if (!chunk.empty()) {
51
1.15M
    chunks_.emplace_back(std::move(chunk));
52
1.15M
  }
53
1.16M
}
54
55
229k
bool TestZeroCopyInputStream::Next(const void** data, int* size) {
56
229k
  if (position_ < static_cast<int>(current_.size())) {
57
    // Still have some data in the current buffer
58
14.2k
    *data = current_.data() + position_;
59
14.2k
    *size = current_.size() - position_;
60
14.2k
    position_ = current_.size();
61
14.2k
    return true;
62
215k
  } else if (!chunks_.empty()) {
63
    // Return the next buffer
64
215k
    current_ = std::move(chunks_.front());
65
215k
    chunks_.pop_front();
66
215k
    *data = current_.data();
67
215k
    *size = current_.size();
68
215k
    position_ = current_.size();
69
215k
    return true;
70
215k
  } else {
71
0
    *size = 0;
72
0
    return !finished_;
73
0
  }
74
229k
}
75
76
57.0k
void TestZeroCopyInputStream::BackUp(int count) {
77
114k
  EXPECT_TRUE(count <= position_) << "Out of bounds while trying to back up "
78
114k
                                     "the input stream. position: "
79
114k
                                  << position_ << " count: " << count
80
114k
                                  << std::endl;
81
57.0k
  position_ -= count;
82
57.0k
}
83
84
2.67M
int64_t TestZeroCopyInputStream::BytesAvailable() const {
85
2.67M
  auto total = current_.size() - position_;
86
4.04G
  for (auto chunk : chunks_) {
87
4.04G
    total += chunk.size();
88
4.04G
  }
89
2.67M
  return total;
90
2.67M
}
91
92
namespace {
93
94
typedef std::vector<size_t> Tuple;
95
96
// Generates all distinct m-tuples of {1..n} numbers and calls f on each one.
97
// If f returns false, the enumeration stops; otherwise it continues.
98
// The numbers within each generated tuple are in increasing order.
99
bool ForAllDistinctTuples(size_t m, size_t n,
100
0
                          std::function<bool(const Tuple&)> f, Tuple& t) {
101
0
  if (0 == m) {
102
    // The base case
103
0
    return f(t);
104
0
  }
105
0
  for (unsigned i = (t.empty() ? 1 : t.back() + 1); i <= n; ++i) {
106
0
    t.emplace_back(i);
107
0
    if (!ForAllDistinctTuples(m - 1, n, f, t)) {
108
      // Somewhere f returned false - early termination
109
0
      return false;
110
0
    }
111
0
    t.pop_back();
112
0
  }
113
0
  return true;
114
0
}
115
116
// Convenience overload without the initial tuple
117
bool ForAllDistinctTuples(size_t m, size_t n,
118
0
                          std::function<bool(const Tuple&)> f) {
119
0
  Tuple t;
120
0
  return ForAllDistinctTuples(m, n, f, t);
121
0
}
122
123
// Calls f for partitioning_coefficient^m-th part of all the partitions (0 <
124
// partitioning_coefficient <= 1). It calls ForAllDistinctTuples() for
125
// partitioning_coefficient*n and multiplies each tuple by
126
// 1/partitioning_coefficient to achieve uniformity (more or less).
127
bool ForSomeDistinctTuples(size_t m, size_t n, double partitioning_coefficient,
128
0
                           std::function<bool(const Tuple&)> f) {
129
0
  return ForAllDistinctTuples(
130
0
      m, static_cast<size_t>(partitioning_coefficient * n),
131
0
      [f, partitioning_coefficient](const Tuple& t) {
132
0
        auto tt = t;
133
0
        for (auto& i : tt) {
134
0
          i = static_cast<size_t>(i / partitioning_coefficient);
135
0
        }
136
0
        return f(tt);
137
0
      });
138
0
}
139
140
// Returns a display string for a partition of str defined by tuple t. Used for
141
// displaying errors.
142
0
std::string PartitionToDisplayString(const std::string& str, const Tuple& t) {
143
0
  std::string result;
144
0
  size_t pos = 0;
145
0
  for (size_t i = 0; i < t.size(); ++i) {
146
0
    if (i > 0) {
147
0
      result += "  |  ";
148
0
    }
149
0
    result += str.substr(pos, t[i] - pos);
150
0
    pos = t[i];
151
0
  }
152
0
  result += "  |  ";
153
0
  result += str.substr(pos);
154
0
  return result;
155
0
}
156
157
}  // namespace
158
159
bool RunTestForInputPartitions(size_t chunk_count,
160
                               double partitioning_coefficient,
161
                               const std::string& input,
162
0
                               std::function<bool(const Tuple& t)> test) {
163
  // To choose a m-partition of input of size n, we need to choose m-1 breakdown
164
  // points between 1 and n-1.
165
0
  return ForSomeDistinctTuples(
166
0
      chunk_count - 1, input.empty() ? 0 : input.size() - 1,
167
0
      partitioning_coefficient, [&input, test](const Tuple& t) {
168
0
        if (!test(t)) {
169
0
          ADD_FAILURE() << "Failed for the following partition \""
170
0
                        << PartitionToDisplayString(input, t) << "\"\n";
171
0
          return false;
172
0
        } else {
173
0
          return true;
174
0
        }
175
0
      });
176
0
}
177
178
0
std::string GenerateInput(const std::string& seed, size_t size) {
179
0
  std::string result = seed;
180
0
  while (result.size() < size) {
181
0
    result += seed;
182
0
  }
183
0
  result.resize(size);
184
0
  return result;
185
0
}
186
187
namespace {
188
189
0
std::string LoadFile(const std::string& file_name) {
190
0
  std::ifstream ifs(file_name);
191
0
  if (!ifs) {
192
0
    ADD_FAILURE() << "Could not open " << file_name.c_str() << std::endl;
193
0
    return std::string();
194
0
  }
195
0
  std::ostringstream ss;
196
0
  ss << ifs.rdbuf();
197
0
  return ss.str();
198
0
}
199
200
}  // namespace
201
202
bool LoadService(const std::string& config_pb_txt_file,
203
                 const std::string& testdata_path,
204
0
                 ::google::api::Service* service) {
205
0
  auto config = LoadFile(testdata_path + config_pb_txt_file);
206
0
  if (config.empty()) {
207
0
    return false;
208
0
  }
209
210
0
  if (!pb::TextFormat::ParseFromString(config, service)) {
211
0
    ADD_FAILURE() << "Could not parse service config from "
212
0
                  << config_pb_txt_file.c_str() << std::endl;
213
0
    return false;
214
0
  } else {
215
0
    return true;
216
0
  }
217
0
}
218
219
bool LoadService(const std::string& config_pb_txt_file,
220
0
                 ::google::api::Service* service) {
221
0
  static const char kTestdata[] = "test/testdata/";
222
0
  return LoadService(config_pb_txt_file, kTestdata, service);
223
0
}
224
225
0
unsigned DelimiterToSize(const unsigned char* delimiter) {
226
0
  unsigned size = 0;
227
  // Bytes 1-4 are big-endian 32-bit message size
228
0
  size = size | static_cast<unsigned>(delimiter[1]);
229
0
  size <<= 8;
230
0
  size = size | static_cast<unsigned>(delimiter[2]);
231
0
  size <<= 8;
232
0
  size = size | static_cast<unsigned>(delimiter[3]);
233
0
  size <<= 8;
234
0
  size = size | static_cast<unsigned>(delimiter[4]);
235
0
  return size;
236
0
}
237
238
0
std::string SizeToDelimiter(unsigned size) {
239
0
  unsigned char delimiter[5];
240
  // Byte 0 is the compression bit - set to 0 (no compression)
241
0
  delimiter[0] = 0;
242
  // Bytes 1-4 are big-endian 32-bit message size
243
0
  delimiter[4] = 0xFF & size;
244
0
  size >>= 8;
245
0
  delimiter[3] = 0xFF & size;
246
0
  size >>= 8;
247
0
  delimiter[2] = 0xFF & size;
248
0
  size >>= 8;
249
0
  delimiter[1] = 0xFF & size;
250
251
0
  return std::string(reinterpret_cast<const char*>(delimiter),
252
0
                     sizeof(delimiter));
253
0
}
254
255
namespace {
256
257
0
bool JsonToStruct(const std::string& json, pb::Struct* message) {
258
0
  static std::unique_ptr<pbutil::TypeResolver> type_resolver(
259
0
      pbutil::NewTypeResolverForDescriptorPool(
260
0
          "type.googleapis.com", pb::Struct::descriptor()->file()->pool()));
261
262
0
  std::string binary;
263
0
  auto status = pbutil::JsonToBinaryString(
264
0
      type_resolver.get(), "type.googleapis.com/google.protobuf.Struct", json,
265
0
      &binary);
266
0
  if (!status.ok()) {
267
0
    ADD_FAILURE() << "Error: " << status.message() << std::endl
268
0
                  << "Failed to parse \"" << json << "\"." << std::endl;
269
0
    return false;
270
0
  }
271
272
0
  if (!message->ParseFromString(binary)) {
273
0
    ADD_FAILURE() << "Failed to create a struct message for \"" << json << "\"."
274
0
                  << std::endl;
275
0
    return false;
276
0
  }
277
278
0
  return true;
279
0
}
280
281
}  // namespace
282
283
bool ExpectJsonObjectEq(const std::string& expected_json,
284
0
                        const std::string& actual_json) {
285
  // Convert expected_json and actual_json to google.protobuf.Struct messages
286
0
  pb::Struct expected_proto, actual_proto;
287
0
  if (!JsonToStruct(expected_json, &expected_proto) ||
288
0
      !JsonToStruct(actual_json, &actual_proto)) {
289
0
    return false;
290
0
  }
291
292
  // Now try matching the protobuf messages
293
0
  if (!pbutil::MessageDifferencer::Equivalent(expected_proto, actual_proto)) {
294
    // Use EXPECT_EQ on debug strings to output the diff
295
0
    EXPECT_EQ(expected_proto.DebugString(), actual_proto.DebugString());
296
0
    return false;
297
0
  }
298
299
0
  return true;
300
0
}
301
302
0
bool ExpectJsonArrayEq(const std::string& expected, const std::string& actual) {
303
  // Wrap the JSON arrays into JSON objects and compare as object
304
0
  return ExpectJsonObjectEq(R"( { "array" : )" + expected + "}",
305
0
                            R"( { "array" : )" + actual + "}");
306
0
}
307
308
bool JsonArrayTester::TestElement(const std::string& expected,
309
0
                                  const std::string& actual) {
310
0
  if (expected_so_far_.empty()) {
311
    // First element - open the array and add the element
312
0
    expected_so_far_ = "[" + expected;
313
0
  } else {
314
    // Had elements before - add a comma and then the element
315
0
    expected_so_far_ += "," + expected;
316
0
  }
317
0
  actual_so_far_ += actual;
318
319
  // Add the closing "]" to the partial arrays and compare
320
0
  return ExpectJsonArrayEq(expected_so_far_ + "]", actual_so_far_ + "]");
321
0
}
322
323
bool JsonArrayTester::TestChunk(const std::string& expected,
324
0
                                const std::string& actual, bool closes) {
325
0
  expected_so_far_ += expected;
326
0
  actual_so_far_ += actual;
327
328
  // Add the closing "]" to the partial arrays if needed and compare
329
0
  return ExpectJsonArrayEq(expected_so_far_ + (closes ? "" : "]"),
330
0
                           actual_so_far_ + (closes ? "" : "]"));
331
0
}
332
333
0
bool JsonArrayTester::TestClosed(const std::string& actual) {
334
0
  if (expected_so_far_.empty()) {
335
    // Empty array case
336
0
    expected_so_far_ = "[";
337
0
  }
338
  // Close the finish
339
0
  expected_so_far_ += "]";
340
0
  actual_so_far_ += actual;
341
342
  // Compare the closed arrays
343
0
  return ExpectJsonArrayEq(expected_so_far_, actual_so_far_);
344
0
}
345
346
}  // namespace testing
347
}  // namespace transcoding
348
349
}  // namespace grpc
350
}  // namespace google