yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
7b570feed
master
1// unit-test-file-system.cpp 2 3#include "../../source/core/slang-castable.h" 4#include "../../source/core/slang-deflate-compression-system.h" 5#include "../../source/core/slang-file-system.h" 6#include "../../source/core/slang-io.h" 7#include "../../source/core/slang-lz4-compression-system.h" 8#include "../../source/core/slang-memory-file-system.h" 9#include "../../source/core/slang-riff-file-system.h" 10#include "../../source/core/slang-zip-file-system.h" 11#include "unit-test/slang-unit-test.h" 12 13using namespace Slang ; 14 15namespace 16{// anonymous 17 18enum class FileSystemType 19{ 20Zip , 21RiffUncompressed , 22RiffDeflate , 23RiffLZ4 , 24Memory , 25Relative , 26CountOf , 27}; 28 29struct Entry 30{ 31typedef Entry ThisType ; 32 33bool operator< (const ThisType & rhs )const {return path < rhs .path ; } 34bool operator== (const ThisType & rhs )const {return path == rhs .path && type == rhs .type ; } 35bool operator!= (const ThisType & rhs )const {return !(* this == rhs ); } 36 37SlangPathType type ; 38String path ; 39}; 40 41}// namespace 42 43static SlangResult _checkFile ( 44ISlangFileSystemExt * fileSystem , 45const char * path , 46const UnownedStringSlice & contentsSlice ) 47{ 48SlangPathType pathType ; 49SLANG_RETURN_ON_FAIL (fileSystem -> getPathType (path ,& pathType )); 50 51if (pathType != SLANG_PATH_TYPE_FILE ) 52 { 53return SLANG_FAIL ; 54 } 55 56ComPtr < ISlangBlob > blob ; 57SLANG_RETURN_ON_FAIL (fileSystem -> loadFile (path ,blob .writeRef ())); 58 59if (blob -> getBufferSize ()!= contentsSlice .getLength ()) 60 { 61return SLANG_FAIL ; 62 } 63if (contentsSlice != 64UnownedStringSlice ((const char * )blob -> getBufferPointer (),blob -> getBufferSize ())) 65 { 66return SLANG_FAIL ; 67 } 68return SLANG_OK ; 69} 70 71static SlangResult _checkFile ( 72ISlangMutableFileSystem * fileSystem , 73const char * path , 74const char * contents ) 75{ 76return _checkFile (fileSystem ,path ,UnownedStringSlice (contents )); 77} 78 79static SlangResult _checkDirectoryExists (ISlangFileSystemExt * fileSystem ,const char * path ) 80{ 81SlangPathType pathType ; 82SLANG_RETURN_ON_FAIL (fileSystem -> getPathType (path ,& pathType )); 83 84if (pathType != SLANG_PATH_TYPE_DIRECTORY ) 85 { 86return SLANG_FAIL ; 87 } 88return SLANG_OK ; 89} 90 91static SlangResult _createAndCheckFile ( 92ISlangMutableFileSystem * fileSystem , 93const char * path , 94const char * contents ) 95{ 96UnownedStringSlice contentsSlice (contents ); 97 98SLANG_RETURN_ON_FAIL ( 99fileSystem -> saveFile (path ,contentsSlice .begin (),contentsSlice .getLength ())); 100SLANG_RETURN_ON_FAIL (_checkFile (fileSystem ,path ,contentsSlice )); 101 102// Delete it 103SLANG_RETURN_ON_FAIL (fileSystem -> remove (path )); 104 105// Check it's gone 106SlangPathType pathType ; 107if (SLANG_SUCCEEDED (fileSystem -> getPathType (path ,& pathType ))) 108 { 109return SLANG_FAIL ; 110 } 111 112// Save as a blob 113ComPtr < ISlangBlob > blob = RawBlob ::create (contentsSlice .begin (),contentsSlice .getLength ()); 114 115SLANG_RETURN_ON_FAIL (fileSystem -> saveFileBlob (path ,blob )); 116SLANG_RETURN_ON_FAIL (_checkFile (fileSystem ,path ,contentsSlice )); 117 118return SLANG_OK ; 119} 120 121static bool _areEqual (ISlangBlob * a ,ISlangBlob * b ) 122{ 123if (a == b ) 124 { 125return true; 126 } 127if ((!a || !b )|| (a -> getBufferSize ()!= b -> getBufferSize ())) 128 { 129return false; 130 } 131 132return ::memcmp (a -> getBufferPointer (),b -> getBufferPointer (),a -> getBufferSize ())== 0 ; 133} 134 135static SlangResult _checkCanonical ( 136ISlangMutableFileSystem * fileSystem , 137const char * const * paths , 138Count count ) 139{ 140if (count <=0 ) 141 { 142return SLANG_FAIL ; 143 } 144 145// The path has to exist to something for canonicalization to be relied upon 146SlangPathType pathType ; 147SLANG_RETURN_ON_FAIL (fileSystem -> getPathType (paths [0 ],& pathType )); 148 149String canonicalPath ; 150 { 151ComPtr < ISlangBlob > blob ; 152SLANG_RETURN_ON_FAIL (fileSystem -> getPath (PathKind ::Canonical ,paths [0 ],blob .writeRef ())); 153canonicalPath = StringUtil ::getString (blob ); 154 } 155 156// The canonicalized path must point to the same thing 157SlangPathType canonicalPathType ; 158SLANG_RETURN_ON_FAIL (fileSystem -> getPathType (canonicalPath .getBuffer (),& canonicalPathType )); 159 160if (canonicalPathType != pathType ) 161 { 162return SLANG_FAIL ; 163 } 164 165// If they are the file, being hte same file, they must hold the same data... 166if (pathType == SLANG_PATH_TYPE_FILE ) 167 { 168ComPtr < ISlangBlob > blob ; 169ComPtr < ISlangBlob > canonicalPathBlob ; 170SLANG_RETURN_ON_FAIL (fileSystem -> loadFile (paths [0 ],blob .writeRef ())); 171SLANG_RETURN_ON_FAIL ( 172fileSystem -> loadFile (canonicalPath .getBuffer (),canonicalPathBlob .writeRef ())); 173 174if (!_areEqual (blob ,canonicalPathBlob )) 175 { 176return SLANG_FAIL ; 177 } 178 } 179 180for (Index i = 1 ;i < count ;++ i ) 181 { 182ComPtr < ISlangBlob > blob ; 183SLANG_RETURN_ON_FAIL (fileSystem -> getPath (PathKind ::Canonical ,paths [i ],blob .writeRef ())); 184const auto checkPath = StringUtil ::getString (blob ); 185 186if (checkPath != canonicalPath ) 187 { 188return SLANG_FAIL ; 189 } 190 } 191 192return SLANG_OK ; 193} 194 195 196static SlangResult _createAndCheckDirectory (ISlangMutableFileSystem * fileSystem ,const char * path ) 197{ 198SLANG_RETURN_ON_FAIL (fileSystem -> createDirectory (path )); 199 200SlangPathType pathType ; 201SLANG_RETURN_ON_FAIL (fileSystem -> getPathType (path ,& pathType )); 202 203if (pathType != SLANG_PATH_TYPE_DIRECTORY ) 204 { 205return SLANG_FAIL ; 206 } 207 208return SLANG_OK ; 209} 210 211static void _entryCallback (SlangPathType pathType ,const char * name ,void * userData ) 212{ 213List < Entry >& out = * (List < Entry >* )userData ; 214out .add (Entry {pathType ,name }); 215} 216 217static SlangResult _enumeratePath ( 218ISlangFileSystemExt * fileSystem , 219const char * path , 220const ConstArrayView < Entry >& entries ) 221{ 222List < Entry > contents ; 223 224SLANG_RETURN_ON_FAIL (fileSystem -> enumeratePathContents (path ,_entryCallback , (void * )& contents )); 225 226contents .sort (); 227 228if (contents .getArrayView ()!= entries ) 229 { 230return SLANG_FAIL ; 231 } 232 233return SLANG_OK ; 234} 235 236static SlangResult _checkSimplifiedPath ( 237ISlangFileSystemExt * fileSystem , 238const char * path , 239const char * normalPath ) 240{ 241ComPtr < ISlangBlob > simplifiedPathBlob ; 242SLANG_RETURN_ON_FAIL ( 243fileSystem -> getPath (PathKind ::Simplified ,path ,simplifiedPathBlob .writeRef ())); 244 245auto simplifiedPath = StringUtil ::getString (simplifiedPathBlob ); 246 247if (simplifiedPath != normalPath ) 248 { 249return SLANG_FAIL ; 250 } 251 252return SLANG_OK ; 253} 254 255SlangResult _appendPathEntries ( 256ISlangFileSystemExt * fileSystem , 257const char * inBasePath , 258List < Entry >& outEntries ) 259{ 260const UnownedStringSlice basePath (inBasePath ); 261if (basePath == toSlice ("." )|| basePath .getLength ()== 0 ) 262 { 263// We don't need to append path prefixes if we are at the root. 264SLANG_RETURN_ON_FAIL ( 265fileSystem -> enumeratePathContents (inBasePath ,_entryCallback , (void * )& outEntries )); 266 } 267else 268 { 269const Index startIndex = outEntries .getCount (); 270SLANG_RETURN_ON_FAIL ( 271fileSystem -> enumeratePathContents (inBasePath ,_entryCallback , (void * )& outEntries )); 272 273const String basePathString (basePath ); 274 275// we need to fix all of the added paths to make absolute 276const Count count = outEntries .getCount (); 277for (Index i = startIndex ;i < count ;++ i ) 278 { 279auto & entry = outEntries [i ]; 280entry .path = Path ::combine (basePathString ,entry .path ); 281 } 282 } 283 284return SLANG_OK ; 285} 286 287static SlangResult _getAllEntries ( 288ISlangFileSystemExt * fileSystem , 289const char * inBasePath , 290List < Entry >& outEntries ) 291{ 292outEntries .clear (); 293 294// Simplify the base 295auto basePath = Path ::simplify (inBasePath ); 296 297_appendPathEntries (fileSystem ,basePath .getBuffer (),outEntries ); 298 299for (Index i = 0 ;i < outEntries .getCount ();++ i ) 300 { 301// We need to make a copy as outEntries is mutated 302const Entry entry = outEntries [i ]; 303if (entry .type == SLANG_PATH_TYPE_DIRECTORY ) 304 { 305_appendPathEntries (fileSystem ,entry .path .getBuffer (),outEntries ); 306 } 307 } 308 309// Sort to remove issues with traversal ordering 310outEntries .sort (); 311return SLANG_OK ; 312} 313 314static SlangResult _checkEqual (ISlangFileSystemExt * a ,ISlangFileSystemExt * b ) 315{ 316List < Entry > aEntries ,bEntries ; 317 318SLANG_RETURN_ON_FAIL (_getAllEntries (a ,"." ,aEntries )); 319SLANG_RETURN_ON_FAIL (_getAllEntries (b ,"." ,bEntries )); 320 321if (aEntries != bEntries ) 322 { 323return SLANG_FAIL ; 324 } 325 326// For all the files check the contents is the same 327 328for (const auto & entry :aEntries ) 329 { 330if (entry .type != SLANG_PATH_TYPE_FILE ) 331 { 332continue ; 333 } 334 335ComPtr < ISlangBlob > blobA ,blobB ; 336 337SLANG_RETURN_ON_FAIL (a -> loadFile (entry .path .getBuffer (),blobA .writeRef ())); 338SLANG_RETURN_ON_FAIL (b -> loadFile (entry .path .getBuffer (),blobB .writeRef ())); 339 340if (blobA -> getBufferSize ()!= blobB -> getBufferSize ()) 341 { 342return SLANG_FAIL ; 343 } 344 345if (::memcmp ( 346blobA -> getBufferPointer (), 347blobB -> getBufferPointer (), 348blobA -> getBufferSize ())!= 0 ) 349 { 350return SLANG_FAIL ; 351 } 352 } 353 354return SLANG_OK ; 355} 356 357static SlangResult _createFileSystem ( 358FileSystemType type , 359ComPtr < ISlangMutableFileSystem >& outFileSystem ) 360{ 361outFileSystem .setNull (); 362switch (type ) 363 { 364case FileSystemType ::Zip : 365return ZipFileSystem ::create (outFileSystem ); 366case FileSystemType ::RiffUncompressed : 367outFileSystem = new RiffFileSystem (nullptr ); 368break ; 369case FileSystemType ::RiffDeflate : 370outFileSystem = new RiffFileSystem (DeflateCompressionSystem ::getSingleton ()); 371break ; 372case FileSystemType ::RiffLZ4 : 373outFileSystem = new RiffFileSystem (LZ4CompressionSystem ::getSingleton ()); 374break ; 375case FileSystemType ::Memory : 376outFileSystem = new MemoryFileSystem ; 377break ; 378case FileSystemType ::Relative : 379 { 380ComPtr < ISlangMutableFileSystem > memoryFileSystem (new MemoryFileSystem ); 381memoryFileSystem -> createDirectory ("base" ); 382 383outFileSystem = new RelativeFileSystem (memoryFileSystem ,"base" ); 384break ; 385 } 386 } 387 388return outFileSystem ?SLANG_OK :SLANG_FAIL ; 389} 390 391static SlangResult _testImplicitDirectory (FileSystemType type ) 392{ 393ComPtr < ISlangMutableFileSystem > fileSystem ; 394SLANG_RETURN_ON_FAIL (_createFileSystem (type ,fileSystem )); 395 396const char contents3 []= "Some text...." ; 397 398SLANG_RETURN_ON_FAIL ( 399fileSystem -> saveFile ("implicit-path/file2.txt" ,contents3 ,SLANG_COUNT_OF (contents3 ))); 400 401 { 402SlangPathType pathType ; 403SLANG_RETURN_ON_FAIL (fileSystem -> getPathType ("implicit-path" ,& pathType )); 404 405SLANG_CHECK (pathType == SLANG_PATH_TYPE_DIRECTORY ); 406 407auto checkEntries = [& ]()-> SlangResult 408 { 409List < Entry > entries ; 410SLANG_RETURN_ON_FAIL (_getAllEntries (fileSystem ,"implicit-path" ,entries )); 411 412// It contains a file 413SLANG_CHECK (entries .getCount ()== 1 ); 414 415for (const auto & entry :entries ) 416 { 417// All of these should exist 418SlangPathType pathType ; 419SLANG_RETURN_ON_FAIL (fileSystem -> getPathType (entry .path .getBuffer (),& pathType )); 420 } 421return SLANG_OK ; 422 }; 423 424SLANG_RETURN_ON_FAIL (checkEntries ()); 425 426// Make an explicit path, and see whe have the same results 427fileSystem -> createDirectory ("implicit-path" ); 428 429SLANG_RETURN_ON_FAIL (checkEntries ()); 430 } 431 432return SLANG_OK ; 433} 434 435static SlangResult _test (FileSystemType type ) 436{ 437ComPtr < ISlangMutableFileSystem > fileSystem ; 438SLANG_RETURN_ON_FAIL (_createFileSystem (type ,fileSystem )); 439 440const auto aText = "someText" ; 441const auto bText = "A longer bit of text...." ; 442const auto d_aText = "Some more silly stuff" ; 443const auto d_bText = "Lets go!" ; 444 445SLANG_RETURN_ON_FAIL (_createAndCheckFile (fileSystem ,"a" ,aText )); 446SLANG_RETURN_ON_FAIL (_createAndCheckFile (fileSystem ,"b" ,bText )); 447 448SLANG_RETURN_ON_FAIL (_createAndCheckDirectory (fileSystem ,"d" )); 449SLANG_RETURN_ON_FAIL (_createAndCheckFile (fileSystem ,"d/a" ,d_aText )); 450SLANG_RETURN_ON_FAIL (_createAndCheckFile (fileSystem ,"d\\b" ,d_bText )); 451 452// Try and absolute path 453SLANG_RETURN_ON_FAIL (_checkFile (fileSystem ,"/a" ,aText )); 454SLANG_RETURN_ON_FAIL (_checkFile (fileSystem ,"/b" ,bText )); 455SLANG_RETURN_ON_FAIL (_checkFile (fileSystem ,"/d/a" ,d_aText )); 456SLANG_RETURN_ON_FAIL (_checkFile (fileSystem ,"/d\\b" ,d_bText )); 457 458 459// Check canonical on files 460 { 461const char * paths []= {"a" ,"/a" ,"./a" ,"d/../a" ,".\\d/.\\..\\a" }; 462SLANG_RETURN_ON_FAIL (_checkCanonical (fileSystem ,paths ,SLANG_COUNT_OF (paths ))); 463 } 464 465 { 466const char * paths []= {"/d/b" ,"d/./b" }; 467SLANG_RETURN_ON_FAIL (_checkCanonical (fileSystem ,paths ,SLANG_COUNT_OF (paths ))); 468 } 469 470// Check canonical on directories 471 { 472const char * paths []= {"." ,"/" ,"/d/.." ,"d/.." }; 473SLANG_RETURN_ON_FAIL (_checkCanonical (fileSystem ,paths ,SLANG_COUNT_OF (paths ))); 474 } 475 476 { 477const char * paths []= {"d" ,"./d" ,"/d" ,"/d/./../d" }; 478SLANG_RETURN_ON_FAIL (_checkCanonical (fileSystem ,paths ,SLANG_COUNT_OF (paths ))); 479 } 480 481// Lets find all the files in the directory 482 483 { 484const Entry entries []= {{SLANG_PATH_TYPE_FILE ,"a" }, {SLANG_PATH_TYPE_FILE ,"b" }}; 485SLANG_RETURN_ON_FAIL (_enumeratePath (fileSystem ,"d" ,makeConstArrayView (entries ))); 486 } 487 488 { 489const Entry entries []= { 490 {SLANG_PATH_TYPE_FILE ,"a" }, 491 {SLANG_PATH_TYPE_FILE ,"b" }, 492 {SLANG_PATH_TYPE_DIRECTORY ,"d" }}; 493SLANG_RETURN_ON_FAIL (_enumeratePath (fileSystem ,"." ,makeConstArrayView (entries ))); 494 495// Let's check that / and \ works for the root directory 496SLANG_RETURN_ON_FAIL (_enumeratePath (fileSystem ,"/" ,makeConstArrayView (entries ))); 497SLANG_RETURN_ON_FAIL (_enumeratePath (fileSystem ,"\\" ,makeConstArrayView (entries ))); 498 } 499 500// Check the root directory exists 501 { 502SLANG_RETURN_ON_FAIL (_checkDirectoryExists (fileSystem ,"." )); 503SLANG_RETURN_ON_FAIL (_checkDirectoryExists (fileSystem ,"/" )); 504SLANG_RETURN_ON_FAIL (_checkDirectoryExists (fileSystem ,"\\" )); 505 } 506 507 { 508SLANG_RETURN_ON_FAIL (_checkSimplifiedPath (fileSystem ,"d/../a" ,"a" )); 509 } 510 511 512// If we have an archive file system check out it's behavior 513if (IArchiveFileSystem * archiveFileSystem = as < IArchiveFileSystem > (fileSystem )) 514 { 515// Load and check its okay 516 517ComPtr < ISlangBlob > archiveBlob ; 518SLANG_RETURN_ON_FAIL (archiveFileSystem -> storeArchive (false,archiveBlob .writeRef ())); 519 520ComPtr < ISlangFileSystemExt > loadedFileSystem ; 521SLANG_RETURN_ON_FAIL (loadArchiveFileSystem ( 522archiveBlob -> getBufferPointer (), 523archiveBlob -> getBufferSize (), 524loadedFileSystem )); 525 526// Check the file systems contents are the same 527SLANG_RETURN_ON_FAIL (_checkEqual (loadedFileSystem ,fileSystem )); 528 } 529 530SLANG_RETURN_ON_FAIL (fileSystem -> remove ("d/a" )); 531 { 532const Entry entries []= {{SLANG_PATH_TYPE_FILE ,"b" }}; 533SLANG_RETURN_ON_FAIL (_enumeratePath (fileSystem ,"d" ,makeConstArrayView (entries ))); 534 } 535SLANG_RETURN_ON_FAIL (fileSystem -> remove ("d\\b" )); 536 { 537SLANG_RETURN_ON_FAIL ( 538_enumeratePath (fileSystem ,"d" ,makeConstArrayView ((const Entry * )nullptr ,0 ))); 539 } 540 541// If it's removed it can't be removed again 542SLANG_CHECK (SLANG_FAILED (fileSystem -> remove ("d\\b" ))); 543 544// Remove the directory 545SLANG_RETURN_ON_FAIL (fileSystem -> remove ("d" )); 546 547 { 548const Entry entries []= {{SLANG_PATH_TYPE_FILE ,"a" }, {SLANG_PATH_TYPE_FILE ,"b" }}; 549SLANG_RETURN_ON_FAIL (_enumeratePath (fileSystem ,"." ,makeConstArrayView (entries ))); 550 } 551 552return SLANG_OK ; 553} 554 555SLANG_UNIT_TEST (fileSystem ) 556{ 557for (Index i = 0 ;i < Count (FileSystemType ::CountOf );++ i ) 558 { 559const auto type = FileSystemType (i ); 560 561SLANG_CHECK (SLANG_SUCCEEDED (_test (type ))); 562 563// Some file system types support 'implicit directories'. 564// This means that if a file is created with a path, the directories 565// required to make that path valid are 'implicitly' created. 566// 567// Currently this behavior is supported by zip, and this test checks 568// that it is working correctly, as we require the file system to 569// behave correctly in other ways irrespectively of if the directory is 570// implicit or not. 571const bool hasImplicitDirectory = (type == FileSystemType ::Zip ); 572if (hasImplicitDirectory ) 573 { 574SLANG_CHECK (SLANG_SUCCEEDED (_testImplicitDirectory (type ))); 575 } 576 } 577}