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