YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/env-test.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
33
#include <fcntl.h>
34
#include <sys/types.h>
35
36
#include <memory>
37
#include <string>
38
39
#include <glog/logging.h>
40
#include <gtest/gtest.h>
41
42
#include "yb/gutil/bind.h"
43
#include "yb/gutil/strings/substitute.h"
44
#include "yb/gutil/strings/util.h"
45
#include "yb/util/alignment.h"
46
#include "yb/util/stol_utils.h"
47
#include "yb/util/crc.h"
48
#include "yb/util/env.h"
49
#include "yb/util/env_util.h"
50
#include "yb/util/memenv/memenv.h"
51
#include "yb/util/os-util.h"
52
#include "yb/util/random.h"
53
#include "yb/util/random_util.h"
54
#include "yb/util/path_util.h"
55
#include "yb/util/status.h"
56
#include "yb/util/stopwatch.h"
57
#include "yb/util/test_macros.h"
58
#include "yb/util/test_util.h"
59
60
DECLARE_int32(o_direct_block_size_bytes);
61
DECLARE_bool(TEST_simulate_fs_without_fallocate);
62
63
#if !defined(__APPLE__)
64
#include <linux/falloc.h>
65
#endif  // !defined(__APPLE__)
66
// Copied from falloc.h. Useful for older kernels that lack support for
67
// hole punching; fallocate(2) will return EOPNOTSUPP.
68
#ifndef FALLOC_FL_KEEP_SIZE
69
#define FALLOC_FL_KEEP_SIZE 0x01 /* default is extend size */
70
#endif
71
#ifndef FALLOC_FL_PUNCH_HOLE
72
#define FALLOC_FL_PUNCH_HOLE  0x02 /* de-allocates range */
73
#endif
74
75
using namespace std::placeholders;
76
77
namespace yb {
78
79
using std::shared_ptr;
80
using std::string;
81
using std::vector;
82
83
static const uint64_t kOneMb = 1024 * 1024;
84
85
class TestEnv : public YBTest, public ::testing::WithParamInterface<bool> {
86
 public:
87
21
  void SetUp() override {
88
21
    YBTest::SetUp();
89
21
    CheckFallocateSupport();
90
21
  }
91
92
  // Verify that fallocate() is supported in the test directory.
93
  // Some local file systems like ext3 do not support it, and we don't
94
  // want to fail tests on those systems.
95
  //
96
  // Sets fallocate_supported_ based on the result.
97
21
  void CheckFallocateSupport() {
98
21
    static bool checked = false;
99
21
    if (checked) return;
100
101
21
    if (FLAGS_TEST_simulate_fs_without_fallocate) {
102
0
      checked = true;
103
0
      return;
104
0
    }
105
106
#if defined(__linux__)
107
    int fd = creat(GetTestPath("check-fallocate").c_str(), S_IWUSR);
108
    PCHECK(fd >= 0);
109
    int err = fallocate(fd, 0, 0, 4096);
110
    if (err != 0) {
111
      PCHECK(errno == ENOTSUP);
112
    } else {
113
      fallocate_supported_ = true;
114
115
      err = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
116
                      1024, 1024);
117
      if (err != 0) {
118
        PCHECK(errno == ENOTSUP);
119
      } else {
120
        fallocate_punch_hole_supported_ = true;
121
      }
122
    }
123
124
    close(fd);
125
#endif
126
127
21
    checked = true;
128
21
  }
129
130
 protected:
131
132
12
  void VerifyTestData(const Slice& read_data, size_t offset) {
133
21.5M
    for (size_t i = 0; i < read_data.size(); i++) {
134
21.5M
      size_t file_offset = offset + i;
135
43.0M
      ASSERT_EQ((file_offset * 31) & 0xff, read_data[i]) << "failed at " << i;
136
21.5M
    }
137
12
  }
138
139
  void VerifyChecksumsMatch(const string file_path, size_t file_size, uint64_t expected_checksum,
140
1
                            const crc::Crc* crc32c) {
141
1
    shared_ptr<RandomAccessFile> raf;
142
1
    ASSERT_OK(env_util::OpenFileForRandom(env_.get(), file_path, &raf));
143
1
    Slice slice;
144
1
    std::unique_ptr<uint8_t[]> scratch(new uint8_t[file_size]);
145
1
    ASSERT_OK(env_util::ReadFully(raf.get(), 0, file_size, &slice, scratch.get()));
146
1
    ASSERT_EQ(file_size, slice.size());
147
1
    uint64_t file_checksum = 0;
148
1
    crc32c->Compute(slice.data(), slice.size(), &file_checksum);
149
2
    ASSERT_EQ(file_checksum, expected_checksum) << "File checksum didn't match expected checksum";
150
1
  }
151
152
  void MakeVectors(size_t num_slices, size_t slice_size, size_t num_iterations,
153
2
                   std::unique_ptr<faststring[]>* data, vector<vector<Slice > >* vec) {
154
2
    data->reset(new faststring[num_iterations * num_slices]);
155
2
    vec->resize(num_iterations);
156
157
2
    int data_idx = 0;
158
2
    int byte_idx = 0;
159
12
    for (size_t vec_idx = 0; vec_idx < num_iterations; vec_idx++) {
160
10
      vector<Slice>& iter_vec = vec->at(vec_idx);
161
10
      iter_vec.resize(num_slices);
162
20.0k
      for (size_t i = 0; i < num_slices; i++) {
163
20.0k
        (*data)[data_idx].resize(slice_size);
164
20.5M
        for (size_t j = 0; j < slice_size; j++) {
165
20.4M
          (*data)[data_idx][j] = (byte_idx * 31) & 0xff;
166
20.4M
          ++byte_idx;
167
20.4M
        }
168
20.0k
        iter_vec[i]= Slice((*data)[data_idx]);
169
20.0k
        ++data_idx;
170
20.0k
      }
171
10
    }
172
2
  }
173
174
11
  void ReadAndVerifyTestData(RandomAccessFile* raf, size_t offset, size_t n) {
175
11
    std::unique_ptr<uint8_t[]> scratch(new uint8_t[n]);
176
11
    Slice s;
177
11
    ASSERT_OK(env_util::ReadFully(raf, offset, n, &s,
178
11
                                         scratch.get()));
179
11
    ASSERT_EQ(n, s.size());
180
11
    ASSERT_NO_FATALS(VerifyTestData(s, offset));
181
11
  }
182
183
  void TestAppendVector(size_t num_slices, size_t slice_size, size_t iterations,
184
2
                        bool fast, bool pre_allocate, const WritableFileOptions& opts) {
185
2
    const string kTestPath = GetTestPath("test_env_appendvec_read_append");
186
2
    shared_ptr<WritableFile> file;
187
2
    ASSERT_OK(env_util::OpenFileForWrite(opts, env_.get(), kTestPath, &file));
188
189
2
    if (pre_allocate) {
190
0
      ASSERT_OK(file->PreAllocate(num_slices * slice_size * iterations));
191
0
      ASSERT_OK(file->Sync());
192
0
    }
193
194
2
    std::unique_ptr<faststring[]> data;
195
2
    vector<vector<Slice> > input;
196
197
2
    MakeVectors(num_slices, slice_size, iterations, &data, &input);
198
199
2
    shared_ptr<RandomAccessFile> raf;
200
201
2
    if (!fast) {
202
0
      ASSERT_OK(env_util::OpenFileForRandom(env_.get(), kTestPath, &raf));
203
0
    }
204
205
2
    srand(123);
206
207
2
    const string test_descr = strings::Substitute(
208
2
        "appending a vector of slices(number of slices=$0,size of slice=$1 b) $2 times",
209
2
        num_slices, slice_size, iterations);
210
2
    LOG_TIMING(INFO, test_descr)  {
211
12
      for (size_t i = 0; i < iterations; i++) {
212
10
        if (fast || random() % 2) {
213
10
          ASSERT_OK(file->AppendVector(input[i]));
214
0
        } else {
215
0
          for (const Slice& slice : input[i]) {
216
0
            ASSERT_OK(file->Append(slice));
217
0
          }
218
0
        }
219
10
        if (!fast) {
220
          // Verify as write. Note: this requires that file is pre-allocated, otherwise
221
          // the ReadFully() fails with EINVAL.
222
0
          if (opts.o_direct) {
223
0
            ASSERT_OK(file->Sync());
224
0
          }
225
0
          ASSERT_NO_FATALS(ReadAndVerifyTestData(raf.get(), num_slices * slice_size * i,
226
0
                                                        num_slices * slice_size));
227
0
        }
228
10
      }
229
2
    }
230
231
    // Verify the entire file
232
2
    ASSERT_OK(file->Close());
233
234
2
    if (fast) {
235
2
      ASSERT_OK(env_util::OpenFileForRandom(env_.get(), kTestPath, &raf));
236
2
    }
237
12
    for (size_t i = 0; i < iterations; i++) {
238
10
      ASSERT_NO_FATALS(ReadAndVerifyTestData(raf.get(), num_slices * slice_size * i,
239
10
                                                    num_slices * slice_size));
240
10
    }
241
2
  }
242
243
1
  void TestAppendRandomData(bool pre_allocate, const WritableFileOptions& opts) {
244
1
    const string kTestPath = GetTestPath("test_env_append_random_read_append");
245
1
    const uint64_t kMaxFileSize = 64 * 1024 * 1024;
246
1
    shared_ptr<WritableFile> file;
247
1
    ASSERT_OK(env_util::OpenFileForWrite(opts, env_.get(), kTestPath, &file));
248
249
1
    if (pre_allocate) {
250
1
      ASSERT_OK(file->PreAllocate(kMaxFileSize));
251
1
      ASSERT_OK(file->Sync());
252
1
    }
253
254
1
    Random rnd(SeedRandom());
255
256
1
    size_t total_size = 0;
257
1
    const int kBufSize = (IOV_MAX + 10) * FLAGS_o_direct_block_size_bytes;
258
1
    char buf[kBufSize];
259
1
    crc::Crc* crc32c = crc::GetCrc32cInstance();
260
1
    uint64_t actual_checksum = 0;
261
262
163
    while (true) {
263
163
      auto i = rnd.Uniform(10);
264
163
      size_t slice_size;
265
163
      if (i < 4) {
266
        // 40% of the time pick a size between 1 and FLAGS_o_direct_block_size_bytes.
267
60
        slice_size = rnd.Uniform(FLAGS_o_direct_block_size_bytes) + 1;
268
103
      } else if (i >= 4 && i < 8) {
269
        // 40% of the time pick a size between block_size and 10*FLAGS_o_direct_block_size_bytes
270
        // that is a multiple of FLAGS_o_direct_block_size_bytes.
271
64
        slice_size = (rnd.Uniform(10) + 1) * FLAGS_o_direct_block_size_bytes;
272
39
      } else if (i == 8) {
273
        // 10% of the time pick a size greater than the IOV_MAX * FLAGS_o_direct_block_size_bytes.
274
16
        slice_size = IOV_MAX * FLAGS_o_direct_block_size_bytes +
275
16
            rnd.Uniform(10 * FLAGS_o_direct_block_size_bytes);
276
23
      } else {
277
        // 10% of the time pick a size such that the file size after writing this slice is a
278
        // multiple of FLAGS_o_direct_block_size_bytes.
279
23
        auto bytes_needed = align_up(total_size, FLAGS_o_direct_block_size_bytes) - total_size;
280
23
        slice_size = FLAGS_o_direct_block_size_bytes + bytes_needed;
281
23
      }
282
163
      if (total_size + slice_size > kMaxFileSize) {
283
1
        break;
284
1
      }
285
162
      total_size += slice_size;
286
287
162
      RandomString(buf, slice_size, &rnd);
288
162
      auto slice = Slice(buf, slice_size);
289
162
      ASSERT_OK(file->Append(slice));
290
162
      ASSERT_EQ(total_size, file->Size());
291
292
      // Compute a rolling checksum over the two byte arrays (size, body).
293
162
      crc32c->Compute(slice.data(), slice.size(), &actual_checksum);
294
295
162
      if (rnd.Uniform(5) == 0) {
296
39
        ASSERT_OK(file->Sync());
297
39
        ASSERT_EQ(total_size, file->Size());
298
39
      }
299
162
    }
300
301
    // Verify the entire file
302
1
    ASSERT_OK(file->Close());
303
1
    ASSERT_NO_FATALS(VerifyChecksumsMatch(kTestPath, total_size, actual_checksum, crc32c));
304
1
  }
305
306
  static bool fallocate_supported_;
307
  static bool fallocate_punch_hole_supported_;
308
};
309
310
bool TestEnv::fallocate_supported_ = false;
311
bool TestEnv::fallocate_punch_hole_supported_ = false;
312
313
2
TEST_P(TestEnv, TestPreallocate) {
314
2
  if (!fallocate_supported_) {
315
2
    LOG(INFO) << "fallocate not supported, skipping test";
316
2
    return;
317
2
  }
318
0
  LOG(INFO) << "Testing PreAllocate()";
319
0
  string test_path = GetTestPath("test_env_wf");
320
0
  shared_ptr<WritableFile> file;
321
0
  WritableFileOptions opts;
322
0
  opts.o_direct = GetParam();
323
0
  ASSERT_OK(env_util::OpenFileForWrite(opts,
324
0
                                       env_.get(), test_path, &file));
325
326
  // pre-allocate 1 MB
327
0
  ASSERT_OK(file->PreAllocate(kOneMb));
328
0
  ASSERT_OK(file->Sync());
329
330
  // the writable file size should report 0
331
0
  ASSERT_EQ(file->Size(), 0);
332
  // but the real size of the file on disk should report 1MB
333
0
  uint64_t size = ASSERT_RESULT(env_->GetFileSize(test_path));
334
0
  ASSERT_EQ(size, kOneMb);
335
336
  // write 1 MB
337
0
  uint8_t scratch[kOneMb];
338
0
  Slice slice(scratch, kOneMb);
339
0
  ASSERT_OK(file->Append(slice));
340
0
  ASSERT_OK(file->Sync());
341
342
  // the writable file size should now report 1 MB
343
0
  ASSERT_EQ(file->Size(), kOneMb);
344
0
  ASSERT_OK(file->Close());
345
  // and the real size for the file on disk should match ony the
346
  // written size
347
0
  size = ASSERT_RESULT(env_->GetFileSize(test_path));
348
0
  ASSERT_EQ(kOneMb, size);
349
0
}
350
351
// To test consecutive pre-allocations we need higher pre-allocations since the
352
// mmapped regions grow in size until 2MBs (so smaller pre-allocations will easily
353
// be smaller than the mmapped regions size).
354
1
TEST_F(TestEnv, TestConsecutivePreallocate) {
355
1
  if (!fallocate_supported_) {
356
1
    LOG(INFO) << "fallocate not supported, skipping test";
357
1
    return;
358
1
  }
359
0
  LOG(INFO) << "Testing consecutive PreAllocate()";
360
0
  string test_path = GetTestPath("test_env_wf");
361
0
  shared_ptr<WritableFile> file;
362
0
  ASSERT_OK(env_util::OpenFileForWrite(
363
0
      WritableFileOptions(), env_.get(), test_path, &file));
364
365
  // pre-allocate 64 MB
366
0
  ASSERT_OK(file->PreAllocate(64 * kOneMb));
367
0
  ASSERT_OK(file->Sync());
368
369
  // the writable file size should report 0
370
0
  ASSERT_EQ(file->Size(), 0);
371
  // but the real size of the file on disk should report 64 MBs
372
0
  uint64_t size = ASSERT_RESULT(env_->GetFileSize(test_path));
373
0
  ASSERT_EQ(size, 64 * kOneMb);
374
375
  // write 1 MB
376
0
  uint8_t scratch[kOneMb];
377
0
  Slice slice(scratch, kOneMb);
378
0
  ASSERT_OK(file->Append(slice));
379
0
  ASSERT_OK(file->Sync());
380
381
  // the writable file size should now report 1 MB
382
0
  ASSERT_EQ(kOneMb, file->Size());
383
0
  size = ASSERT_RESULT(env_->GetFileSize(test_path));
384
0
  ASSERT_EQ(64 * kOneMb, size);
385
386
  // pre-allocate 64 additional MBs
387
0
  ASSERT_OK(file->PreAllocate(64 * kOneMb));
388
0
  ASSERT_OK(file->Sync());
389
390
  // the writable file size should now report 1 MB
391
0
  ASSERT_EQ(kOneMb, file->Size());
392
  // while the real file size should report 128 MB's
393
0
  size = ASSERT_RESULT(env_->GetFileSize(test_path));
394
0
  ASSERT_EQ(128 * kOneMb, size);
395
396
  // write another MB
397
0
  ASSERT_OK(file->Append(slice));
398
0
  ASSERT_OK(file->Sync());
399
400
  // the writable file size should now report 2 MB
401
0
  ASSERT_EQ(file->Size(), 2 * kOneMb);
402
  // while the real file size should reamin at 128 MBs
403
0
  size = ASSERT_RESULT(env_->GetFileSize(test_path));
404
0
  ASSERT_EQ(128 * kOneMb, size);
405
406
  // close the file (which ftruncates it to the real size)
407
0
  ASSERT_OK(file->Close());
408
  // and the real size for the file on disk should match only the written size
409
0
  size = ASSERT_RESULT(env_->GetFileSize(test_path));
410
0
  ASSERT_EQ(2* kOneMb, size);
411
412
0
}
413
414
1
TEST_F(TestEnv, TestHolePunch) {
415
1
  if (!fallocate_punch_hole_supported_) {
416
1
    LOG(INFO) << "hole punching not supported, skipping test";
417
1
    return;
418
1
  }
419
0
  string test_path = GetTestPath("test_env_wf");
420
0
  std::unique_ptr<RWFile> file;
421
0
  ASSERT_OK(env_->NewRWFile(test_path, &file));
422
423
  // Write 1 MB. The size and size-on-disk both agree.
424
0
  uint8_t scratch[kOneMb];
425
0
  Slice slice(scratch, kOneMb);
426
0
  ASSERT_OK(file->Write(0, slice));
427
0
  ASSERT_OK(file->Sync());
428
0
  uint64_t sz;
429
0
  ASSERT_OK(file->Size(&sz));
430
0
  ASSERT_EQ(kOneMb, sz);
431
0
  uint64_t size_on_disk = ASSERT_RESULT(env_->GetFileSizeOnDisk(test_path));
432
  // Some kernels and filesystems (e.g. Centos 6.6 with XFS) aggressively
433
  // preallocate file disk space when writing to files, so the disk space may be
434
  // greater than 1MiB.
435
0
  ASSERT_LE(kOneMb, size_on_disk);
436
437
  // Punch some data out at byte marker 4096. Now the two sizes diverge.
438
0
  uint64_t punch_amount = 4096 * 4;
439
0
  uint64_t new_size_on_disk;
440
0
  ASSERT_OK(file->PunchHole(4096, punch_amount));
441
0
  ASSERT_OK(file->Size(&sz));
442
0
  ASSERT_EQ(kOneMb, sz);
443
0
  new_size_on_disk = ASSERT_RESULT(env_->GetFileSizeOnDisk(test_path));
444
0
  ASSERT_EQ(size_on_disk - punch_amount, new_size_on_disk);
445
0
}
446
447
class ShortReadRandomAccessFile : public RandomAccessFile {
448
 public:
449
  explicit ShortReadRandomAccessFile(shared_ptr<RandomAccessFile> wrapped)
450
1
      : wrapped_(std::move(wrapped)), seed_(SeedRandom()) {}
451
452
  virtual Status Read(uint64_t offset, size_t n, Slice* result,
453
8
                      uint8_t *scratch) const override {
454
8
    CHECK_GT(n, 0);
455
    // Divide the requested amount of data by a small integer,
456
    // and issue the shorter read to the underlying file.
457
8
    auto short_n = n / ((rand_r(&seed_) % 3) + 1);
458
8
    if (short_n == 0) {
459
0
      short_n = 1;
460
0
    }
461
462
0
    VLOG(1) << "Reading " << short_n << " instead of " << n;
463
464
8
    return wrapped_->Read(offset, short_n, result, scratch);
465
8
  }
466
467
0
  Result<uint64_t> Size() const override {
468
0
    return wrapped_->Size();
469
0
  }
470
471
0
  Result<uint64_t> INode() const override {
472
0
    return wrapped_->INode();
473
0
  }
474
475
0
  const string& filename() const override { return wrapped_->filename(); }
476
477
0
  size_t memory_footprint() const override {
478
0
    return wrapped_->memory_footprint();
479
0
  }
480
481
 private:
482
  const shared_ptr<RandomAccessFile> wrapped_;
483
  mutable unsigned int seed_;
484
};
485
486
// Write 'size' bytes of data to a file, with a simple pattern stored in it.
487
3
static void WriteTestFile(Env* env, const string& path, size_t size) {
488
3
  shared_ptr<WritableFile> wf;
489
3
  ASSERT_OK(env_util::OpenFileForWrite(env, path, &wf));
490
3
  faststring data;
491
3
  data.resize(size);
492
1.11M
  for (size_t i = 0; i < data.size(); i++) {
493
1.11M
    data[i] = (i * 31) & 0xff;
494
1.11M
  }
495
3
  ASSERT_OK(wf->Append(Slice(data)));
496
3
  ASSERT_OK(wf->Close());
497
3
}
498
499
1
TEST_F(TestEnv, TestReadFully) {
500
1
  SeedRandom();
501
1
  const string kTestPath = "test";
502
1
  const int kFileSize = 64 * 1024;
503
1
  std::unique_ptr<Env> mem(NewMemEnv(Env::Default()));
504
505
1
  WriteTestFile(mem.get(), kTestPath, kFileSize);
506
1
  ASSERT_NO_FATALS();
507
508
  // Reopen for read
509
1
  shared_ptr<RandomAccessFile> raf;
510
1
  ASSERT_OK(env_util::OpenFileForRandom(mem.get(), kTestPath, &raf));
511
512
1
  ShortReadRandomAccessFile sr_raf(raf);
513
514
1
  const int kReadLength = 10000;
515
1
  Slice s;
516
1
  std::unique_ptr<uint8_t[]> scratch(new uint8_t[kReadLength]);
517
518
  // Verify that ReadFully reads the whole requested data.
519
1
  ASSERT_OK(env_util::ReadFully(&sr_raf, 0, kReadLength, &s, scratch.get()));
520
2
  ASSERT_EQ(s.data(), scratch.get()) << "Should have returned a contiguous copy";
521
1
  ASSERT_EQ(kReadLength, s.size());
522
523
  // Verify that the data read was correct.
524
1
  VerifyTestData(s, 0);
525
526
  // Verify that ReadFully fails with an IOError at EOF.
527
1
  Status status = env_util::ReadFully(&sr_raf, kFileSize - 100, 200, &s, scratch.get());
528
1
  ASSERT_FALSE(status.ok());
529
1
  ASSERT_TRUE(status.IsIOError());
530
1
  ASSERT_STR_CONTAINS(status.ToString(), "EOF");
531
1
}
532
533
2
TEST_P(TestEnv, TestAppendVector) {
534
2
  WritableFileOptions opts;
535
2
  opts.o_direct = GetParam();
536
2
  LOG(INFO) << "Testing AppendVector() only, NO pre-allocation";
537
2
  ASSERT_NO_FATALS(TestAppendVector(2000, 1024, 5, true, false, opts));
538
539
2
  if (!fallocate_supported_) {
540
2
    LOG(INFO) << "fallocate not supported, skipping preallocated runs";
541
0
  } else {
542
0
    LOG(INFO) << "Testing AppendVector() only, WITH pre-allocation";
543
0
    ASSERT_NO_FATALS(TestAppendVector(2000, 1024, 5, true, true, opts));
544
0
    LOG(INFO) << "Testing AppendVector() together with Append() and Read(), WITH pre-allocation";
545
0
    ASSERT_NO_FATALS(TestAppendVector(128, 4096, 5, false, true, opts));
546
0
  }
547
2
}
548
549
1
TEST_F(TestEnv, TestRandomData) {
550
1
  WritableFileOptions opts;
551
1
  opts.o_direct = true;
552
1
  LOG(INFO) << "Testing Append() with random data and requests of random sizes";
553
1
  ASSERT_NO_FATALS(TestAppendRandomData(true, opts));
554
1
}
555
556
1
TEST_F(TestEnv, TestGetExecutablePath) {
557
1
  string p;
558
1
  ASSERT_OK(Env::Default()->GetExecutablePath(&p));
559
2
  ASSERT_TRUE(HasSuffixString(p, "env-test")) << p;
560
1
}
561
562
1
TEST_F(TestEnv, TestOpenEmptyRandomAccessFile) {
563
1
  Env* env = Env::Default();
564
1
  string test_file = JoinPathSegments(GetTestDataDirectory(), "test_file");
565
1
  ASSERT_NO_FATALS(WriteTestFile(env, test_file, 0));
566
1
  std::unique_ptr<RandomAccessFile> readable_file;
567
1
  ASSERT_OK(env->NewRandomAccessFile(test_file, &readable_file));
568
1
  uint64_t size = ASSERT_RESULT(readable_file->Size());
569
1
  ASSERT_EQ(0, size);
570
1
}
571
572
1
TEST_F(TestEnv, TestOverwrite) {
573
1
  string test_path = GetTestPath("test_env_wf");
574
575
  // File does not exist, create it.
576
1
  shared_ptr<WritableFile> writer;
577
1
  ASSERT_OK(env_util::OpenFileForWrite(env_.get(), test_path, &writer));
578
579
  // File exists, overwrite it.
580
1
  ASSERT_OK(env_util::OpenFileForWrite(env_.get(), test_path, &writer));
581
582
  // File exists, try to overwrite (and fail).
583
1
  WritableFileOptions opts;
584
1
  opts.mode = Env::CREATE_NON_EXISTING;
585
1
  Status s = env_util::OpenFileForWrite(opts,
586
1
                                        env_.get(), test_path, &writer);
587
1
  ASSERT_TRUE(s.IsAlreadyPresent());
588
1
}
589
590
1
TEST_F(TestEnv, TestReopen) {
591
1
  LOG(INFO) << "Testing reopening behavior";
592
1
  string test_path = GetTestPath("test_env_wf");
593
1
  string first = "The quick brown fox";
594
1
  string second = "jumps over the lazy dog";
595
596
  // Create the file and write to it.
597
1
  shared_ptr<WritableFile> writer;
598
1
  ASSERT_OK(env_util::OpenFileForWrite(WritableFileOptions(),
599
1
                                       env_.get(), test_path, &writer));
600
1
  ASSERT_OK(writer->Append(first));
601
1
  ASSERT_EQ(first.length(), writer->Size());
602
1
  ASSERT_OK(writer->Close());
603
604
  // Reopen it and append to it.
605
1
  WritableFileOptions reopen_opts;
606
1
  reopen_opts.mode = Env::OPEN_EXISTING;
607
1
  ASSERT_OK(env_util::OpenFileForWrite(reopen_opts,
608
1
                                       env_.get(), test_path, &writer));
609
1
  ASSERT_EQ(first.length(), writer->Size());
610
1
  ASSERT_OK(writer->Append(second));
611
1
  ASSERT_EQ(first.length() + second.length(), writer->Size());
612
1
  ASSERT_OK(writer->Close());
613
614
  // Check that the file has both strings.
615
1
  shared_ptr<RandomAccessFile> reader;
616
1
  ASSERT_OK(env_util::OpenFileForRandom(env_.get(), test_path, &reader));
617
1
  uint64_t size = ASSERT_RESULT(reader->Size());
618
1
  ASSERT_EQ(first.length() + second.length(), size);
619
1
  Slice s;
620
1
  std::vector<uint8_t> scratch(size);
621
1
  ASSERT_OK(env_util::ReadFully(reader.get(), 0, size, &s, scratch.data()));
622
1
  ASSERT_EQ(first + second, s.ToString());
623
1
}
624
625
1
TEST_F(TestEnv, TestIsDirectory) {
626
1
  string dir = GetTestPath("a_directory");
627
1
  ASSERT_OK(env_->CreateDir(dir));
628
1
  bool is_dir;
629
1
  ASSERT_OK(env_->IsDirectory(dir, &is_dir));
630
1
  ASSERT_TRUE(is_dir);
631
632
1
  string not_dir = GetTestPath("not_a_directory");
633
1
  std::unique_ptr<WritableFile> writer;
634
1
  ASSERT_OK(env_->NewWritableFile(not_dir, &writer));
635
1
  ASSERT_OK(env_->IsDirectory(not_dir, &is_dir));
636
1
  ASSERT_FALSE(is_dir);
637
1
}
638
639
static Status TestWalkCb(vector<string>* actual,
640
                         Env::FileType type,
641
12
                         const string& dirname, const string& basename) {
642
0
  VLOG(1) << type << ":" << dirname << ":" << basename;
643
12
  actual->push_back(JoinPathSegments(dirname, basename));
644
12
  return Status::OK();
645
12
}
646
647
4
static Status CreateDir(Env* env, const string& name, vector<string>* created) {
648
4
  RETURN_NOT_OK(env->CreateDir(name));
649
4
  created->push_back(name);
650
4
  return Status::OK();
651
4
}
652
653
8
static Status CreateFile(Env* env, const string& name, vector<string>* created) {
654
8
  std::unique_ptr<WritableFile> writer;
655
8
  RETURN_NOT_OK(env->NewWritableFile(name, &writer));
656
8
  created->push_back(writer->filename());
657
8
  return Status::OK();
658
8
}
659
660
1
TEST_F(TestEnv, TestWalk) {
661
  // We test with this tree:
662
  //
663
  // /root/
664
  // /root/file_1
665
  // /root/file_2
666
  // /root/dir_a/file_1
667
  // /root/dir_a/file_2
668
  // /root/dir_b/file_1
669
  // /root/dir_b/file_2
670
  // /root/dir_b/dir_c/file_1
671
  // /root/dir_b/dir_c/file_2
672
1
  string root = GetTestPath("root");
673
1
  string subdir_a = JoinPathSegments(root, "dir_a");
674
1
  string subdir_b = JoinPathSegments(root, "dir_b");
675
1
  string subdir_c = JoinPathSegments(subdir_b, "dir_c");
676
1
  string file_one = "file_1";
677
1
  string file_two = "file_2";
678
1
  vector<string> expected;
679
1
  ASSERT_OK(CreateDir(env_.get(), root, &expected));
680
1
  ASSERT_OK(CreateFile(env_.get(), JoinPathSegments(root, file_one), &expected));
681
1
  ASSERT_OK(CreateFile(env_.get(), JoinPathSegments(root, file_two), &expected));
682
1
  ASSERT_OK(CreateDir(env_.get(), subdir_a, &expected));
683
1
  ASSERT_OK(CreateFile(env_.get(), JoinPathSegments(subdir_a, file_one), &expected));
684
1
  ASSERT_OK(CreateFile(env_.get(), JoinPathSegments(subdir_a, file_two), &expected));
685
1
  ASSERT_OK(CreateDir(env_.get(), subdir_b, &expected));
686
1
  ASSERT_OK(CreateFile(env_.get(), JoinPathSegments(subdir_b, file_one), &expected));
687
1
  ASSERT_OK(CreateFile(env_.get(), JoinPathSegments(subdir_b, file_two), &expected));
688
1
  ASSERT_OK(CreateDir(env_.get(), subdir_c, &expected));
689
1
  ASSERT_OK(CreateFile(env_.get(), JoinPathSegments(subdir_c, file_one), &expected));
690
1
  ASSERT_OK(CreateFile(env_.get(), JoinPathSegments(subdir_c, file_two), &expected));
691
692
  // Do the walk.
693
  //
694
  // Sadly, tr1/unordered_set doesn't implement equality operators, so we
695
  // compare sorted vectors instead.
696
1
  vector<string> actual;
697
1
  ASSERT_OK(env_->Walk(root, Env::PRE_ORDER, std::bind(&TestWalkCb, &actual, _1, _2, _3)));
698
1
  sort(expected.begin(), expected.end());
699
1
  sort(actual.begin(), actual.end());
700
1
  ASSERT_EQ(expected, actual);
701
1
}
702
703
static Status TestWalkErrorCb(int* num_calls,
704
                              Env::FileType type,
705
2
                              const string& dirname, const string& basename) {
706
2
  (*num_calls)++;
707
2
  return STATUS(Aborted, "Returning abort status");
708
2
}
709
710
1
TEST_F(TestEnv, TestWalkCbReturnsError) {
711
1
  string new_dir = GetTestPath("foo");
712
1
  string new_file = "myfile";
713
1
  ASSERT_OK(env_->CreateDir(new_dir));
714
1
  std::unique_ptr<WritableFile> writer;
715
1
  ASSERT_OK(env_->NewWritableFile(JoinPathSegments(new_dir, new_file), &writer));
716
1
  int num_calls = 0;
717
1
  ASSERT_TRUE(env_->Walk(new_dir, Env::PRE_ORDER,
718
1
                         std::bind(&TestWalkErrorCb, &num_calls, _1, _2, _3)).IsIOError());
719
720
  // Once for the directory and once for the file inside it.
721
1
  ASSERT_EQ(2, num_calls);
722
1
}
723
724
1
TEST_F(TestEnv, TestGetBlockSize) {
725
  // Does not exist.
726
1
  auto result = env_->GetBlockSize("does_not_exist");
727
1
  ASSERT_TRUE(!result.ok() && result.status().IsNotFound());
728
729
  // Try with a directory.
730
1
  auto block_size = ASSERT_RESULT(env_->GetBlockSize("."));
731
1
  ASSERT_GT(block_size, 0);
732
733
  // Try with a file.
734
1
  string path = GetTestPath("foo");
735
1
  std::unique_ptr<WritableFile> writer;
736
1
  ASSERT_OK(env_->NewWritableFile(path, &writer));
737
1
  block_size = ASSERT_RESULT(env_->GetBlockSize(path));
738
1
  ASSERT_GT(block_size, 0);
739
1
}
740
741
1
TEST_F(TestEnv, TestRWFile) {
742
  // Create the file.
743
1
  std::unique_ptr<RWFile> file;
744
1
  ASSERT_OK(env_->NewRWFile(GetTestPath("foo"), &file));
745
746
  // Append to it.
747
1
  string kTestData = "abcde";
748
1
  ASSERT_OK(file->Write(0, kTestData));
749
750
  // Read from it.
751
1
  Slice result;
752
1
  std::unique_ptr<uint8_t[]> scratch(new uint8_t[kTestData.length()]);
753
1
  ASSERT_OK(file->Read(0, kTestData.length(), &result, scratch.get()));
754
1
  ASSERT_EQ(result, kTestData);
755
1
  uint64_t sz;
756
1
  ASSERT_OK(file->Size(&sz));
757
1
  ASSERT_EQ(kTestData.length(), sz);
758
759
  // Write past the end of the file and rewrite some of the interior.
760
1
  ASSERT_OK(file->Write(kTestData.length() * 2, kTestData));
761
1
  ASSERT_OK(file->Write(kTestData.length(), kTestData));
762
1
  ASSERT_OK(file->Write(1, kTestData));
763
1
  string kNewTestData = "aabcdebcdeabcde";
764
1
  std::unique_ptr<uint8_t[]> scratch2(new uint8_t[kNewTestData.length()]);
765
1
  ASSERT_OK(file->Read(0, kNewTestData.length(), &result, scratch2.get()));
766
767
  // Retest.
768
1
  ASSERT_EQ(result, kNewTestData);
769
1
  ASSERT_OK(file->Size(&sz));
770
1
  ASSERT_EQ(kNewTestData.length(), sz);
771
772
  // Make sure we can't overwrite it.
773
1
  RWFileOptions opts;
774
1
  opts.mode = Env::CREATE_NON_EXISTING;
775
1
  ASSERT_TRUE(env_->NewRWFile(opts, GetTestPath("foo"), &file).IsAlreadyPresent());
776
777
  // Reopen it without truncating the existing data.
778
1
  opts.mode = Env::OPEN_EXISTING;
779
1
  ASSERT_OK(env_->NewRWFile(opts, GetTestPath("foo"), &file));
780
1
  ASSERT_OK(file->Read(0, kNewTestData.length(), &result, scratch2.get()));
781
1
  ASSERT_EQ(result, kNewTestData);
782
1
}
783
784
1
TEST_F(TestEnv, TestCanonicalize) {
785
1
  vector<string> synonyms = { GetTestPath("."), GetTestPath("./."), GetTestPath(".//./") };
786
3
  for (const string& synonym : synonyms) {
787
3
    string result;
788
3
    ASSERT_OK(env_->Canonicalize(synonym, &result));
789
3
    ASSERT_EQ(GetTestDataDirectory(), result);
790
3
  }
791
792
1
  string dir = GetTestPath("some_dir");
793
1
  ASSERT_OK(env_->CreateDir(dir));
794
1
  string result;
795
1
  ASSERT_OK(env_->Canonicalize(dir + "/", &result));
796
1
  ASSERT_EQ(dir, result);
797
798
1
  ASSERT_TRUE(env_->Canonicalize(dir + "/bar", nullptr).IsNotFound());
799
1
}
800
801
1
TEST_F(TestEnv, TestGetTotalRAMBytes) {
802
1
  int64_t ram = 0;
803
1
  ASSERT_OK(env_->GetTotalRAMBytes(&ram));
804
805
  // Can't test much about it.
806
1
  ASSERT_GT(ram, 0);
807
1
}
808
809
1
TEST_F(TestEnv, TestGetFreeSpace) {
810
1
  char cwd[1024];
811
1
  char* ret = getcwd(cwd, sizeof(cwd));
812
1
  ASSERT_NE(ret, nullptr);
813
814
1
  constexpr int64_t kMaxAllowedDeltaBytes = 65536;
815
816
  // Number of times the difference between the return value from GetFreeSpaceBytes and
817
  // the output from command 'df' should be less than kMaxAllowedDeltaBytes before we consider
818
  // this test has passed.
819
1
  constexpr int kCountRequired = 10;
820
821
  // Minimum block size for MacOS is 512.
822
1
  constexpr int block_size = 512;
823
1
  const string cmd = strings::Substitute(
824
1
      "(export BLOCKSIZE=$0; df $1 | tail -1 | awk '{print $$4}' | tr -d '\\n')", block_size, cwd);
825
826
1
  int success_count = 0;
827
10
  for (int i = 0; i < kCountRequired * 10; i++) {
828
10
    const int64_t free_space = static_cast<int64_t>(ASSERT_RESULT(env_->GetFreeSpaceBytes(cwd)));
829
830
10
    string df_free_space_str;
831
10
    ASSERT_TRUE(RunShellProcess(cmd, &df_free_space_str));
832
10
    const int64_t df_free_space = block_size * ASSERT_RESULT(CheckedStoll(df_free_space_str));
833
834
     // We might not get the exact same answer because disk space is being consumed and freed.
835
10
    const int64_t delta_bytes = abs(df_free_space - free_space);
836
10
    if (delta_bytes > kMaxAllowedDeltaBytes) {
837
0
      LOG(INFO) << "df returned: " << df_free_space
838
0
                << ", GetFreeSpaceBytes returned: " << free_space;
839
10
    } else {
840
10
      success_count++;
841
10
      if (success_count >= kCountRequired) {
842
1
        break;
843
1
      }
844
10
    }
845
10
  }
846
1
  ASSERT_GE(success_count, kCountRequired);
847
1
}
848
849
// Test that CopyFile() copies all the bytes properly.
850
1
TEST_F(TestEnv, TestCopyFile) {
851
1
  string orig_path = GetTestPath("test");
852
1
  string copy_path = orig_path + ".copy";
853
1
  const int kFileSize = 1024 * 1024 + 11; // Some odd number of bytes.
854
855
1
  Env* env = Env::Default();
856
1
  ASSERT_NO_FATALS(WriteTestFile(env, orig_path, kFileSize));
857
1
  ASSERT_OK(env_util::CopyFile(env, orig_path, copy_path, WritableFileOptions()));
858
1
  std::unique_ptr<RandomAccessFile> copy;
859
1
  ASSERT_OK(env->NewRandomAccessFile(copy_path, &copy));
860
1
  ASSERT_NO_FATALS(ReadAndVerifyTestData(copy.get(), 0, kFileSize));
861
1
}
862
863
INSTANTIATE_TEST_CASE_P(BufferedIO, TestEnv, ::testing::Values(false));
864
INSTANTIATE_TEST_CASE_P(DirectIO, TestEnv, ::testing::Values(true));
865
866
}  // namespace yb