diff options
| author | Yong He <yonghe@outlook.com> | 2021-09-24 11:33:44 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-09-24 11:33:44 -0700 |
| commit | bec8e6aec85b6e3f875c58bdd59eb15613978358 (patch) | |
| tree | 0791fb2ce1be786c17e5a6ee489ed3065fc07332 /tools/slang-unit-test | |
| parent | f2a3c933bc11a498c622fa18694c84beca8ca031 (diff) | |
Move existing unit tests to a standalone dll. (#1945)
Diffstat (limited to 'tools/slang-unit-test')
| -rw-r--r-- | tools/slang-unit-test/unit-offset-container.cpp | 117 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-byte-encode.cpp | 139 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-command-line-args.cpp | 125 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-compression.cpp | 189 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-find-type-by-name.cpp | 55 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-free-list.cpp | 53 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-json.cpp | 320 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-memory-arena.cpp | 271 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-path.cpp | 62 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-riff.cpp | 179 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-short-list.cpp | 77 | ||||
| -rw-r--r-- | tools/slang-unit-test/unit-test-string.cpp | 262 |
12 files changed, 1849 insertions, 0 deletions
diff --git a/tools/slang-unit-test/unit-offset-container.cpp b/tools/slang-unit-test/unit-offset-container.cpp new file mode 100644 index 000000000..6a179c319 --- /dev/null +++ b/tools/slang-unit-test/unit-offset-container.cpp @@ -0,0 +1,117 @@ +// unit-test-path.cpp + +#include "../../source/core/slang-offset-container.h" + +#include "tools/unit-test/slang-unit-test.h" + +using namespace Slang; + +static void _checkEncodeDecode(uint32_t size) +{ + uint8_t encode[OffsetString::kMaxSizeEncodeSize]; + + size_t encodeSize = OffsetString::calcEncodedSize(size, encode); + + size_t decodedSize; + const char* chars = OffsetString::decodeSize((const char*)encode, decodedSize); + + SLANG_CHECK(decodedSize == size); + SLANG_CHECK(chars - (const char*)encode == encodeSize); +} + +namespace { // anonymous + +struct Root +{ + Offset32Array<Offset32Ptr<OffsetString> > dirs; + Offset32Ptr<OffsetString> name; + float value; +}; + +} // anonymous + +SLANG_UNIT_TEST(offsetContainer) +{ + _checkEncodeDecode(253); + + for (int64_t i = 0; i < 0x100000000; i += (i / 2) + 1) + { + _checkEncodeDecode(uint32_t(i)); + } + + { + OffsetContainer container; + + const char* strings[] = + { + "Hello", + "World", + nullptr, + }; + + { + auto& base = container.asBase(); + + Offset32Ptr<Root> root = container.newObject<Root>(); + + auto array = container.newArray<Offset32Ptr<OffsetString>>(SLANG_COUNT_OF(strings)); + for (Int i = 0; i < SLANG_COUNT_OF(strings); ++i) + { + base[array[i]] = container.newString(strings[i]); + } + base[root]->dirs = array; + } + + { + List<uint8_t> copy; + copy.addRange(container.getData(), container.getDataCount()); + + MemoryOffsetBase base; + base.set(copy.getBuffer(), copy.getCount()); + + Root* root = (Root*)(copy.getBuffer() + kStartOffset); + + SLANG_CHECK(root->dirs.getCount() == SLANG_COUNT_OF(strings)); + + Int count = root->dirs.getCount(); + for (Int i = 0; i < count; ++i) + { + OffsetString* str = base.asRaw(base.asRaw(root->dirs[i])); + + const char* check = strings[i]; + + if (check) + { + SLANG_CHECK(str != nullptr); + const char* strCstr = str->getCstr(); + SLANG_CHECK(strcmp(strCstr, check) == 0); + } + else + { + SLANG_CHECK(str == nullptr); + } + } + + { + Index index = 0; + for (const auto v : root->dirs) + { + OffsetString* str = base.asRaw(base.asRaw(v)); + const char* check = strings[index]; + if (check) + { + SLANG_CHECK(str != nullptr); + const char* strCstr = str->getCstr(); + SLANG_CHECK(strcmp(strCstr, check) == 0); + } + else + { + SLANG_CHECK(str == nullptr); + } + + index ++; + } + } + } + } +} diff --git a/tools/slang-unit-test/unit-test-byte-encode.cpp b/tools/slang-unit-test/unit-test-byte-encode.cpp new file mode 100644 index 000000000..38bd5561d --- /dev/null +++ b/tools/slang-unit-test/unit-test-byte-encode.cpp @@ -0,0 +1,139 @@ +// unit-test-byte-encode.cpp + +#include "../../source/core/slang-byte-encode-util.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "tools/unit-test/slang-unit-test.h" + +#include "../../source/core/slang-random-generator.h" +#include "../../source/core/slang-list.h" + +using namespace Slang; + +static void checkUInt32(uint32_t value) +{ + uint8_t buffer[ByteEncodeUtil::kMaxLiteEncodeUInt32 + 1]; + + int writeLen = ByteEncodeUtil::encodeLiteUInt32(value, buffer); + buffer[writeLen] = 0xcd; + + uint32_t decode; + int readLen = ByteEncodeUtil::decodeLiteUInt32(buffer, &decode); + + SLANG_CHECK(readLen == writeLen && decode == value); +} + +SLANG_UNIT_TEST(byteEncode) +{ + DefaultRandomGenerator randGen(0x5346536a); + + { + SLANG_CHECK(ByteEncodeUtil::calcMsb8(0) == -1); + SLANG_CHECK(ByteEncodeUtil::calcMsb8(1) == 0); + SLANG_CHECK(ByteEncodeUtil::calcMsb8(0x81) == 7); + } + + { + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0) == -1); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0x81) == 7); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0x00000001) == 0); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0x00000081) == 7); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0x00000181) == 8); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0x00008181) == 15); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0x00018181) == 16); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0x00818181) == 23); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0x01818181) == 24); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0x81818181) == 31); + SLANG_CHECK(ByteEncodeUtil::calcMsb32(0xffffffff) == 31); + } + + { + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0x00000000) == -1); + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0x00000001) == 0); + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0x00000081) == 0); + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0x00000181) == 1); + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0x00008181) == 1); + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0x00018181) == 2); + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0x00818181) == 2); + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0x01818181) == 3); + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0x81818181) == 3); + SLANG_CHECK(ByteEncodeUtil::calcMsByte32(0xffffffff) == 3); + } + + { + SLANG_CHECK(ByteEncodeUtil::calcNonZeroMsByte32(0x00000001) == 0); + SLANG_CHECK(ByteEncodeUtil::calcNonZeroMsByte32(0x00000081) == 0); + SLANG_CHECK(ByteEncodeUtil::calcNonZeroMsByte32(0x00000181) == 1); + SLANG_CHECK(ByteEncodeUtil::calcNonZeroMsByte32(0x00008181) == 1); + SLANG_CHECK(ByteEncodeUtil::calcNonZeroMsByte32(0x00018181) == 2); + SLANG_CHECK(ByteEncodeUtil::calcNonZeroMsByte32(0x00818181) == 2); + SLANG_CHECK(ByteEncodeUtil::calcNonZeroMsByte32(0x01818181) == 3); + SLANG_CHECK(ByteEncodeUtil::calcNonZeroMsByte32(0x81818181) == 3); + SLANG_CHECK(ByteEncodeUtil::calcNonZeroMsByte32(0xffffffff) == 3); + } + + { + const int blockSize = 1024; + + List<uint8_t> encodedBuffer; + encodedBuffer.setCount(ByteEncodeUtil::kMaxLiteEncodeUInt32 * blockSize); + + List<uint32_t> initialBuffer; + initialBuffer.setCount(blockSize); + List<uint32_t> decodeBuffer; + decodeBuffer.setCount(blockSize); + // Put in cache? + memset(decodeBuffer.begin(), 0, blockSize * sizeof(uint32_t)); + + for (int i = 0; i < blockSize; i++) + { + const int v = ByteEncodeUtil::calcMsb8(uint32_t((randGen.nextInt32() & 0xf) | 1)); + + // Make the commonality of different numbers that bytes are most common, then shorts etc.. + uint32_t mask; + switch (v) + { + case 0: mask = 0xffffffff; break; + case 1: mask = 0x00ffffff; break; + case 2: mask = 0x0000ffff; break; + case 3: mask = 0x000000ff; break; + } + + initialBuffer[i] = randGen.nextInt32() & mask; + } + + size_t numEncodeBytes = ByteEncodeUtil::encodeLiteUInt32(initialBuffer.begin(), blockSize, encodedBuffer.begin()); + + SLANG_CHECK(ByteEncodeUtil::calcEncodeLiteSizeUInt32(initialBuffer.begin(), blockSize) == numEncodeBytes); + + size_t numEncodeBytes2 = ByteEncodeUtil::decodeLiteUInt32(encodedBuffer.begin(), blockSize, decodeBuffer.begin()); + + SLANG_CHECK(numEncodeBytes2 == numEncodeBytes); + + SLANG_CHECK(memcmp(decodeBuffer.begin(), initialBuffer.begin(), sizeof(uint32_t) * blockSize) == 0); + } + + { + checkUInt32(uint32_t(0)); + checkUInt32(uint32_t(0x7fffff)); + checkUInt32(uint32_t(0x7fff)); + checkUInt32(uint32_t(0x7f)); + checkUInt32(uint32_t(0x7fffffff)); + checkUInt32(uint32_t(0xffffffff)); + +#if 1 + for (int64_t i = 0; i < SLANG_INT64(0x100000000); i += 371) + { + checkUInt32(uint32_t(i)); + } +#else + for (int64_t i = 0; i < SLANG_INT64(0x100000000); i += 1) + { + checkUInt32(uint32_t(i)); + } +#endif + } + +} diff --git a/tools/slang-unit-test/unit-test-command-line-args.cpp b/tools/slang-unit-test/unit-test-command-line-args.cpp new file mode 100644 index 000000000..a4dc8a16c --- /dev/null +++ b/tools/slang-unit-test/unit-test-command-line-args.cpp @@ -0,0 +1,125 @@ +// unit-test-command-line-args.cpp + +#include "../../source/compiler-core/slang-command-line-args.h" + +#include "tools/unit-test/slang-unit-test.h" + +using namespace Slang; + +SLANG_UNIT_TEST(commandLineArgs) +{ + RefPtr<CommandLineContext> context = new CommandLineContext; + + + // Simple scoped version + { + CommandLineArgs args(context); + DownstreamArgs downstreamArgs(context); + + DiagnosticSink sink(context->getSourceManager(), nullptr); + + const char* inArgs[] = + { + "-Xa...", + "-blah", + "10", + "-X.", + }; + + args.setArgs(inArgs, SLANG_COUNT_OF(inArgs)); + + SLANG_CHECK(SLANG_SUCCEEDED(downstreamArgs.stripDownstreamArgs(args, DownstreamArgs::Flag::AllowNewNames, &sink))); + + const char* aArgs[] = + { + "-blah", + "10" + }; + + SLANG_CHECK(downstreamArgs.getArgsByName("a").hasArgs(aArgs, SLANG_COUNT_OF(aArgs))); + SLANG_CHECK(args.getArgCount() == 0 && sink.getErrorCount() == 0); + } + + // Leaving off terminating -X. is ok + { + CommandLineArgs args(context); + DownstreamArgs downstreamArgs(context); + + DiagnosticSink sink(context->getSourceManager(), nullptr); + + const char* inArgs[] = + { + "-Xa...", + "-blah", + "10", + }; + + args.setArgs(inArgs, SLANG_COUNT_OF(inArgs)); + + SLANG_CHECK(SLANG_SUCCEEDED(downstreamArgs.stripDownstreamArgs(args, DownstreamArgs::Flag::AllowNewNames, &sink))); + + const char* aArgs[] = + { + "-blah", + "10" + }; + + SLANG_CHECK(downstreamArgs.getArgsByName("a").hasArgs(aArgs, SLANG_COUNT_OF(aArgs))); + SLANG_CHECK(args.getArgCount() == 0 && sink.getErrorCount() == 0); + } + + // Having a nesting + + { + CommandLineArgs args(context); + DownstreamArgs downstreamArgs(context); + + DiagnosticSink sink(context->getSourceManager(), nullptr); + + const char* inArgs[] = + { + "-something", + "andAnother", + "-Xa...", + "-blah", + "-Xb...", + "-hey", + "-X.", + "10", + "-X.", + "-Xc", + "somethingForC", + }; + + args.setArgs(inArgs, SLANG_COUNT_OF(inArgs)); + + SLANG_CHECK(SLANG_SUCCEEDED(downstreamArgs.stripDownstreamArgs(args, DownstreamArgs::Flag::AllowNewNames, &sink))); + + const char* aArgs[] = + { + "-blah", + "-Xb...", + "-hey", + "-X.", + "10" + }; + + const char* cArgs[] = + { + "somethingForC", + }; + + const char* mainArgs[] = + { + "-something", + "andAnother", + }; + + SLANG_CHECK(downstreamArgs.getArgsByName("a").hasArgs(aArgs, SLANG_COUNT_OF(aArgs))); + SLANG_CHECK(downstreamArgs.getArgsByName("c").hasArgs(cArgs, SLANG_COUNT_OF(cArgs))); + + SLANG_CHECK(args.hasArgs(mainArgs, SLANG_COUNT_OF(mainArgs)) && sink.getErrorCount() == 0); + } + +} + diff --git a/tools/slang-unit-test/unit-test-compression.cpp b/tools/slang-unit-test/unit-test-compression.cpp new file mode 100644 index 000000000..89716b443 --- /dev/null +++ b/tools/slang-unit-test/unit-test-compression.cpp @@ -0,0 +1,189 @@ +// unit-compression.cpp + +#include "tools/unit-test/slang-unit-test.h" + +#include "source/core/slang-io.h" +#include "../../source/core/slang-zip-file-system.h" + +#include "../../source/core/slang-lz4-compression-system.h" +#include "../../source/core/slang-deflate-compression-system.h" + +using namespace Slang; + +static bool _equals(const void* data, size_t size, ISlangBlob* blob) +{ + return blob && blob->getBufferSize() == size && memcmp(blob->getBufferPointer(), data, size) == 0; +} + +template <size_t SIZE> +static bool _equals(const char (&text)[SIZE], ISlangBlob* blob) +{ + return _equals(text, SIZE, blob); +} + +static List<String> _getContents(ISlangFileSystemExt* fileSystem, const char* path) +{ + List<String> objs; + + fileSystem->enumeratePathContents(path, [](SlangPathType pathType, const char* name, void* userData) { + List<String>& out = *(List<String>*)userData; + out.add(name); + }, &objs); + + return objs; +} + +SLANG_UNIT_TEST(compression) +{ + const SlangArchiveType archiveTypes[] = + { + SLANG_ARCHIVE_TYPE_RIFF, + SLANG_ARCHIVE_TYPE_RIFF_DEFLATE, + SLANG_ARCHIVE_TYPE_RIFF_LZ4, + SLANG_ARCHIVE_TYPE_ZIP + }; + + for (auto archiveType : archiveTypes) + { + // Test out archive file systems + RefPtr<ArchiveFileSystem> archiveFileSystem; + SLANG_CHECK(SLANG_SUCCEEDED(createArchiveFileSystem(archiveType, archiveFileSystem))); + + const char contents[] = "I'm compressed"; + const char contents2[] = "Some more stuff"; + const char contents3[] = "Replace it"; + + { + ISlangMutableFileSystem* fileSystem = archiveFileSystem; + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->createDirectory("hello"))); + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->createDirectory("hello2"))); + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->remove("hello"))); + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->createDirectory("hello"))); + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->saveFile("file.txt", contents, SLANG_COUNT_OF(contents)))); + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->saveFile("file2.txt", contents2, SLANG_COUNT_OF(contents2)))); + + ComPtr<ISlangBlob> blob; + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file.txt", blob.writeRef()))); + SLANG_CHECK(_equals(contents, blob)); + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file2.txt", blob.writeRef()))); + SLANG_CHECK(_equals(contents2, blob)); + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->saveFile("file2.txt", contents3, SLANG_COUNT_OF(contents3)))); + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file2.txt", blob.writeRef()))); + SLANG_CHECK(_equals(contents3, blob)); + + // Check the path type + { + SlangPathType pathType; + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType("file2.txt", &pathType))); + SLANG_CHECK(pathType == SLANG_PATH_TYPE_FILE); + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType("hello", &pathType))); + SLANG_CHECK(pathType == SLANG_PATH_TYPE_DIRECTORY); + } + + // Enumerate + { + for (const auto& obj : _getContents(fileSystem, "")) + { + // All of these should exist + SlangPathType pathType; + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType(obj.getBuffer(), &pathType))); + } + } + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->saveFile("implicit-path/file2.txt", contents3, SLANG_COUNT_OF(contents3)))); + + { + SlangPathType pathType; + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType("implicit-path", &pathType))); + + SLANG_CHECK(pathType == SLANG_PATH_TYPE_DIRECTORY); + + List<String> objs = _getContents(fileSystem, "implicit-path"); + + // It contains a file + SLANG_CHECK(objs.getCount() == 1); + + for (const auto& obj : objs) + { + String path = Path::combine("implicit-path", obj); + + // All of these should exist + SlangPathType pathType; + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->getPathType(path.getBuffer(), &pathType))); + } + + // Make an explicit path, and see whe have the same results + fileSystem->createDirectory("implicit-path"); + + objs = _getContents(fileSystem, "implicit-path"); + SLANG_CHECK(objs.getCount() == 1); + } + } + + + // Load and check its okay + + { + ComPtr<ISlangBlob> archiveBlob; + SLANG_CHECK(SLANG_SUCCEEDED(archiveFileSystem->storeArchive(false, archiveBlob.writeRef()))); + + + RefPtr<ArchiveFileSystem> fileSystem; +#if 0 + SLANG_CHECK(SLANG_SUCCEEDED(createArchiveFileSystem(archiveType, fileSystem))); + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadArchive(archiveBlob->getBufferPointer(), archiveBlob->getBufferSize()))); +#else + SLANG_CHECK(SLANG_SUCCEEDED(loadArchiveFileSystem(archiveBlob->getBufferPointer(), archiveBlob->getBufferSize(), fileSystem))); +#endif + + ComPtr<ISlangBlob> blob; + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file.txt", blob.writeRef()))); + SLANG_CHECK(_equals(contents, blob)); + + SLANG_CHECK(SLANG_SUCCEEDED(fileSystem->loadFile("file2.txt", blob.writeRef()))); + SLANG_CHECK(_equals(contents3, blob)); + } + } + + // Test out compression systems + for (Index i = 0; i < 2; ++i) + { + // Lets try lz4 + + ICompressionSystem* system = nullptr; + if (i == 0) + { + system = LZ4CompressionSystem::getSingleton(); + } + else + { + system = DeflateCompressionSystem::getSingleton(); + } + + const char src[] = "Some text to compress"; + size_t srcSize = sizeof(src); + + ComPtr<ISlangBlob> compressedBlob; + + CompressionStyle style; + + SLANG_CHECK(SLANG_SUCCEEDED(system->compress(&style, src, srcSize, compressedBlob.writeRef()))); + + // Now lets decompress + + List<char> decompressedData; + decompressedData.setCount(srcSize); + + SLANG_CHECK(SLANG_SUCCEEDED(system->decompress(compressedBlob->getBufferPointer(), compressedBlob->getBufferSize(), srcSize, decompressedData.getBuffer()))); + SLANG_CHECK(memcmp(src, decompressedData.getBuffer(), srcSize) == 0); + } +} diff --git a/tools/slang-unit-test/unit-test-find-type-by-name.cpp b/tools/slang-unit-test/unit-test-find-type-by-name.cpp new file mode 100644 index 000000000..d12a0d91f --- /dev/null +++ b/tools/slang-unit-test/unit-test-find-type-by-name.cpp @@ -0,0 +1,55 @@ +// unit-test-byte-encode.cpp + +#include "../../slang.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "tools/unit-test/slang-unit-test.h" + +using namespace Slang; + +SLANG_UNIT_TEST(findTypeByName) +{ + const char* testSource = + "struct TestStruct {" + " int member0;" + " Texture2D texture1;" + "};"; + auto session = spCreateSession(); + auto request = spCreateCompileRequest(session); + spAddCodeGenTarget(request, SLANG_DXBC); + int tuIndex = spAddTranslationUnit(request, SLANG_SOURCE_LANGUAGE_SLANG, "tu1"); + spAddTranslationUnitSourceString(request, tuIndex, "internalFile", testSource); + spCompile(request); + + auto testBody = [&]() + { + auto reflection = slang::ShaderReflection::get(request); + auto testStruct = reflection->findTypeByName("TestStruct"); + SLANG_CHECK_ABORT(testStruct->getFieldCount() == 2); + auto field0Name = testStruct->getFieldByIndex(0)->getName(); + SLANG_CHECK_ABORT(field0Name != nullptr && strcmp(field0Name, "member0") == 0); + auto field1Name = testStruct->getFieldByIndex(1)->getName(); + SLANG_CHECK_ABORT(field1Name != nullptr && strcmp(field1Name, "texture1") == 0); + + auto intType = reflection->findTypeByName("int"); + auto intTypeName = intType->getName(); + SLANG_CHECK_ABORT(intTypeName && strcmp(intTypeName, "int") == 0); + + auto paramBlockType = reflection->findTypeByName("ParameterBlock<TestStruct>"); + SLANG_CHECK_ABORT(paramBlockType != nullptr); + auto paramBlockTypeName = paramBlockType->getName(); + SLANG_CHECK_ABORT(paramBlockTypeName && strcmp(paramBlockTypeName, "ParameterBlock") == 0); + auto paramBlockElementType = paramBlockType->getElementType(); + SLANG_CHECK_ABORT(paramBlockElementType != nullptr); + auto paramBlockElementTypeName = paramBlockElementType->getName(); + SLANG_CHECK_ABORT(paramBlockElementTypeName && strcmp(paramBlockElementTypeName, "TestStruct") == 0); + }; + + testBody(); + + spDestroyCompileRequest(request); + spDestroySession(session); +} + diff --git a/tools/slang-unit-test/unit-test-free-list.cpp b/tools/slang-unit-test/unit-test-free-list.cpp new file mode 100644 index 000000000..d6c8b47a7 --- /dev/null +++ b/tools/slang-unit-test/unit-test-free-list.cpp @@ -0,0 +1,53 @@ +// unit-test-free-list.cpp + +#include "../../source/core/slang-free-list.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "tools/unit-test/slang-unit-test.h" + +#include "../../source/core/slang-random-generator.h" +#include "../../source/core/slang-list.h" + +using namespace Slang; + +SLANG_UNIT_TEST(freeList) +{ + FreeList freeList; + freeList.init(sizeof(int), sizeof(void*), 10); + + DefaultRandomGenerator randGen(0x24343); + + List<int*> allocs; + + for (int i = 0; i < 1000; i++) + { + const int numAlloc = randGen.nextInt32UpTo(20); + + for (int j = 0; j < numAlloc; j++) + { + int* ptr = (int*)freeList.allocate(); + *ptr = i; + allocs.add(ptr); + } + + int numDealloc = randGen.nextInt32UpTo(19); + numDealloc = int(allocs.getCount()) < numDealloc ? int(allocs.getCount()) : numDealloc; + + for (int j = 0; j < numDealloc; j++) + { + const int index = randGen.nextInt32UpTo(int(allocs.getCount())); + + int* alloc = allocs[index]; + + SLANG_CHECK(*alloc <= i); + SLANG_CHECK(*alloc >= 0); + + freeList.deallocate(alloc); + + allocs.fastRemoveAt(index); + } + } +} + diff --git a/tools/slang-unit-test/unit-test-json.cpp b/tools/slang-unit-test/unit-test-json.cpp new file mode 100644 index 000000000..2d2874cc8 --- /dev/null +++ b/tools/slang-unit-test/unit-test-json.cpp @@ -0,0 +1,320 @@ + +#include "../../source/compiler-core/slang-json-lexer.h" +#include "../../source/core/slang-string-escape-util.h" +#include "../../source/compiler-core/slang-json-parser.h" +#include "../../source/compiler-core/slang-json-value.h" + +#include "tools/unit-test/slang-unit-test.h" + +using namespace Slang; + +namespace { // anonymous + +struct Element +{ + JSONTokenType type; + const char* value; +}; + +} // anonymous + +static SlangResult _lex(const char* in, DiagnosticSink* sink, List<JSONToken>& toks) +{ + SourceManager* sourceManager = sink->getSourceManager(); + + String contents(in); + SourceFile* sourceFile = sourceManager->createSourceFileWithString(PathInfo::makeUnknown(), contents); + SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr, SourceLoc()); + + JSONLexer lexer; + + lexer.init(sourceView, sink); + + while (lexer.peekType() != JSONTokenType::EndOfFile) + { + if (lexer.peekType() == JSONTokenType::Invalid) + { + toks.add(lexer.peekToken()); + return SLANG_FAIL; + } + + toks.add(lexer.peekToken()); + lexer.advance(); + } + + toks.add(lexer.peekToken()); + + // If we advance from end of file we should still be at EndOfFile + SLANG_ASSERT(lexer.advance() == JSONTokenType::EndOfFile); + + return SLANG_OK; +} + +static SlangResult _parse(const char* in, DiagnosticSink* sink, JSONListener* listener) +{ + SourceManager* sourceManager = sink->getSourceManager(); + + String contents(in); + SourceFile* sourceFile = sourceManager->createSourceFileWithString(PathInfo::makeUnknown(), contents); + SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr, SourceLoc()); + + JSONLexer lexer; + lexer.init(sourceView, sink); + + JSONParser parser; + SLANG_RETURN_ON_FAIL(parser.parse(&lexer, sourceView, listener, sink)); + return SLANG_OK; +} + +static bool _areEqual(SourceManager* sourceManager, const List<JSONToken>& toks, const Element* eles, Index elesCount) +{ + if (toks.getCount() != elesCount) + { + return false; + } + + SourceView* sourceView = toks.getCount() ? sourceManager->findSourceView(toks[0].loc) : nullptr; + const char*const content = sourceView ? sourceView->getContent().begin() : nullptr; + + for (Index i = 0; i < toks.getCount(); ++i) + { + const JSONToken& tok = toks[i]; + const auto& ele = eles[i]; + + if (tok.type != ele.type) + { + return false; + } + + SLANG_ASSERT(sourceView->getRange().contains(tok.loc)); + + const char* start = content + sourceView->getRange().getOffset(tok.loc); + + UnownedStringSlice lexeme(start, tok.length); + + if (lexeme != ele.value) + { + return false; + } + } + + return true; +} + +SLANG_UNIT_TEST(json) +{ + SourceManager sourceManager; + sourceManager.initialize(nullptr, nullptr); + DiagnosticSink sink(&sourceManager, nullptr); + + { + const char text[] = " { \"Hello\" : [ \"World\", 1, 2.0, -3.0, -435.5345435, 45e-10, 421.00e+20, 17e1] }"; + + const Element eles[] = + { + {JSONTokenType::LBrace, "{" }, + {JSONTokenType::StringLiteral, "\"Hello\""}, + {JSONTokenType::Colon, ":" }, + {JSONTokenType::LBracket, "[" }, + {JSONTokenType::StringLiteral, "\"World\"" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::IntegerLiteral, "1" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "2.0" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "-3.0" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "-435.5345435" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "45e-10" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "421.00e+20" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "17e1" }, + {JSONTokenType::RBracket, "]" }, + {JSONTokenType::RBrace, "}" }, + {JSONTokenType::EndOfFile, "" }, + }; + + List<JSONToken> toks; + SLANG_CHECK(SLANG_SUCCEEDED(_lex(text, &sink, toks))); + + SLANG_CHECK(_areEqual(&sourceManager, toks, eles, SLANG_COUNT_OF(eles))); + } + + { + StringEscapeHandler* handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::JSON); + + + { + const auto slice = UnownedStringSlice::fromLiteral("\n\r\b\f\t \"\\/ Some text..."); + + SLANG_CHECK(handler->isEscapingNeeded(slice)); + SLANG_CHECK(!handler->isEscapingNeeded(UnownedStringSlice::fromLiteral("Hello!"))); + + StringBuilder escaped; + handler->appendEscaped(slice, escaped); + + StringBuilder unescaped; + handler->appendUnescaped(escaped.getUnownedSlice(), unescaped); + + SLANG_CHECK(unescaped == slice); + } + + { + uint32_t v = 0x7f; + + StringBuilder buf; + while (v < 0x10000) + { + char work[10] = "\\u"; + + for (Int i = 0; i < 4; ++i) + { + const uint32_t digitValue = (v >> ((3 - i) * 4)) & 0xf; + + char digitC = (digitValue > 9) ? char(digitValue - 10 + 'a') : char(digitValue + '0'); + work[i + 2] = digitC; + } + + buf << UnownedStringSlice(work, 6); + + v += v; + } + + // Decode it + StringBuilder unescaped; + handler->appendUnescaped(buf.getUnownedSlice(), unescaped); + + // Encode it + StringBuilder escaped; + handler->appendEscaped(unescaped.getUnownedSlice(), escaped); + + SLANG_CHECK(escaped == buf); + } + } + + { + const char in[] = "{ \"Hello\" : \"Json\", \"!\" : 10, \"array\" : [1, 2, 3.0] }"; + + { + auto style = JSONWriter::IndentationStyle::Allman; + + JSONWriter writer(style); + _parse(in, &sink, &writer); + + JSONWriter writerCheck(style); + _parse(writer.getBuilder().getBuffer(), &sink, &writerCheck); + + SLANG_CHECK(writerCheck.getBuilder() == writer.getBuilder()); + } + + { + auto style = JSONWriter::IndentationStyle::KNR; + + JSONWriter writer(style, 80); + _parse(in, &sink, &writer); + + JSONWriter writerCheck(style); + _parse(writer.getBuilder().getBuffer(), &sink, &writerCheck); + + SLANG_CHECK(writerCheck.getBuilder() == writer.getBuilder()); + } + + { + // Let's parse into a Value + RefPtr<JSONContainer> container = new JSONContainer(&sourceManager); + + JSONValue value; + { + JSONBuilder builder(container); + + SLANG_CHECK(SLANG_SUCCEEDED(_parse(in, &sink, &builder))); + value = builder.getRootValue(); + } + // Let's recreate + JSONValue copy; + { + JSONBuilder builder(container); + container->traverseRecursively(value, &builder); + copy = builder.getRootValue(); + } + + SLANG_CHECK(container->areEqual(value, copy)); + + } + } + + { + // Only need a SourceManager if we are going to store lexemes + RefPtr<JSONContainer> container = new JSONContainer(nullptr); + + { + List<JSONValue> values; + + for (Int i = 0; i < 100; ++i) + { + + values.add(JSONValue::makeInt(i)); + values.add(JSONValue::makeFloat(-double(i))); + } + + JSONValue array = container->createArray(values.getBuffer(), values.getCount()); + + auto arrayView = container->getArray(array); + + SLANG_CHECK(arrayView.getCount() == values.getCount()); + + // Check the values are the same + SLANG_CHECK(container->areEqual(arrayView.getBuffer(), values.getBuffer(), arrayView.getCount())); + + { + JSONWriter writer(JSONWriter::IndentationStyle::KNR, 80); + + container->traverseRecursively(array, &writer); + } + } + { + JSONValue obj = JSONValue::makeEmptyObject(); + + JSONKey key = container->getKey(UnownedStringSlice::fromLiteral("Hello")); + + container->setKeyValue(obj, key, JSONValue::makeNull()); + container->setKeyValue(obj, key, JSONValue::makeInt(10)); + + auto objView = container->getObject(obj); + + SLANG_CHECK(objView.getCount() == 1); + + SLANG_CHECK(objView[0].value.asInteger() == 10); + } + } + + // Check repeated keys works out + // Check out comparison works with different key orders + { + RefPtr<JSONContainer> container = new JSONContainer(&sourceManager); + const char aText[] = "{ \"a\" : 10, \"b\" : 20.0, \"a\" : \"Hello\" }"; + + + JSONBuilder builder(container); + SLANG_CHECK(SLANG_SUCCEEDED(_parse(aText, &sink, &builder))); + const JSONValue a = builder.getRootValue(); + + builder.reset(); + + const char bText[] = "{ \"b\" : 20.0, \"a\" : \"Hello\"}"; + SLANG_CHECK(SLANG_SUCCEEDED(_parse(bText, &sink, &builder))); + const JSONValue b = builder.getRootValue(); + + SLANG_CHECK(container->areEqual(a, b)); + + JSONBuilder convertBuilder(container, JSONBuilder::Flag::ConvertLexemes); + + SLANG_CHECK(SLANG_SUCCEEDED(_parse(aText, &sink, &convertBuilder))); + const JSONValue c = builder.getRootValue(); + + SLANG_CHECK(container->areEqual(a, c)); + } +} + diff --git a/tools/slang-unit-test/unit-test-memory-arena.cpp b/tools/slang-unit-test/unit-test-memory-arena.cpp new file mode 100644 index 000000000..b2671160a --- /dev/null +++ b/tools/slang-unit-test/unit-test-memory-arena.cpp @@ -0,0 +1,271 @@ +// unit-test-free-list.cpp + +#include "../../source/core/slang-memory-arena.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "tools/unit-test/slang-unit-test.h" + +#include "../../source/core/slang-random-generator.h" +#include "../../source/core/slang-list.h" + +using namespace Slang; + + +namespace // anonymous +{ + +struct Block +{ + void* m_data; + size_t m_size; + uint8_t m_value; +}; + +enum class TestMode +{ + eUnaligned, + eImplicitAligned, ///< Alignment is kept implicitly with Unaligned allocs of the right size + eDefaultAligned, + eExplicitAligned, + eCount, +}; + +} // anonymous + +static size_t getAlignment(TestMode mode) +{ + switch (mode) + { + default: + case TestMode::eUnaligned: + return 1; + case TestMode::eExplicitAligned: + return 16; + case TestMode::eImplicitAligned: + return 32; + case TestMode::eDefaultAligned: + return MemoryArena::kMinAlignment; + } +} + +static bool hasValueShort(const uint8_t* data, size_t size, uint8_t value) +{ + for (size_t i = 0; i < size; ++i) + { + if (data[i] != value) + { + return false; + } + } + return true; +} + +static bool hasValue(const uint8_t* data, size_t size, uint8_t value) +{ + const size_t alignMask = sizeof(size_t) - 1; + + if (size <= sizeof(size_t) * 2) + { + return hasValueShort(data, size, value); + } + + if (size_t(data) & alignMask) + { + size_t firstSize = sizeof(size_t) - (size_t(data) & alignMask); + if (!hasValueShort(data, firstSize, value)) + { + return false; + } + size -= firstSize; + data += firstSize; + + assert((size_t(data) & alignMask) == 0); + } + + // Now do the middle + size_t numWords = size / sizeof(size_t); + + // Expand the byte up to a word size + size_t wordValue = (size_t(value) << 8) | value; + wordValue = (wordValue << 16) | wordValue; + wordValue = (sizeof(size_t) > 4) ? size_t((uint64_t(wordValue) << 32) | wordValue) : wordValue; + + const size_t* wordData = (const size_t*)data; + for (size_t i = 0; i < numWords; ++i) + { + if (wordData[i] != wordValue) + { + return false; + } + } + + // Do the end piece + return hasValueShort(data + sizeof(size_t) * numWords, size & alignMask, value); +} + +SLANG_UNIT_TEST(memoryArena) +{ + DefaultRandomGenerator randGen(0x5346536a); + + { + const size_t blockSize = 1024; + MemoryArena arena; + arena.init(blockSize); + + List<void*> blocks; + + blocks.add(arena.allocate(100)); + blocks.add(arena.allocate(blockSize * 2)); + blocks.add(arena.allocate(100)); + blocks.add(arena.allocate(blockSize * 2)); + blocks.add(arena.allocate(100)); + + arena.deallocateAll(); + blocks.add(arena.allocate(100)); + blocks.add(arena.allocate(blockSize * 2)); + + arena.reset(); + + { + uint32_t data[] = { 1, 2, 3 }; + + const uint32_t* copy = arena.allocateAndCopyArray(data, SLANG_COUNT_OF(data)); + + SLANG_CHECK(::memcmp(copy, data, sizeof(data)) == 0); + } + } + + { + int count = 0; + const size_t blockSize = 1024; + + for (TestMode mode = TestMode(0); int(mode) < int(TestMode::eCount); mode = TestMode(int(mode) + 1)) + { + const size_t alignment = getAlignment(mode); + + MemoryArena arena; + arena.init(blockSize, alignment); + + List<Block> blocks; + + for (int i = 0; i < 10000; i++) + { + count++; + + const int var = randGen.nextInt32() & 0x3ff; + if (var < 3 && blocks.getCount() > 0) + { + if (var == 1) + { + // Deallocate everything + arena.deallocateAll(); + blocks.clear(); + } + else if (var == 2) + { + arena.reset(); + blocks.clear(); + } + else if (var == 3) + { + arena.rewindToCursor(nullptr); + blocks.clear(); + } + else if (var == 4) + { + // Rewind to a random position + int rewindIndex = randGen.nextInt32UpTo(int32_t(blocks.getCount())); + // rewind to this block + arena.rewindToCursor(blocks[rewindIndex].m_data); + // All the blocks (includign this one) and now deallocated + blocks.setCount(rewindIndex); + + } + else + { + size_t usedMemory = arena.calcTotalMemoryUsed(); + size_t allocatedMemory = arena.calcTotalMemoryAllocated(); + + SLANG_CHECK(allocatedMemory >= usedMemory); + } + } + else + { + size_t sizeInBytes = (randGen.nextInt32() & 255) + 1; + + // Lets go for an oversized block + if ((randGen.nextInt32() & 0xff) < 2) + { + sizeInBytes += blockSize; + } + else if ((randGen.nextInt32() & 0xff) < 2) + { + // Let's try for a block that's awkwardly sized + sizeInBytes = blockSize / 3 + 10; + } + + const uint8_t value = uint8_t(randGen.nextInt32()); + + void* mem = nullptr; + switch (mode) + { + default: + case TestMode::eUnaligned: + { + mem = arena.allocateUnaligned(sizeInBytes); + break; + } + case TestMode::eImplicitAligned: + { + // Fix the size to get implicit alignment + sizeInBytes = (sizeInBytes & ~(alignment - 1)) + alignment; + mem = arena.allocateUnaligned(sizeInBytes); + break; + } + case TestMode::eExplicitAligned: + { + mem = arena.allocateAligned(sizeInBytes, alignment); + break; + } + case TestMode::eDefaultAligned: + { + mem = arena.allocate(sizeInBytes); + break; + } + } + + // Check it is aligned + SLANG_CHECK((size_t(mem) & (alignment - 1)) == 0); + + ::memset(mem, value, sizeInBytes); + + Block block; + + block.m_data = mem; + block.m_size = sizeInBytes; + block.m_value = value; + + blocks.add(block); + } + + // Check the blocks + for (Index j = 0; j < blocks.getCount(); ++j) + { + const Block& block = blocks[j]; + + SLANG_CHECK(arena.isValid(block.m_data, block.m_size)); + + SLANG_CHECK(hasValue((uint8_t*)block.m_data, block.m_size, block.m_value)); + } + } + } + } + { + // Do lots of allocations and test out rewind + + + + } +} diff --git a/tools/slang-unit-test/unit-test-path.cpp b/tools/slang-unit-test/unit-test-path.cpp new file mode 100644 index 000000000..c27feee9c --- /dev/null +++ b/tools/slang-unit-test/unit-test-path.cpp @@ -0,0 +1,62 @@ +// unit-test-path.cpp + +#include "../../source/core/slang-io.h" + +#include "tools/unit-test/slang-unit-test.h" + +using namespace Slang; + +SLANG_UNIT_TEST(path) +{ +#if SLANG_WINDOWS_FAMILY + // Disable for now on non windows has some problems on *some* Linux based CI. + { + String path; + SlangResult res = Path::getCanonical("source/slang", path); + SLANG_CHECK(SLANG_SUCCEEDED(res)); + + String parentPath; + res = Path::getCanonical("source", parentPath); + SLANG_CHECK(SLANG_SUCCEEDED(res)); + + String parentPath2 = Path::getParentDirectory(path); + SLANG_CHECK(parentPath == parentPath2); + } +#endif + // Test the paths + { + SLANG_CHECK(Path::simplify(".") == "."); + SLANG_CHECK(Path::simplify("..") == ".."); + SLANG_CHECK(Path::simplify("blah/..") == "."); + + SLANG_CHECK(Path::simplify("blah/.././a") == "a"); + + SLANG_CHECK(Path::simplify("a:/what/.././../is/./../this/.") == "a:/../this"); + + SLANG_CHECK(Path::simplify("a:/what/.././../is/./../this/./") == "a:/../this"); + + SLANG_CHECK(Path::simplify("a:\\what\\..\\.\\..\\is\\.\\..\\this\\.\\") == "a:/../this"); + + SLANG_CHECK(Path::simplify("tests/preprocessor/.\\pragma-once-a.h") == "tests/preprocessor/pragma-once-a.h"); + + + SLANG_CHECK(Path::hasRelativeElement(".")); + SLANG_CHECK(Path::hasRelativeElement("..")); + SLANG_CHECK(Path::hasRelativeElement("blah/..")); + + SLANG_CHECK(Path::hasRelativeElement("blah/.././a")); + SLANG_CHECK(Path::hasRelativeElement("a") == false); + SLANG_CHECK(Path::hasRelativeElement("blah/a") == false); + SLANG_CHECK(Path::hasRelativeElement("a:\\blah/a") == false); + + + SLANG_CHECK(Path::hasRelativeElement("a:/what/.././../is/./../this/.")); + + SLANG_CHECK(Path::hasRelativeElement("a:/what/.././../is/./../this/./")); + + SLANG_CHECK(Path::hasRelativeElement("a:\\what\\..\\.\\..\\is\\.\\..\\this\\.\\")); + + + } +} + diff --git a/tools/slang-unit-test/unit-test-riff.cpp b/tools/slang-unit-test/unit-test-riff.cpp new file mode 100644 index 000000000..2902a9af5 --- /dev/null +++ b/tools/slang-unit-test/unit-test-riff.cpp @@ -0,0 +1,179 @@ +// unit-test-riff.cpp + +#include "../../source/core/slang-riff.h" + +#include "../../source/core/slang-random-generator.h" + +#include "tools/unit-test/slang-unit-test.h" + +using namespace Slang; + +static void _writeRandom(RandomGenerator* rand, size_t maxSize, RiffContainer& ioContainer, List<uint8_t>& ioData) +{ + while (true) + { + const Index oldCount = ioData.getCount(); + + const size_t allocSize = size_t(rand->nextInt32InRange(1, 50)); + + if (allocSize + oldCount > maxSize) + { + break; + } + + ioData.setCount(oldCount + Index(allocSize)); + rand->nextData(ioData.getBuffer() + oldCount, allocSize); + + // Write + ioContainer.write(ioData.getBuffer() + oldCount, allocSize); + } + + // Should be a single block with same data as the List + RiffContainer::DataChunk* dataChunk = as<RiffContainer::DataChunk>(ioContainer.getCurrentChunk()); + SLANG_ASSERT(dataChunk); +} + +SLANG_UNIT_TEST(riff) +{ + typedef RiffContainer::ScopeChunk ScopeChunk; + typedef RiffContainer::Chunk::Kind Kind; + + const FourCC markThings = SLANG_FOUR_CC('T', 'H', 'I', 'N'); + const FourCC markData = SLANG_FOUR_CC('D', 'A', 'T', 'A'); + + { + RiffContainer container; + + { + ScopeChunk scopeContainer(&container, Kind::List, markThings); + { + ScopeChunk scopeChunk(&container, Kind::Data, markData); + + const char hello[] = "Hello "; + const char world[] = "World!"; + + container.write(hello, sizeof(hello)); + container.write(world, sizeof(world)); + } + + { + ScopeChunk scopeChunk(&container, Kind::Data, markData); + + const char test0[] = "Testing... "; + const char test1[] = "Testing!"; + + container.write(test0, sizeof(test0)); + container.write(test1, sizeof(test1)); + } + + { + ScopeChunk innerScopeContainer(&container, Kind::List, markThings); + + { + ScopeChunk scopeChunk(&container, Kind::Data, markData); + + const char another[] = "Another?"; + container.write(another, sizeof(another)); + } + } + } + + SLANG_CHECK(container.isFullyConstructed()); + SLANG_CHECK(RiffContainer::isChunkOk(container.getRoot())); + + { + StringBuilder builder; + { + StringWriter writer(&builder, 0); + RiffUtil::dump(container.getRoot(), &writer); + } + + { + OwnedMemoryStream stream(FileAccess::ReadWrite); + SLANG_CHECK(SLANG_SUCCEEDED(RiffUtil::write(container.getRoot(), true, &stream))); + + stream.seek(SeekOrigin::Start, 0); + + RiffContainer readContainer; + SLANG_CHECK(SLANG_SUCCEEDED(RiffUtil::read(&stream, readContainer))); + + // Dump the read contents + StringBuilder readBuilder; + { + StringWriter writer(&readBuilder, 0); + RiffUtil::dump(readContainer.getRoot(), &writer); + } + + // They should be the same + SLANG_CHECK(readBuilder == builder); + } + } + + } + + // Test writing as a stream only allocates a single data block (as long as there is enough space). + { + RiffContainer container; + + ScopeChunk scopeChunk(&container, Kind::List, markData); + { + ScopeChunk scopeChunk(&container, Kind::Data, markData); + RefPtr<RandomGenerator> rand = RandomGenerator::create(0x345234); + + List<uint8_t> data; + _writeRandom(rand, container.getMemoryArena().getBlockPayloadSize() / 2, container, data); + + // Should be a single block with same data as the List + RiffContainer::DataChunk* dataChunk = as<RiffContainer::DataChunk>(container.getCurrentChunk()); + SLANG_ASSERT(dataChunk); + + // It should be a single block + SLANG_CHECK(dataChunk->getSingleData() != nullptr); + + SLANG_CHECK(dataChunk->isEqual(data.getBuffer(), data.getCount())); + + } + } + + // Test writing across multiple data blocks + { + RefPtr<RandomGenerator> rand = RandomGenerator::create(0x345234); + + for (Int i = 0 ; i < 100; ++i) + { + RiffContainer container; + + const size_t maxSize = rand->nextInt32InRange(1, int32_t(container.getMemoryArena().getBlockPayloadSize() * 3)); + + ScopeChunk scopeChunk(&container, Kind::List, markData); + { + ScopeChunk scopeChunk(&container, Kind::Data, markData); + + List<uint8_t> data; + _writeRandom(rand, maxSize, container, data); + + // Should be a single block with same data as the List + RiffContainer::DataChunk* dataChunk = as<RiffContainer::DataChunk>(container.getCurrentChunk()); + SLANG_CHECK(dataChunk && dataChunk->isEqual(data.getBuffer(), data.getCount())); + } + } + } + +#if 0 + { + RiffContainer container; + { + FileStream readStream("ambient-drop.wav", FileMode::Open, FileAccess::Read, FileShare::ReadWrite); + SLANG_CHECK(SLANG_SUCCEEDED(RiffUtil::read(&readStream, container))); + RiffUtil::dump(container.getRoot(), StdWriters::getOut()); + } + // Write it + { + + FileStream writeStream("check.wav", FileMode::Create, FileAccess::Write, FileShare::ReadWrite); + SLANG_CHECK(SLANG_SUCCEEDED(RiffUtil::write(container.getRoot(), true, &writeStream))); + } + } +#endif +} + diff --git a/tools/slang-unit-test/unit-test-short-list.cpp b/tools/slang-unit-test/unit-test-short-list.cpp new file mode 100644 index 000000000..2760d633e --- /dev/null +++ b/tools/slang-unit-test/unit-test-short-list.cpp @@ -0,0 +1,77 @@ +// unit-test-path.cpp + +#include "source/core/slang-basic.h" +#include "tools/unit-test/slang-unit-test.h" + +using namespace Slang; + +template<typename T> +static bool _checkArrayView(ArrayView<T> v0, ArrayView<T> v1) +{ + if (v0.getCount() != v1.getCount()) + return false; + for (Index i = 0; i < v0.getCount(); i++) + if (v0[i] != v1[i]) + return false; + return true; +} + +SLANG_UNIT_TEST(shortList) +{ + { + ShortList<String, 4> shortList = { "a", "b", "c" }; + shortList.add("d"); + auto arrayView = shortList.getArrayView(); + SLANG_CHECK(arrayView.ownsStorage == false); + SLANG_CHECK(_checkArrayView(arrayView.arrayView, + List<String>{"a", "b", "c", "d"}.getArrayView())); + shortList.add("e"); + auto arrayView2 = shortList.getArrayView(); + SLANG_CHECK(arrayView2.ownsStorage == true); + SLANG_CHECK(_checkArrayView(arrayView2.arrayView, + List<String>{"a", "b", "c", "d", "e"}.getArrayView())); + auto arrayView3 = shortList.getArrayView(0, 2); + SLANG_CHECK(arrayView3.ownsStorage == false); + SLANG_CHECK(_checkArrayView(arrayView3.arrayView, + List<String>{"a", "b"}.getArrayView())); + auto arrayView4 = shortList.getArrayView(4, 1); + SLANG_CHECK(arrayView4.ownsStorage == false); + SLANG_CHECK(_checkArrayView(arrayView4.arrayView, + List<String>{"e"}.getArrayView())); + auto arrayView5 = shortList.getArrayView(2, 3); + SLANG_CHECK(arrayView5.ownsStorage == true); + SLANG_CHECK(_checkArrayView(arrayView5.arrayView, + List<String>{"c", "d", "e"}.getArrayView())); + + ShortList<String, 1> copy2; + ShortList<String, 2> copy1; + copy1 = shortList; + for (auto item : copy1) + copy2.add(item); + SLANG_CHECK(_checkArrayView(copy2.getArrayView().arrayView, + List<String>{"a", "b", "c", "d", "e"}.getArrayView())); + + SLANG_CHECK(copy2.indexOf("a") == 0); + SLANG_CHECK(copy2.indexOf("e") == 4); + + SLANG_CHECK(copy2.lastIndexOf("a") == 0); + SLANG_CHECK(copy2.lastIndexOf("e") == 4); + + copy2.compress(); + copy2.add("f"); + copy2.fastRemove("c"); + copy2.compress(); + SLANG_CHECK(_checkArrayView(copy2.getArrayView().arrayView, + List<String>{"a", "b", "f", "d", "e"}.getArrayView())); + + shortList.removeLast(); + shortList.removeLast(); + shortList.compress(); + SLANG_CHECK(_checkArrayView(shortList.getArrayView().arrayView, + List<String>{"a", "b", "c"}.getArrayView())); + shortList.add("d"); + shortList.add("e"); + SLANG_CHECK(_checkArrayView(shortList.getArrayView().arrayView, + List<String>{"a", "b", "c", "d", "e"}.getArrayView())); + } +} diff --git a/tools/slang-unit-test/unit-test-string.cpp b/tools/slang-unit-test/unit-test-string.cpp new file mode 100644 index 000000000..629ed2373 --- /dev/null +++ b/tools/slang-unit-test/unit-test-string.cpp @@ -0,0 +1,262 @@ +// unit-test-path.cpp + +#include "../../source/core/slang-string-util.h" + +#include "tools/unit-test/slang-unit-test.h" + +//#include <math.h> + +#include <sstream> + +using namespace Slang; + +static bool _areEqual(const List<UnownedStringSlice>& lines, const UnownedStringSlice* checkLines, Int checkLinesCount) +{ + if (checkLinesCount != lines.getCount()) + { + return false; + } + + for (Int i = 0; i < checkLinesCount; ++i) + { + if (lines[i] != checkLines[i]) + { + return false; + } + } + return true; +} + +static bool _checkLines(const UnownedStringSlice& input, const UnownedStringSlice* checkLines, Int checkLinesCount) +{ + List<UnownedStringSlice> lines; + StringUtil::calcLines(input, lines); + return _areEqual(lines, checkLines, checkLinesCount); +} + +static bool _checkLineParser(const UnownedStringSlice& input) +{ + UnownedStringSlice remaining(input), line; + for (const auto parserLine : LineParser(input)) + { + if (!StringUtil::extractLine(remaining, line) || line != parserLine) + { + return false; + } + } + return StringUtil::extractLine(remaining, line) == false; +} + +static void _append(double v, StringBuilder& buf) +{ + std::ostringstream stream; + stream.imbue(std::locale::classic()); + stream.setf(std::ios::fixed, std::ios::floatfield); + stream.precision(20); + + stream << std::scientific << v; + + buf << stream.str().c_str(); +} + +// Unit of least precision +static int64_t _calcULPDistance(double a, double b) +{ + // Save work if the floats are equal. + // Also handles +0 == -0 + if (a == b) + { + return 0; + } + + const int64_t max = int64_t((~uint64_t(0)) >> 1); + +#if 0 + // Max distance for NaN + if (isnan(a) || isnan(b)) + { + return max; + } + + // If one's infinite and they're not equal, max distance. + if (isinf(a) || isinf(b)) + { + return max; + } +#endif + + int64_t ia, ib; + memcpy(&ia, &a, sizeof(a)); + memcpy(&ib, &b, sizeof(b)); + + // Don't compare differently-signed floats. + if ((ia < 0) != (ib < 0)) + { + return max; + } + + // Return the absolute value of the distance in ULPs. + int64_t distance = ia - ib; + return distance < 0 ? -distance : distance; +} + +static bool _areApproximatelyEqual(double a, double b, double fixedEpsilon = 1e-10, int ulpsEpsilon = 100) +{ + // Handle the near-zero case. + const double difference = abs(a - b); + if (difference <= fixedEpsilon) + { + return true; + } + + return _calcULPDistance(a, b) <= ulpsEpsilon; +} + +SLANG_UNIT_TEST(string) +{ + { + UnownedStringSlice checkLines[] = { UnownedStringSlice::fromLiteral("") }; + SLANG_CHECK(_checkLines(UnownedStringSlice::fromLiteral(""), checkLines, SLANG_COUNT_OF(checkLines))); + } + { + // Will emit no lines + SLANG_CHECK(_checkLines(UnownedStringSlice(nullptr, nullptr), nullptr, 0)); + } + { + // Two lines - both empty + UnownedStringSlice checkLines[] = { UnownedStringSlice(), UnownedStringSlice()}; + SLANG_CHECK(_checkLines(UnownedStringSlice::fromLiteral("\n"), checkLines, SLANG_COUNT_OF(checkLines))); + } + { + UnownedStringSlice checkLines[] = { UnownedStringSlice::fromLiteral("Hello"), UnownedStringSlice::fromLiteral("World!") }; + SLANG_CHECK(_checkLines(UnownedStringSlice::fromLiteral("Hello\nWorld!"), checkLines, SLANG_COUNT_OF(checkLines))); + } + { + UnownedStringSlice checkLines[] = { UnownedStringSlice::fromLiteral("Hello"), UnownedStringSlice::fromLiteral("World!"), UnownedStringSlice() }; + SLANG_CHECK(_checkLines(UnownedStringSlice::fromLiteral("Hello\n\rWorld!\n"), checkLines, SLANG_COUNT_OF(checkLines))); + } + + { + SLANG_CHECK(_checkLineParser(UnownedStringSlice::fromLiteral("Hello\n\rWorld!\n"))); + SLANG_CHECK(_checkLineParser(UnownedStringSlice::fromLiteral("\n"))); + SLANG_CHECK(_checkLineParser(UnownedStringSlice::fromLiteral(""))); + } + { + Int value; + SLANG_CHECK(SLANG_SUCCEEDED(StringUtil::parseInt(UnownedStringSlice("-10"), value)) && value == -10); + SLANG_CHECK(SLANG_SUCCEEDED(StringUtil::parseInt(UnownedStringSlice("0"), value)) && value == 0); + SLANG_CHECK(SLANG_SUCCEEDED(StringUtil::parseInt(UnownedStringSlice("-0"), value)) && value == 0); + + SLANG_CHECK(SLANG_SUCCEEDED(StringUtil::parseInt(UnownedStringSlice("13824"), value)) && value == 13824); + SLANG_CHECK(SLANG_SUCCEEDED(StringUtil::parseInt(UnownedStringSlice("-13824"), value)) && value == -13824); + } + + { + UnownedStringSlice values[] = { UnownedStringSlice("hello"), UnownedStringSlice("world"), UnownedStringSlice("!") }; + ArrayView<UnownedStringSlice> valuesView(values, SLANG_COUNT_OF(values)); + + List<UnownedStringSlice> checkValues; + StringBuilder builder; + + { + builder.Clear(); + StringUtil::join(values, 0, ',', builder); + SLANG_CHECK(builder == ""); + } + + { + builder.Clear(); + StringUtil::join(values, 1, ',', builder); + SLANG_CHECK(builder == "hello"); + + StringUtil::split(builder.getUnownedSlice(), ',', checkValues); + SLANG_CHECK(checkValues.getArrayView() == ArrayView<UnownedStringSlice>(values, 1)); + } + + { + builder.Clear(); + StringUtil::join(values, 2, ',', builder); + SLANG_CHECK(builder == "hello,world"); + + StringUtil::split(builder.getUnownedSlice(), ',', checkValues); + SLANG_CHECK(checkValues.getArrayView() == ArrayView<UnownedStringSlice>(values, 2)); + } + + { + builder.Clear(); + StringUtil::join(values, 3, UnownedStringSlice("ab"), builder); + SLANG_CHECK(builder == "helloabworldab!"); + + StringUtil::split(builder.getUnownedSlice(), UnownedStringSlice("ab"), checkValues); + SLANG_CHECK(checkValues.getArrayView() == ArrayView<UnownedStringSlice>(values, 3)); + } + } + { + + List<double> values; + values.add(0.0); + values.add(-0.0); + + for (Index i = -300; i < 300; ++i) + { + double value = pow(10, i); + + values.add(value); + values.add(-value); + + values.addRange(value / 3); + values.addRange(-value / 3); + } + + StringBuilder buf; + + for (auto value : values) + { + buf.Clear(); + _append(value, buf); + + UnownedStringSlice slice = buf.getUnownedSlice(); + + double parsedValue; + SlangResult res = StringUtil::parseDouble(slice, parsedValue); + + auto ulpsParsed = _calcULPDistance(value, parsedValue); + + SLANG_CHECK(SLANG_SUCCEEDED(res)); + + // Check that they are equal + SLANG_CHECK(_areApproximatelyEqual(value, parsedValue)); + } + } + { + List<int64_t> values; + values.add(0); + + for (Index i = 0; i < 63; ++i) + { + auto value = int64_t(1) << i; + + values.add(value); + values.add(-value); + } + + StringBuilder buf; + + for (auto value : values) + { + buf.Clear(); + buf << value; + + + int64_t parsedValue; + + UnownedStringSlice slice = buf.getUnownedSlice(); + SlangResult res = StringUtil::parseInt64(slice, parsedValue); + + SLANG_CHECK(SLANG_SUCCEEDED(res)); + + // Check that they are equal + SLANG_CHECK(value == parsedValue); + } + } +} |
