serialize-test.c++ 11.1 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
Kenton Varda's avatar
Kenton Varda committed
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
Kenton Varda's avatar
Kenton Varda committed
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
Kenton Varda's avatar
Kenton Varda committed
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
Kenton Varda's avatar
Kenton Varda committed
21 22

#include "serialize.h"
Kenton Varda's avatar
Kenton Varda committed
23
#include <kj/debug.h>
Kenton Varda's avatar
Kenton Varda committed
24 25 26 27 28
#include <gtest/gtest.h>
#include <string>
#include <stdlib.h>
#include "test-util.h"

29
namespace capnp {
30
namespace _ {  // private
Kenton Varda's avatar
Kenton Varda committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
namespace {

class TestMessageBuilder: public MallocMessageBuilder {
  // A MessageBuilder that tries to allocate an exact number of total segments, by allocating
  // minimum-size segments until it reaches the number, then allocating one large segment to
  // finish.

public:
  explicit TestMessageBuilder(uint desiredSegmentCount)
      : MallocMessageBuilder(0, AllocationStrategy::FIXED_SIZE),
        desiredSegmentCount(desiredSegmentCount) {}
  ~TestMessageBuilder() {
    EXPECT_EQ(0u, desiredSegmentCount);
  }

46
  kj::ArrayPtr<word> allocateSegment(uint minimumSize) override {
Kenton Varda's avatar
Kenton Varda committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
    if (desiredSegmentCount <= 1) {
      if (desiredSegmentCount < 1) {
        ADD_FAILURE() << "Allocated more segments than desired.";
      } else {
        --desiredSegmentCount;
      }
      return MallocMessageBuilder::allocateSegment(SUGGESTED_FIRST_SEGMENT_WORDS);
    } else {
      --desiredSegmentCount;
      return MallocMessageBuilder::allocateSegment(minimumSize);
    }
  }

private:
  uint desiredSegmentCount;
};

TEST(Serialize, FlatArray) {
  TestMessageBuilder builder(1);
  initTestMessage(builder.initRoot<TestAllTypes>());

68
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
69

70 71 72 73 74 75 76 77 78 79 80 81 82 83
  {
    FlatArrayMessageReader reader(serialized.asPtr());
    checkTestMessage(reader.getRoot<TestAllTypes>());
    EXPECT_EQ(serialized.end(), reader.getEnd());
  }

  kj::Array<word> serializedWithSuffix = kj::heapArray<word>(serialized.size() + 5);
  memcpy(serializedWithSuffix.begin(), serialized.begin(), serialized.size() * sizeof(word));

  {
    FlatArrayMessageReader reader(serializedWithSuffix.asPtr());
    checkTestMessage(reader.getRoot<TestAllTypes>());
    EXPECT_EQ(serializedWithSuffix.end() - 5, reader.getEnd());
  }
Kenton Varda's avatar
Kenton Varda committed
84 85 86 87 88 89
}

TEST(Serialize, FlatArrayOddSegmentCount) {
  TestMessageBuilder builder(7);
  initTestMessage(builder.initRoot<TestAllTypes>());

90
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
91

92 93 94 95 96 97 98 99 100 101 102 103 104 105
  {
    FlatArrayMessageReader reader(serialized.asPtr());
    checkTestMessage(reader.getRoot<TestAllTypes>());
    EXPECT_EQ(serialized.end(), reader.getEnd());
  }

  kj::Array<word> serializedWithSuffix = kj::heapArray<word>(serialized.size() + 5);
  memcpy(serializedWithSuffix.begin(), serialized.begin(), serialized.size() * sizeof(word));

  {
    FlatArrayMessageReader reader(serializedWithSuffix.asPtr());
    checkTestMessage(reader.getRoot<TestAllTypes>());
    EXPECT_EQ(serializedWithSuffix.end() - 5, reader.getEnd());
  }
Kenton Varda's avatar
Kenton Varda committed
106 107
}

108
TEST(Serialize, FlatArrayEvenSegmentCount) {
Kenton Varda's avatar
Kenton Varda committed
109 110 111
  TestMessageBuilder builder(10);
  initTestMessage(builder.initRoot<TestAllTypes>());

112
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
113

114 115 116 117 118 119 120 121 122 123 124 125 126 127
  {
    FlatArrayMessageReader reader(serialized.asPtr());
    checkTestMessage(reader.getRoot<TestAllTypes>());
    EXPECT_EQ(serialized.end(), reader.getEnd());
  }

  kj::Array<word> serializedWithSuffix = kj::heapArray<word>(serialized.size() + 5);
  memcpy(serializedWithSuffix.begin(), serialized.begin(), serialized.size() * sizeof(word));

  {
    FlatArrayMessageReader reader(serializedWithSuffix.asPtr());
    checkTestMessage(reader.getRoot<TestAllTypes>());
    EXPECT_EQ(serializedWithSuffix.end() - 5, reader.getEnd());
  }
Kenton Varda's avatar
Kenton Varda committed
128 129
}

130
class TestInputStream: public kj::InputStream {
Kenton Varda's avatar
Kenton Varda committed
131
public:
132
  TestInputStream(kj::ArrayPtr<const word> data, bool lazy)
Kenton Varda's avatar
Kenton Varda committed
133
      : pos(reinterpret_cast<const char*>(data.begin())),
134 135
        end(reinterpret_cast<const char*>(data.end())),
        lazy(lazy) {}
Kenton Varda's avatar
Kenton Varda committed
136 137
  ~TestInputStream() {}

138
  size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override {
139
    KJ_ASSERT(maxBytes <= size_t(end - pos), "Overran end of stream.");
140 141 142 143
    size_t amount = lazy ? minBytes : maxBytes;
    memcpy(buffer, pos, amount);
    pos += amount;
    return amount;
Kenton Varda's avatar
Kenton Varda committed
144 145 146 147 148
  }

private:
  const char* pos;
  const char* end;
149
  bool lazy;
Kenton Varda's avatar
Kenton Varda committed
150 151 152 153 154 155
};

TEST(Serialize, InputStream) {
  TestMessageBuilder builder(1);
  initTestMessage(builder.initRoot<TestAllTypes>());

156
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
157

158 159
  TestInputStream stream(serialized.asPtr(), false);
  InputStreamMessageReader reader(stream, ReaderOptions());
Kenton Varda's avatar
Kenton Varda committed
160 161 162 163

  checkTestMessage(reader.getRoot<TestAllTypes>());
}

164
TEST(Serialize, InputStreamScratchSpace) {
Kenton Varda's avatar
Kenton Varda committed
165 166 167
  TestMessageBuilder builder(1);
  initTestMessage(builder.initRoot<TestAllTypes>());

168
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
169

170 171
  word scratch[4096];
  TestInputStream stream(serialized.asPtr(), false);
172
  InputStreamMessageReader reader(stream, ReaderOptions(), kj::ArrayPtr<word>(scratch, 4096));
Kenton Varda's avatar
Kenton Varda committed
173 174 175 176

  checkTestMessage(reader.getRoot<TestAllTypes>());
}

177
TEST(Serialize, InputStreamLazy) {
Kenton Varda's avatar
Kenton Varda committed
178 179 180
  TestMessageBuilder builder(1);
  initTestMessage(builder.initRoot<TestAllTypes>());

181
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
182

183 184
  TestInputStream stream(serialized.asPtr(), true);
  InputStreamMessageReader reader(stream, ReaderOptions());
Kenton Varda's avatar
Kenton Varda committed
185 186 187 188

  checkTestMessage(reader.getRoot<TestAllTypes>());
}

189
TEST(Serialize, InputStreamOddSegmentCount) {
Kenton Varda's avatar
Kenton Varda committed
190 191 192
  TestMessageBuilder builder(7);
  initTestMessage(builder.initRoot<TestAllTypes>());

193
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
194

195 196
  TestInputStream stream(serialized.asPtr(), false);
  InputStreamMessageReader reader(stream, ReaderOptions());
Kenton Varda's avatar
Kenton Varda committed
197 198 199 200

  checkTestMessage(reader.getRoot<TestAllTypes>());
}

201
TEST(Serialize, InputStreamOddSegmentCountLazy) {
Kenton Varda's avatar
Kenton Varda committed
202 203 204
  TestMessageBuilder builder(7);
  initTestMessage(builder.initRoot<TestAllTypes>());

205
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
206

207 208
  TestInputStream stream(serialized.asPtr(), true);
  InputStreamMessageReader reader(stream, ReaderOptions());
Kenton Varda's avatar
Kenton Varda committed
209 210 211 212

  checkTestMessage(reader.getRoot<TestAllTypes>());
}

213
TEST(Serialize, InputStreamEvenSegmentCount) {
Kenton Varda's avatar
Kenton Varda committed
214 215 216
  TestMessageBuilder builder(10);
  initTestMessage(builder.initRoot<TestAllTypes>());

217
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
218

219 220
  TestInputStream stream(serialized.asPtr(), false);
  InputStreamMessageReader reader(stream, ReaderOptions());
Kenton Varda's avatar
Kenton Varda committed
221 222 223 224

  checkTestMessage(reader.getRoot<TestAllTypes>());
}

225
TEST(Serialize, InputStreamEvenSegmentCountLazy) {
Kenton Varda's avatar
Kenton Varda committed
226 227 228
  TestMessageBuilder builder(10);
  initTestMessage(builder.initRoot<TestAllTypes>());

229
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
230

231 232
  TestInputStream stream(serialized.asPtr(), true);
  InputStreamMessageReader reader(stream, ReaderOptions());
Kenton Varda's avatar
Kenton Varda committed
233 234 235 236

  checkTestMessage(reader.getRoot<TestAllTypes>());
}

237
class TestOutputStream: public kj::OutputStream {
Kenton Varda's avatar
Kenton Varda committed
238 239 240 241 242 243 244 245
public:
  TestOutputStream() {}
  ~TestOutputStream() {}

