capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

filesystem-test.c++ (30537B)


      1 // Copyright (c) 2016 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #include "filesystem.h"
     23 #include "test.h"
     24 #include <wchar.h>
     25 
     26 namespace kj {
     27 namespace {
     28 
     29 KJ_TEST("Path") {
     30   KJ_EXPECT(Path(nullptr).toString() == ".");
     31   KJ_EXPECT(Path(nullptr).toString(true) == "/");
     32   KJ_EXPECT(Path("foo").toString() == "foo");
     33   KJ_EXPECT(Path("foo").toString(true) == "/foo");
     34 
     35   KJ_EXPECT(Path({"foo", "bar"}).toString() == "foo/bar");
     36   KJ_EXPECT(Path({"foo", "bar"}).toString(true) == "/foo/bar");
     37 
     38   KJ_EXPECT(Path::parse("foo/bar").toString() == "foo/bar");
     39   KJ_EXPECT(Path::parse("foo//bar").toString() == "foo/bar");
     40   KJ_EXPECT(Path::parse("foo/./bar").toString() == "foo/bar");
     41   KJ_EXPECT(Path::parse("foo/../bar").toString() == "bar");
     42   KJ_EXPECT(Path::parse("foo/bar/..").toString() == "foo");
     43   KJ_EXPECT(Path::parse("foo/bar/../..").toString() == ".");
     44 
     45   KJ_EXPECT(Path({"foo", "bar"}).eval("baz").toString() == "foo/bar/baz");
     46   KJ_EXPECT(Path({"foo", "bar"}).eval("./baz").toString() == "foo/bar/baz");
     47   KJ_EXPECT(Path({"foo", "bar"}).eval("baz/qux").toString() == "foo/bar/baz/qux");
     48   KJ_EXPECT(Path({"foo", "bar"}).eval("baz//qux").toString() == "foo/bar/baz/qux");
     49   KJ_EXPECT(Path({"foo", "bar"}).eval("baz/./qux").toString() == "foo/bar/baz/qux");
     50   KJ_EXPECT(Path({"foo", "bar"}).eval("baz/../qux").toString() == "foo/bar/qux");
     51   KJ_EXPECT(Path({"foo", "bar"}).eval("baz/qux/..").toString() == "foo/bar/baz");
     52   KJ_EXPECT(Path({"foo", "bar"}).eval("../baz").toString() == "foo/baz");
     53   KJ_EXPECT(Path({"foo", "bar"}).eval("baz/../../qux/").toString() == "foo/qux");
     54   KJ_EXPECT(Path({"foo", "bar"}).eval("/baz/qux").toString() == "baz/qux");
     55   KJ_EXPECT(Path({"foo", "bar"}).eval("//baz/qux").toString() == "baz/qux");
     56   KJ_EXPECT(Path({"foo", "bar"}).eval("/baz/../qux").toString() == "qux");
     57 
     58   KJ_EXPECT(Path({"foo", "bar"}).basename()[0] == "bar");
     59   KJ_EXPECT(Path({"foo", "bar", "baz"}).parent().toString() == "foo/bar");
     60 
     61   KJ_EXPECT(Path({"foo", "bar"}).append("baz").toString() == "foo/bar/baz");
     62   KJ_EXPECT(Path({"foo", "bar"}).append(Path({"baz", "qux"})).toString() == "foo/bar/baz/qux");
     63 
     64   {
     65     // Test methods which are overloaded for && on a non-rvalue path.
     66     Path path({"foo", "bar"});
     67     KJ_EXPECT(path.eval("baz").toString() == "foo/bar/baz");
     68     KJ_EXPECT(path.eval("./baz").toString() == "foo/bar/baz");
     69     KJ_EXPECT(path.eval("baz/qux").toString() == "foo/bar/baz/qux");
     70     KJ_EXPECT(path.eval("baz//qux").toString() == "foo/bar/baz/qux");
     71     KJ_EXPECT(path.eval("baz/./qux").toString() == "foo/bar/baz/qux");
     72     KJ_EXPECT(path.eval("baz/../qux").toString() == "foo/bar/qux");
     73     KJ_EXPECT(path.eval("baz/qux/..").toString() == "foo/bar/baz");
     74     KJ_EXPECT(path.eval("../baz").toString() == "foo/baz");
     75     KJ_EXPECT(path.eval("baz/../../qux/").toString() == "foo/qux");
     76     KJ_EXPECT(path.eval("/baz/qux").toString() == "baz/qux");
     77     KJ_EXPECT(path.eval("/baz/../qux").toString() == "qux");
     78 
     79     KJ_EXPECT(path.basename()[0] == "bar");
     80     KJ_EXPECT(path.parent().toString() == "foo");
     81 
     82     KJ_EXPECT(path.append("baz").toString() == "foo/bar/baz");
     83     KJ_EXPECT(path.append(Path({"baz", "qux"})).toString() == "foo/bar/baz/qux");
     84   }
     85 
     86   KJ_EXPECT(kj::str(Path({"foo", "bar"})) == "foo/bar");
     87 }
     88 
     89 KJ_TEST("Path comparisons") {
     90   KJ_EXPECT(Path({"foo", "bar"}) == Path({"foo", "bar"}));
     91   KJ_EXPECT(!(Path({"foo", "bar"}) != Path({"foo", "bar"})));
     92   KJ_EXPECT(Path({"foo", "bar"}) != Path({"foo", "baz"}));
     93   KJ_EXPECT(!(Path({"foo", "bar"}) == Path({"foo", "baz"})));
     94 
     95   KJ_EXPECT(Path({"foo", "bar"}) != Path({"fob", "bar"}));
     96   KJ_EXPECT(Path({"foo", "bar"}) != Path({"foo", "bar", "baz"}));
     97   KJ_EXPECT(Path({"foo", "bar", "baz"}) != Path({"foo", "bar"}));
     98 
     99   KJ_EXPECT(Path({"foo", "bar"}) <= Path({"foo", "bar"}));
    100   KJ_EXPECT(Path({"foo", "bar"}) >= Path({"foo", "bar"}));
    101   KJ_EXPECT(!(Path({"foo", "bar"}) < Path({"foo", "bar"})));
    102   KJ_EXPECT(!(Path({"foo", "bar"}) > Path({"foo", "bar"})));
    103 
    104   KJ_EXPECT(Path({"foo", "bar"}) < Path({"foo", "bar", "baz"}));
    105   KJ_EXPECT(!(Path({"foo", "bar"}) > Path({"foo", "bar", "baz"})));
    106   KJ_EXPECT(Path({"foo", "bar", "baz"}) > Path({"foo", "bar"}));
    107   KJ_EXPECT(!(Path({"foo", "bar", "baz"}) < Path({"foo", "bar"})));
    108 
    109   KJ_EXPECT(Path({"foo", "bar"}) < Path({"foo", "baz"}));
    110   KJ_EXPECT(Path({"foo", "bar"}) > Path({"foo", "baa"}));
    111   KJ_EXPECT(Path({"foo", "bar"}) > Path({"foo"}));
    112 
    113   KJ_EXPECT(Path({"foo", "bar"}).startsWith(Path({})));
    114   KJ_EXPECT(Path({"foo", "bar"}).startsWith(Path({"foo"})));
    115   KJ_EXPECT(Path({"foo", "bar"}).startsWith(Path({"foo", "bar"})));
    116   KJ_EXPECT(!Path({"foo", "bar"}).startsWith(Path({"foo", "bar", "baz"})));
    117   KJ_EXPECT(!Path({"foo", "bar"}).startsWith(Path({"foo", "baz"})));
    118   KJ_EXPECT(!Path({"foo", "bar"}).startsWith(Path({"baz", "foo", "bar"})));
    119   KJ_EXPECT(!Path({"foo", "bar"}).startsWith(Path({"baz"})));
    120 
    121   KJ_EXPECT(Path({"foo", "bar"}).endsWith(Path({})));
    122   KJ_EXPECT(Path({"foo", "bar"}).endsWith(Path({"bar"})));
    123   KJ_EXPECT(Path({"foo", "bar"}).endsWith(Path({"foo", "bar"})));
    124   KJ_EXPECT(!Path({"foo", "bar"}).endsWith(Path({"baz", "foo", "bar"})));
    125   KJ_EXPECT(!Path({"foo", "bar"}).endsWith(Path({"fob", "bar"})));
    126   KJ_EXPECT(!Path({"foo", "bar"}).endsWith(Path({"foo", "bar", "baz"})));
    127   KJ_EXPECT(!Path({"foo", "bar"}).endsWith(Path({"baz"})));
    128 }
    129 
    130 KJ_TEST("Path exceptions") {
    131   KJ_EXPECT_THROW_MESSAGE("invalid path component", Path(""));
    132   KJ_EXPECT_THROW_MESSAGE("invalid path component", Path("."));
    133   KJ_EXPECT_THROW_MESSAGE("invalid path component", Path(".."));
    134   KJ_EXPECT_THROW_MESSAGE("NUL character", Path(StringPtr("foo\0bar", 7)));
    135 
    136   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting", Path::parse(".."));
    137   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting", Path::parse("../foo"));
    138   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting", Path::parse("foo/../.."));
    139   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("expected a relative path", Path::parse("/foo"));
    140 
    141   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("NUL character", Path::parse(kj::StringPtr("foo\0bar", 7)));
    142 
    143   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    144       Path({"foo", "bar"}).eval("../../.."));
    145   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    146       Path({"foo", "bar"}).eval("../baz/../../.."));
    147   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    148       Path({"foo", "bar"}).eval("baz/../../../.."));
    149   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    150       Path({"foo", "bar"}).eval("/.."));
    151   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    152       Path({"foo", "bar"}).eval("/baz/../.."));
    153 
    154   KJ_EXPECT_THROW_MESSAGE("root path has no basename", Path(nullptr).basename());
    155   KJ_EXPECT_THROW_MESSAGE("root path has no parent", Path(nullptr).parent());
    156 }
    157 
    158 constexpr kj::ArrayPtr<const wchar_t> operator "" _a(const wchar_t* str, size_t n) {
    159   return { str, n };
    160 }
    161 
    162 KJ_TEST("Win32 Path") {
    163   KJ_EXPECT(Path({"foo", "bar"}).toWin32String() == "foo\\bar");
    164   KJ_EXPECT(Path({"foo", "bar"}).toWin32String(true) == "\\\\foo\\bar");
    165   KJ_EXPECT(Path({"c:", "foo", "bar"}).toWin32String(true) == "c:\\foo\\bar");
    166   KJ_EXPECT(Path({"A:", "foo", "bar"}).toWin32String(true) == "A:\\foo\\bar");
    167 
    168   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz").toWin32String() == "foo\\bar\\baz");
    169   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("./baz").toWin32String() == "foo\\bar\\baz");
    170   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz/qux").toWin32String() == "foo\\bar\\baz\\qux");
    171   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz//qux").toWin32String() == "foo\\bar\\baz\\qux");
    172   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz/./qux").toWin32String() == "foo\\bar\\baz\\qux");
    173   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz/../qux").toWin32String() == "foo\\bar\\qux");
    174   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz/qux/..").toWin32String() == "foo\\bar\\baz");
    175   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("../baz").toWin32String() == "foo\\baz");
    176   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz/../../qux/").toWin32String() == "foo\\qux");
    177   KJ_EXPECT(Path({"foo", "bar"}).evalWin32(".\\baz").toWin32String() == "foo\\bar\\baz");
    178   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz\\qux").toWin32String() == "foo\\bar\\baz\\qux");
    179   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz\\\\qux").toWin32String() == "foo\\bar\\baz\\qux");
    180   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz\\.\\qux").toWin32String() == "foo\\bar\\baz\\qux");
    181   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz\\..\\qux").toWin32String() == "foo\\bar\\qux");
    182   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz\\qux\\..").toWin32String() == "foo\\bar\\baz");
    183   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("..\\baz").toWin32String() == "foo\\baz");
    184   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz\\..\\..\\qux\\").toWin32String() == "foo\\qux");
    185   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("baz\\../..\\qux/").toWin32String() == "foo\\qux");
    186 
    187   KJ_EXPECT(Path({"c:", "foo", "bar"}).evalWin32("/baz/qux")
    188       .toWin32String(true) == "c:\\baz\\qux");
    189   KJ_EXPECT(Path({"c:", "foo", "bar"}).evalWin32("\\baz\\qux")
    190       .toWin32String(true) == "c:\\baz\\qux");
    191   KJ_EXPECT(Path({"c:", "foo", "bar"}).evalWin32("d:\\baz\\qux")
    192       .toWin32String(true) == "d:\\baz\\qux");
    193   KJ_EXPECT(Path({"c:", "foo", "bar"}).evalWin32("d:\\baz\\..\\qux")
    194       .toWin32String(true) == "d:\\qux");
    195   KJ_EXPECT(Path({"c:", "foo", "bar"}).evalWin32("\\\\baz\\qux")
    196       .toWin32String(true) == "\\\\baz\\qux");
    197   KJ_EXPECT(Path({"foo", "bar"}).evalWin32("d:\\baz\\..\\qux")
    198       .toWin32String(true) == "d:\\qux");
    199   KJ_EXPECT(Path({"foo", "bar", "baz"}).evalWin32("\\qux")
    200       .toWin32String(true) == "\\\\foo\\bar\\qux");
    201 
    202   KJ_EXPECT(Path({"foo", "bar"}).forWin32Api(false) == L"foo\\bar");
    203   KJ_EXPECT(Path({"foo", "bar"}).forWin32Api(true) == L"\\\\?\\UNC\\foo\\bar");
    204   KJ_EXPECT(Path({"c:", "foo", "bar"}).forWin32Api(true) == L"\\\\?\\c:\\foo\\bar");
    205   KJ_EXPECT(Path({"A:", "foo", "bar"}).forWin32Api(true) == L"\\\\?\\A:\\foo\\bar");
    206 
    207   KJ_EXPECT(Path::parseWin32Api(L"\\\\?\\c:\\foo\\bar"_a).toString() == "c:/foo/bar");
    208   KJ_EXPECT(Path::parseWin32Api(L"\\\\?\\UNC\\foo\\bar"_a).toString() == "foo/bar");
    209   KJ_EXPECT(Path::parseWin32Api(L"c:\\foo\\bar"_a).toString() == "c:/foo/bar");
    210   KJ_EXPECT(Path::parseWin32Api(L"\\\\foo\\bar"_a).toString() == "foo/bar");
    211 }
    212 
    213 KJ_TEST("Win32 Path exceptions") {
    214   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("colons are prohibited",
    215       Path({"c:", "foo", "bar"}).toWin32String());
    216   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("colons are prohibited",
    217       Path({"c:", "foo:bar"}).toWin32String(true));
    218   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"con"}).toWin32String());
    219   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"CON", "bar"}).toWin32String());
    220   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"foo", "cOn"}).toWin32String());
    221   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"prn"}).toWin32String());
    222   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"aux"}).toWin32String());
    223   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"NUL"}).toWin32String());
    224   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"nul.txt"}).toWin32String());
    225   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"com3"}).toWin32String());
    226   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"lpt9"}).toWin32String());
    227   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("DOS reserved name", Path({"com1.hello"}).toWin32String());
    228 
    229   KJ_EXPECT_THROW_MESSAGE("drive letter or netbios", Path({"?", "foo"}).toWin32String(true));
    230 
    231   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    232       Path({"foo", "bar"}).evalWin32("../../.."));
    233   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    234       Path({"foo", "bar"}).evalWin32("../baz/../../.."));
    235   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    236       Path({"foo", "bar"}).evalWin32("baz/../../../.."));
    237   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    238       Path({"foo", "bar"}).evalWin32("c:\\..\\.."));
    239   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("break out of starting",
    240       Path({"c:", "foo", "bar"}).evalWin32("/baz/../../.."));
    241   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("must specify drive letter",
    242       Path({"foo"}).evalWin32("\\baz\\qux"));
    243 }
    244 
    245 KJ_TEST("WriteMode operators") {
    246   WriteMode createOrModify = WriteMode::CREATE | WriteMode::MODIFY;
    247 
    248   KJ_EXPECT(has(createOrModify, WriteMode::MODIFY));
    249   KJ_EXPECT(has(createOrModify, WriteMode::CREATE));
    250   KJ_EXPECT(!has(createOrModify, WriteMode::CREATE_PARENT));
    251   KJ_EXPECT(has(createOrModify, createOrModify));
    252   KJ_EXPECT(!has(createOrModify, createOrModify | WriteMode::CREATE_PARENT));
    253   KJ_EXPECT(!has(createOrModify, WriteMode::CREATE | WriteMode::CREATE_PARENT));
    254   KJ_EXPECT(!has(WriteMode::CREATE, createOrModify));
    255 
    256   KJ_EXPECT(createOrModify != WriteMode::MODIFY);
    257   KJ_EXPECT(createOrModify != WriteMode::CREATE);
    258 
    259   KJ_EXPECT(createOrModify - WriteMode::CREATE == WriteMode::MODIFY);
    260   KJ_EXPECT(WriteMode::CREATE + WriteMode::MODIFY == createOrModify);
    261 
    262   // Adding existing bit / subtracting non-existing bit are no-ops.
    263   KJ_EXPECT(createOrModify + WriteMode::MODIFY == createOrModify);
    264   KJ_EXPECT(createOrModify - WriteMode::CREATE_PARENT == createOrModify);
    265 }
    266 
    267 // ======================================================================================
    268 
    269 class TestClock final: public Clock {
    270 public:
    271   void tick() {
    272     time += 1 * SECONDS;
    273   }
    274 
    275   Date now() const override { return time; }
    276 
    277   void expectChanged(const FsNode& file) {
    278     KJ_EXPECT(file.stat().lastModified == time);
    279     time += 1 * SECONDS;
    280   }
    281   void expectUnchanged(const FsNode& file) {
    282     KJ_EXPECT(file.stat().lastModified != time);
    283   }
    284 
    285 private:
    286   Date time = UNIX_EPOCH + 1 * SECONDS;
    287 };
    288 
    289 KJ_TEST("InMemoryFile") {
    290   TestClock clock;
    291 
    292   auto file = newInMemoryFile(clock);
    293   clock.expectChanged(*file);
    294 
    295   KJ_EXPECT(file->readAllText() == "");
    296   clock.expectUnchanged(*file);
    297 
    298   file->writeAll("foo");
    299   clock.expectChanged(*file);
    300   KJ_EXPECT(file->readAllText() == "foo");
    301 
    302   file->write(3, StringPtr("bar").asBytes());
    303   clock.expectChanged(*file);
    304   KJ_EXPECT(file->readAllText() == "foobar");
    305 
    306   file->write(3, StringPtr("baz").asBytes());
    307   clock.expectChanged(*file);
    308   KJ_EXPECT(file->readAllText() == "foobaz");
    309 
    310   file->write(9, StringPtr("qux").asBytes());
    311   clock.expectChanged(*file);
    312   KJ_EXPECT(file->readAllText() == kj::StringPtr("foobaz\0\0\0qux", 12));
    313 
    314   file->truncate(6);
    315   clock.expectChanged(*file);
    316   KJ_EXPECT(file->readAllText() == "foobaz");
    317 
    318   file->truncate(18);
    319   clock.expectChanged(*file);
    320   KJ_EXPECT(file->readAllText() == kj::StringPtr("foobaz\0\0\0\0\0\0\0\0\0\0\0\0", 18));
    321 
    322   {
    323     auto mapping = file->mmap(0, 18);
    324     auto privateMapping = file->mmapPrivate(0, 18);
    325     auto writableMapping = file->mmapWritable(0, 18);
    326     clock.expectUnchanged(*file);
    327 
    328     KJ_EXPECT(mapping.size() == 18);
    329     KJ_EXPECT(privateMapping.size() == 18);
    330     KJ_EXPECT(writableMapping->get().size() == 18);
    331     clock.expectUnchanged(*file);
    332 
    333     KJ_EXPECT(writableMapping->get().begin() == mapping.begin());
    334     KJ_EXPECT(privateMapping.begin() != mapping.begin());
    335 
    336     KJ_EXPECT(kj::str(mapping.slice(0, 6).asChars()) == "foobaz");
    337     KJ_EXPECT(kj::str(privateMapping.slice(0, 6).asChars()) == "foobaz");
    338     clock.expectUnchanged(*file);
    339 
    340     file->write(0, StringPtr("qux").asBytes());
    341     clock.expectChanged(*file);
    342     KJ_EXPECT(kj::str(mapping.slice(0, 6).asChars()) == "quxbaz");
    343     KJ_EXPECT(kj::str(privateMapping.slice(0, 6).asChars()) == "foobaz");
    344 
    345     file->write(12, StringPtr("corge").asBytes());
    346     KJ_EXPECT(kj::str(mapping.slice(12, 17).asChars()) == "corge");
    347 
    348     // Can shrink.
    349     file->truncate(6);
    350     KJ_EXPECT(kj::str(mapping.slice(12, 17).asChars()) == kj::StringPtr("\0\0\0\0\0", 5));
    351 
    352     // Can regrow.
    353     file->truncate(18);
    354     KJ_EXPECT(kj::str(mapping.slice(12, 17).asChars()) == kj::StringPtr("\0\0\0\0\0", 5));
    355 
    356     // Can't grow past previoous capacity.
    357     KJ_EXPECT_THROW_MESSAGE("cannot resize the file backing store", file->truncate(100));
    358 
    359     clock.expectChanged(*file);
    360     writableMapping->changed(writableMapping->get().slice(0, 3));
    361     clock.expectChanged(*file);
    362     writableMapping->sync(writableMapping->get().slice(0, 3));
    363     clock.expectChanged(*file);
    364   }
    365 
    366   // But now we can since the mapping is gone.
    367   file->truncate(100);
    368 
    369   file->truncate(6);
    370   clock.expectChanged(*file);
    371 
    372   KJ_EXPECT(file->readAllText() == "quxbaz");
    373   file->zero(3, 3);
    374   clock.expectChanged(*file);
    375   KJ_EXPECT(file->readAllText() == StringPtr("qux\0\0\0", 6));
    376 }
    377 
    378 KJ_TEST("InMemoryFile::copy()") {
    379   TestClock clock;
    380 
    381   auto source = newInMemoryFile(clock);
    382   source->writeAll("foobarbaz");
    383 
    384   auto dest = newInMemoryFile(clock);
    385   dest->writeAll("quxcorge");
    386   clock.expectChanged(*dest);
    387 
    388   KJ_EXPECT(dest->copy(3, *source, 6, kj::maxValue) == 3);
    389   clock.expectChanged(*dest);
    390   KJ_EXPECT(dest->readAllText() == "quxbazge");
    391 
    392   KJ_EXPECT(dest->copy(0, *source, 3, 4) == 4);
    393   clock.expectChanged(*dest);
    394   KJ_EXPECT(dest->readAllText() == "barbazge");
    395 
    396   KJ_EXPECT(dest->copy(0, *source, 128, kj::maxValue) == 0);
    397   clock.expectUnchanged(*dest);
    398 
    399   KJ_EXPECT(dest->copy(4, *source, 3, 0) == 0);
    400   clock.expectUnchanged(*dest);
    401 
    402   String bigString = strArray(repeat("foobar", 10000), "");
    403   source->truncate(bigString.size() + 1000);
    404   source->write(123, bigString.asBytes());
    405 
    406   dest->copy(321, *source, 123, bigString.size());
    407   KJ_EXPECT(dest->readAllText().slice(321) == bigString);
    408 }
    409 
    410 KJ_TEST("File::copy()") {
    411   TestClock clock;
    412 
    413   auto source = newInMemoryFile(clock);
    414   source->writeAll("foobarbaz");
    415 
    416   auto dest = newInMemoryFile(clock);
    417   dest->writeAll("quxcorge");
    418   clock.expectChanged(*dest);
    419 
    420   KJ_EXPECT(dest->File::copy(3, *source, 6, kj::maxValue) == 3);
    421   clock.expectChanged(*dest);
    422   KJ_EXPECT(dest->readAllText() == "quxbazge");
    423 
    424   KJ_EXPECT(dest->File::copy(0, *source, 3, 4) == 4);
    425   clock.expectChanged(*dest);
    426   KJ_EXPECT(dest->readAllText() == "barbazge");
    427 
    428   KJ_EXPECT(dest->File::copy(0, *source, 128, kj::maxValue) == 0);
    429   clock.expectUnchanged(*dest);
    430 
    431   KJ_EXPECT(dest->File::copy(4, *source, 3, 0) == 0);
    432   clock.expectUnchanged(*dest);
    433 
    434   String bigString = strArray(repeat("foobar", 10000), "");
    435   source->truncate(bigString.size() + 1000);
    436   source->write(123, bigString.asBytes());
    437 
    438   dest->File::copy(321, *source, 123, bigString.size());
    439   KJ_EXPECT(dest->readAllText().slice(321) == bigString);
    440 }
    441 
    442 KJ_TEST("InMemoryDirectory") {
    443   TestClock clock;
    444 
    445   auto dir = newInMemoryDirectory(clock);
    446   clock.expectChanged(*dir);
    447 
    448   KJ_EXPECT(dir->listNames() == nullptr);
    449   KJ_EXPECT(dir->listEntries() == nullptr);
    450   KJ_EXPECT(!dir->exists(Path("foo")));
    451   KJ_EXPECT(dir->tryOpenFile(Path("foo")) == nullptr);
    452   KJ_EXPECT(dir->tryOpenFile(Path("foo"), WriteMode::MODIFY) == nullptr);
    453   clock.expectUnchanged(*dir);
    454 
    455   {
    456     auto file = dir->openFile(Path("foo"), WriteMode::CREATE);
    457     clock.expectChanged(*dir);
    458     file->writeAll("foobar");
    459     clock.expectUnchanged(*dir);
    460   }
    461   clock.expectUnchanged(*dir);
    462 
    463   KJ_EXPECT(dir->exists(Path("foo")));
    464   clock.expectUnchanged(*dir);
    465 
    466   {
    467     auto stats = dir->lstat(Path("foo"));
    468     clock.expectUnchanged(*dir);
    469     KJ_EXPECT(stats.type == FsNode::Type::FILE);
    470     KJ_EXPECT(stats.size == 6);
    471   }
    472 
    473   {
    474     auto list = dir->listNames();
    475     clock.expectUnchanged(*dir);
    476     KJ_ASSERT(list.size() == 1);
    477     KJ_EXPECT(list[0] == "foo");
    478   }
    479 
    480   {
    481     auto list = dir->listEntries();
    482     clock.expectUnchanged(*dir);
    483     KJ_ASSERT(list.size() == 1);
    484     KJ_EXPECT(list[0].name == "foo");
    485     KJ_EXPECT(list[0].type == FsNode::Type::FILE);
    486   }
    487 
    488   KJ_EXPECT(dir->openFile(Path("foo"))->readAllText() == "foobar");
    489   clock.expectUnchanged(*dir);
    490 
    491   KJ_EXPECT(dir->tryOpenFile(Path({"foo", "bar"}), WriteMode::MODIFY) == nullptr);
    492   KJ_EXPECT(dir->tryOpenFile(Path({"bar", "baz"}), WriteMode::MODIFY) == nullptr);
    493   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("parent is not a directory",
    494       dir->tryOpenFile(Path({"bar", "baz"}), WriteMode::CREATE));
    495   clock.expectUnchanged(*dir);
    496 
    497   {
    498     auto file = dir->openFile(Path({"bar", "baz"}), WriteMode::CREATE | WriteMode::CREATE_PARENT);
    499     clock.expectChanged(*dir);
    500     file->writeAll("bazqux");
    501     clock.expectUnchanged(*dir);
    502   }
    503   clock.expectUnchanged(*dir);
    504 
    505   KJ_EXPECT(dir->openFile(Path({"bar", "baz"}))->readAllText() == "bazqux");
    506   clock.expectUnchanged(*dir);
    507 
    508   {
    509     auto stats = dir->lstat(Path("bar"));
    510     clock.expectUnchanged(*dir);
    511     KJ_EXPECT(stats.type == FsNode::Type::DIRECTORY);
    512   }
    513 
    514   {
    515     auto list = dir->listNames();
    516     clock.expectUnchanged(*dir);
    517     KJ_ASSERT(list.size() == 2);
    518     KJ_EXPECT(list[0] == "bar");
    519     KJ_EXPECT(list[1] == "foo");
    520   }
    521 
    522   {
    523     auto list = dir->listEntries();
    524     clock.expectUnchanged(*dir);
    525     KJ_ASSERT(list.size() == 2);
    526     KJ_EXPECT(list[0].name == "bar");
    527     KJ_EXPECT(list[0].type == FsNode::Type::DIRECTORY);
    528     KJ_EXPECT(list[1].name == "foo");
    529     KJ_EXPECT(list[1].type == FsNode::Type::FILE);
    530   }
    531 
    532   {
    533     auto subdir = dir->openSubdir(Path("bar"));
    534     clock.expectUnchanged(*dir);
    535     clock.expectUnchanged(*subdir);
    536 
    537     KJ_EXPECT(subdir->openFile(Path("baz"))->readAllText() == "bazqux");
    538     clock.expectUnchanged(*subdir);
    539   }
    540 
    541   auto subdir = dir->openSubdir(Path("corge"), WriteMode::CREATE);
    542   clock.expectChanged(*dir);
    543 
    544   subdir->openFile(Path("grault"), WriteMode::CREATE)->writeAll("garply");
    545   clock.expectUnchanged(*dir);
    546   clock.expectChanged(*subdir);
    547 
    548   KJ_EXPECT(dir->openFile(Path({"corge", "grault"}))->readAllText() == "garply");
    549 
    550   dir->openFile(Path({"corge", "grault"}), WriteMode::CREATE | WriteMode::MODIFY)
    551      ->write(0, StringPtr("rag").asBytes());
    552   KJ_EXPECT(dir->openFile(Path({"corge", "grault"}))->readAllText() == "ragply");
    553   clock.expectUnchanged(*dir);
    554 
    555   {
    556     auto replacer =
    557         dir->replaceFile(Path({"corge", "grault"}), WriteMode::CREATE | WriteMode::MODIFY);
    558     clock.expectUnchanged(*subdir);
    559     replacer->get().writeAll("rag");
    560     clock.expectUnchanged(*subdir);
    561     // Don't commit.
    562   }
    563   clock.expectUnchanged(*subdir);
    564   KJ_EXPECT(dir->openFile(Path({"corge", "grault"}))->readAllText() == "ragply");
    565 
    566   {
    567     auto replacer =
    568         dir->replaceFile(Path({"corge", "grault"}), WriteMode::CREATE | WriteMode::MODIFY);
    569     clock.expectUnchanged(*subdir);
    570     replacer->get().writeAll("rag");
    571     clock.expectUnchanged(*subdir);
    572     replacer->commit();
    573     clock.expectChanged(*subdir);
    574     KJ_EXPECT(dir->openFile(Path({"corge", "grault"}))->readAllText() == "rag");
    575   }
    576 
    577   KJ_EXPECT(dir->openFile(Path({"corge", "grault"}))->readAllText() == "rag");
    578 
    579   {
    580     auto appender = dir->appendFile(Path({"corge", "grault"}), WriteMode::MODIFY);
    581     appender->write("waldo", 5);
    582     appender->write("fred", 4);
    583   }
    584 
    585   KJ_EXPECT(dir->openFile(Path({"corge", "grault"}))->readAllText() == "ragwaldofred");
    586 
    587   KJ_EXPECT(dir->exists(Path("foo")));
    588   clock.expectUnchanged(*dir);
    589   dir->remove(Path("foo"));
    590   clock.expectChanged(*dir);
    591   KJ_EXPECT(!dir->exists(Path("foo")));
    592   KJ_EXPECT(!dir->tryRemove(Path("foo")));
    593   clock.expectUnchanged(*dir);
    594 
    595   KJ_EXPECT(dir->exists(Path({"bar", "baz"})));
    596   clock.expectUnchanged(*dir);
    597   dir->remove(Path({"bar", "baz"}));
    598   clock.expectUnchanged(*dir);
    599   KJ_EXPECT(!dir->exists(Path({"bar", "baz"})));
    600   KJ_EXPECT(dir->exists(Path("bar")));
    601   KJ_EXPECT(!dir->tryRemove(Path({"bar", "baz"})));
    602   clock.expectUnchanged(*dir);
    603 
    604   KJ_EXPECT(dir->exists(Path("corge")));
    605   KJ_EXPECT(dir->exists(Path({"corge", "grault"})));
    606   clock.expectUnchanged(*dir);
    607   dir->remove(Path("corge"));
    608   clock.expectChanged(*dir);
    609   KJ_EXPECT(!dir->exists(Path("corge")));
    610   KJ_EXPECT(!dir->exists(Path({"corge", "grault"})));
    611   KJ_EXPECT(!dir->tryRemove(Path("corge")));
    612   clock.expectUnchanged(*dir);
    613 }
    614 
    615 KJ_TEST("InMemoryDirectory symlinks") {
    616   TestClock clock;
    617 
    618   auto dir = newInMemoryDirectory(clock);
    619   clock.expectChanged(*dir);
    620 
    621   dir->symlink(Path("foo"), "bar/qux/../baz", WriteMode::CREATE);
    622   clock.expectChanged(*dir);
    623 
    624   KJ_EXPECT(!dir->trySymlink(Path("foo"), "bar/qux/../baz", WriteMode::CREATE));
    625   clock.expectUnchanged(*dir);
    626 
    627   {
    628     auto stats = dir->lstat(Path("foo"));
    629     clock.expectUnchanged(*dir);
    630     KJ_EXPECT(stats.type == FsNode::Type::SYMLINK);
    631   }
    632 
    633   KJ_EXPECT(dir->readlink(Path("foo")) == "bar/qux/../baz");
    634 
    635   // Broken link into non-existing directory cannot be opened in any mode.
    636   KJ_EXPECT(dir->tryOpenFile(Path("foo")) == nullptr);
    637   KJ_EXPECT(dir->tryOpenFile(Path("foo"), WriteMode::CREATE) == nullptr);
    638   KJ_EXPECT(dir->tryOpenFile(Path("foo"), WriteMode::MODIFY) == nullptr);
    639   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("parent is not a directory",
    640       dir->tryOpenFile(Path("foo"), WriteMode::CREATE | WriteMode::MODIFY));
    641   KJ_EXPECT_THROW_RECOVERABLE_MESSAGE("parent is not a directory",
    642       dir->tryOpenFile(Path("foo"),
    643           WriteMode::CREATE | WriteMode::MODIFY | WriteMode::CREATE_PARENT));
    644 
    645   // Create the directory.
    646   auto subdir = dir->openSubdir(Path("bar"), WriteMode::CREATE);
    647   clock.expectChanged(*dir);
    648 
    649   // Link still points to non-existing file so cannot be open in most modes.
    650   KJ_EXPECT(dir->tryOpenFile(Path("foo")) == nullptr);
    651   KJ_EXPECT(dir->tryOpenFile(Path("foo"), WriteMode::CREATE) == nullptr);
    652   KJ_EXPECT(dir->tryOpenFile(Path("foo"), WriteMode::MODIFY) == nullptr);
    653   clock.expectUnchanged(*dir);
    654 
    655   // But... CREATE | MODIFY works.
    656   dir->openFile(Path("foo"), WriteMode::CREATE | WriteMode::MODIFY)
    657      ->writeAll("foobar");
    658   clock.expectUnchanged(*dir);  // Change is only to subdir!
    659 
    660   KJ_EXPECT(dir->openFile(Path({"bar", "baz"}))->readAllText() == "foobar");
    661   KJ_EXPECT(dir->openFile(Path("foo"))->readAllText() == "foobar");
    662   KJ_EXPECT(dir->openFile(Path("foo"), WriteMode::MODIFY)->readAllText() == "foobar");
    663 
    664   // operations that modify the symlink
    665   dir->symlink(Path("foo"), "corge", WriteMode::MODIFY);
    666   KJ_EXPECT(dir->openFile(Path({"bar", "baz"}))->readAllText() == "foobar");
    667   KJ_EXPECT(dir->readlink(Path("foo")) == "corge");
    668   KJ_EXPECT(!dir->exists(Path("foo")));
    669   KJ_EXPECT(dir->lstat(Path("foo")).type == FsNode::Type::SYMLINK);
    670   KJ_EXPECT(dir->tryOpenFile(Path("foo")) == nullptr);
    671 
    672   dir->remove(Path("foo"));
    673   KJ_EXPECT(!dir->exists(Path("foo")));
    674   KJ_EXPECT(dir->tryOpenFile(Path("foo")) == nullptr);
    675 }
    676 
    677 KJ_TEST("InMemoryDirectory link") {
    678   TestClock clock;
    679 
    680   auto src = newInMemoryDirectory(clock);
    681   auto dst = newInMemoryDirectory(clock);
    682 
    683   src->openFile(Path({"foo", "bar"}), WriteMode::CREATE | WriteMode::CREATE_PARENT)
    684      ->writeAll("foobar");
    685   src->openFile(Path({"foo", "baz", "qux"}), WriteMode::CREATE | WriteMode::CREATE_PARENT)
    686      ->writeAll("bazqux");
    687   clock.expectChanged(*src);
    688   clock.expectUnchanged(*dst);
    689 
    690   dst->transfer(Path("link"), WriteMode::CREATE, *src, Path("foo"), TransferMode::LINK);
    691   clock.expectUnchanged(*src);
    692   clock.expectChanged(*dst);
    693 
    694   KJ_EXPECT(dst->openFile(Path({"link", "bar"}))->readAllText() == "foobar");
    695   KJ_EXPECT(dst->openFile(Path({"link", "baz", "qux"}))->readAllText() == "bazqux");
    696 
    697   KJ_EXPECT(dst->exists(Path({"link", "bar"})));
    698   src->remove(Path({"foo", "bar"}));
    699   KJ_EXPECT(!dst->exists(Path({"link", "bar"})));
    700 }
    701 
    702 KJ_TEST("InMemoryDirectory copy") {
    703   TestClock clock;
    704 
    705   auto src = newInMemoryDirectory(clock);
    706   auto dst = newInMemoryDirectory(clock);
    707 
    708   src->openFile(Path({"foo", "bar"}), WriteMode::CREATE | WriteMode::CREATE_PARENT)
    709      ->writeAll("foobar");
    710   src->openFile(Path({"foo", "baz", "qux"}), WriteMode::CREATE | WriteMode::CREATE_PARENT)
    711      ->writeAll("bazqux");
    712   clock.expectChanged(*src);
    713   clock.expectUnchanged(*dst);
    714 
    715   dst->transfer(Path("link"), WriteMode::CREATE, *src, Path("foo"), TransferMode::COPY);
    716   clock.expectUnchanged(*src);
    717   clock.expectChanged(*dst);
    718 
    719   KJ_EXPECT(src->openFile(Path({"foo", "bar"}))->readAllText() == "foobar");
    720   KJ_EXPECT(src->openFile(Path({"foo", "baz", "qux"}))->readAllText() == "bazqux");
    721   KJ_EXPECT(dst->openFile(Path({"link", "bar"}))->readAllText() == "foobar");
    722   KJ_EXPECT(dst->openFile(Path({"link", "baz", "qux"}))->readAllText() == "bazqux");
    723 
    724   KJ_EXPECT(dst->exists(Path({"link", "bar"})));
    725   src->remove(Path({"foo", "bar"}));
    726   KJ_EXPECT(dst->openFile(Path({"link", "bar"}))->readAllText() == "foobar");
    727 }
    728 
    729 KJ_TEST("InMemoryDirectory move") {
    730   TestClock clock;
    731 
    732   auto src = newInMemoryDirectory(clock);
    733   auto dst = newInMemoryDirectory(clock);
    734 
    735   src->openFile(Path({"foo", "bar"}), WriteMode::CREATE | WriteMode::CREATE_PARENT)
    736      ->writeAll("foobar");
    737   src->openFile(Path({"foo", "baz", "qux"}), WriteMode::CREATE | WriteMode::CREATE_PARENT)
    738      ->writeAll("bazqux");
    739   clock.expectChanged(*src);
    740   clock.expectUnchanged(*dst);
    741 
    742   dst->transfer(Path("link"), WriteMode::CREATE, *src, Path("foo"), TransferMode::MOVE);
    743   clock.expectChanged(*src);
    744 
    745   KJ_EXPECT(!src->exists(Path({"foo"})));
    746   KJ_EXPECT(dst->openFile(Path({"link", "bar"}))->readAllText() == "foobar");
    747   KJ_EXPECT(dst->openFile(Path({"link", "baz", "qux"}))->readAllText() == "bazqux");
    748 }
    749 
    750 KJ_TEST("InMemoryDirectory createTemporary") {
    751   TestClock clock;
    752 
    753   auto dir = newInMemoryDirectory(clock);
    754   auto file = dir->createTemporary();
    755   file->writeAll("foobar");
    756   KJ_EXPECT(file->readAllText() == "foobar");
    757   KJ_EXPECT(dir->listNames() == nullptr);
    758 }
    759 
    760 }  // namespace
    761 }  // namespace kj