  void write(const void* buffer, size_t size) override {
    data.append(reinterpret_cast<const char*>(buffer), size);
  }

246
  const bool dataEquals(kj::ArrayPtr<const word> other) {
Kenton Varda's avatar
Kenton Varda committed
247 248 249 250 251 252 253 254 255 256 257 258
    return data ==
        std::string(reinterpret_cast<const char*>(other.begin()), other.size() * sizeof(word));
  }

private:
  std::string data;
};

TEST(Serialize, WriteMessage) {
  TestMessageBuilder builder(1);
  initTestMessage(builder.initRoot<TestAllTypes>());

259
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
260 261 262 263 264 265 266 267 268 269 270

  TestOutputStream output;
  writeMessage(output, builder);

  EXPECT_TRUE(output.dataEquals(serialized.asPtr()));
}

TEST(Serialize, WriteMessageOddSegmentCount) {
  TestMessageBuilder builder(7);
  initTestMessage(builder.initRoot<TestAllTypes>());

271
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
272 273 274 275 276 277 278

  TestOutputStream output;
  writeMessage(output, builder);

  EXPECT_TRUE(output.dataEquals(serialized.asPtr()));
}

279
TEST(Serialize, WriteMessageEvenSegmentCount) {
Kenton Varda's avatar
Kenton Varda committed
280 281 282
  TestMessageBuilder builder(10);
  initTestMessage(builder.initRoot<TestAllTypes>());

283
  kj::Array<word> serialized = messageToFlatArray(builder);
Kenton Varda's avatar
Kenton Varda committed
284 285 286 287 288 289 290 291 292

  TestOutputStream output;
  writeMessage(output, builder);

  EXPECT_TRUE(output.dataEquals(serialized.asPtr()));
}

TEST(Serialize, FileDescriptors) {
  char filename[] = "/tmp/capnproto-serialize-test-XXXXXX";
293
  kj::AutoCloseFd tmpfile(mkstemp(filename));
Kenton Varda's avatar
Kenton Varda committed
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
  ASSERT_GE(tmpfile.get(), 0);

  // Unlink the file so that it will be deleted on close.
  EXPECT_EQ(0, unlink(filename));

  {
    TestMessageBuilder builder(7);
    initTestMessage(builder.initRoot<TestAllTypes>());
    writeMessageToFd(tmpfile.get(), builder);
  }

  {
    TestMessageBuilder builder(1);
    builder.initRoot<TestAllTypes>().setTextField("second message in file");
    writeMessageToFd(tmpfile.get(), builder);
  }

  lseek(tmpfile, 0, SEEK_SET);

  {
    StreamFdMessageReader reader(tmpfile.get());
    checkTestMessage(reader.getRoot<TestAllTypes>());
  }

  {
    StreamFdMessageReader reader(tmpfile.get());
    EXPECT_EQ("second message in file", reader.getRoot<TestAllTypes>().getTextField());
  }
}

324
TEST(Serialize, RejectTooManySegments) {
Kenton Varda's avatar
Kenton Varda committed
325
  kj::Array<word> data = kj::heapArray<word>(8192);
326 327 328 329 330 331 332
  WireValue<uint32_t>* table = reinterpret_cast<WireValue<uint32_t>*>(data.begin());
  table[0].set(1024);
  for (uint i = 0; i < 1024; i++) {
    table[i+1].set(1);
  }
  TestInputStream input(data.asPtr(), false);

333
  kj::Maybe<kj::Exception> e = kj::runCatchingExceptions([&]() {
334
    InputStreamMessageReader reader(input);
335
#if !KJ_NO_EXCEPTIONS
336
    ADD_FAILURE() << "Should have thrown an exception.";
337 338 339 340
#endif
  });

  EXPECT_TRUE(e != nullptr) << "Should have thrown an exception.";
341 342 343 344 345 346
}

TEST(Serialize, RejectHugeMessage) {
  // A message whose root struct contains two words of data!
  AlignedData<4> data = {{0,0,0,0,3,0,0,0, 0,0,0,0,2,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}};

347
  TestInputStream input(kj::arrayPtr(data.words, 4), false);
348 349 350 351 352

  // We'll set the traversal limit to 2 words so our 3-word message is too big.
  ReaderOptions options;
  options.traversalLimitInWords = 2;

353
  kj::Maybe<kj::Exception> e = kj::runCatchingExceptions([&]() {
354
    InputStreamMessageReader reader(input, options);
355
#if !KJ_NO_EXCEPTIONS
356
    ADD_FAILURE() << "Should have thrown an exception.";
357 358 359 360
#endif
  });

  EXPECT_TRUE(e != nullptr) << "Should have thrown an exception.";
361 362
}

363
// TODO(test):  Test error cases.
Kenton Varda's avatar
Kenton Varda committed
364 365

}  // namespace
366
}  // namespace _ (private)
367
}  // namespace capnp