From b6de9a0091ab6a7414b46c7eb50f25b9512fb455 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Mon, 15 Mar 2021 12:48:20 -0400 Subject: Test Doc System (#1754) * #include an absolute path didn't work - because paths were taken to always be relative. * Use capability system in docs. Simplify how requirements/availability is produced. * Small fixes in output of availablity. * Updated stdlib doc. * Small improvements. * Added doc test type. Improved readability of straight .md text Made -doc option output to diagnostic stream. * Add test for checking requirements info is correctly extracted. Co-authored-by: Tim Foley --- source/slang/slang-doc-markdown-writer.cpp | 13 +- source/slang/slang.cpp | 28 +-- tests/doc/doc-req.slang | 15 ++ tests/doc/doc-req.slang.expected | 18 ++ tests/doc/doc.slang | 2 +- tests/doc/doc.slang.expected | 375 +++++++++++++++++++++++++++++ tools/slang-test/slang-test-main.cpp | 66 +++++ 7 files changed, 496 insertions(+), 21 deletions(-) create mode 100644 tests/doc/doc-req.slang create mode 100644 tests/doc/doc-req.slang.expected create mode 100644 tests/doc/doc.slang.expected diff --git a/source/slang/slang-doc-markdown-writer.cpp b/source/slang/slang-doc-markdown-writer.cpp index 5105b0b26..c2bc05dbb 100644 --- a/source/slang/slang-doc-markdown-writer.cpp +++ b/source/slang/slang-doc-markdown-writer.cpp @@ -830,6 +830,8 @@ void DocMarkdownWriter::writeCallableOverridable(const DocMarkup::Entry& entry, _appendAsBullets(_getUniqueParams(genericDecls), '`'); // And parameters _appendAsBullets(_getUniqueParams(paramDecls), '`'); + + out << toSlice("\n"); } } } @@ -1034,8 +1036,9 @@ void DocMarkdownWriter::writeAggType(const DocMarkup::Entry& entry, AggTypeDeclB if (params.getCount()) { - out << "## Generic Parameters\n\n"; + out << toSlice("## Generic Parameters\n\n"); _appendAsBullets(_getAsNameAndTextList(params), '`'); + out << toSlice("\n"); } } @@ -1044,9 +1047,9 @@ void DocMarkdownWriter::writeAggType(const DocMarkup::Entry& entry, AggTypeDeclB _getDeclsOfType(aggTypeDecl, fields); if (fields.getCount()) { - out << "## Fields\n\n"; - + out << toSlice("## Fields\n\n"); _appendAsBullets(_getAsNameAndTextList(fields), '`'); + out << toSlice("\n"); } } @@ -1072,6 +1075,7 @@ void DocMarkdownWriter::writeAggType(const DocMarkup::Entry& entry, AggTypeDeclB out << "## Methods\n\n"; _appendAsBullets(_getAsStringList(uniqueMethods), '`'); + out << toSlice("\n"); } } } @@ -1081,7 +1085,8 @@ void DocMarkdownWriter::writePreamble(const DocMarkup::Entry& entry) SLANG_UNUSED(entry); auto& out = m_builder; - out << toSlice("\n"); + //out << toSlice("\n"); + out.appendRepeatedChar('-', 80); out << toSlice("\n"); } diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index ae3a1f419..93d7be310 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -1843,26 +1843,22 @@ SlangResult FrontEndCompileRequest::executeActionsInner() // cause any problems with scoping ASTBuilder* astBuilder = getLinkage()->getASTBuilder(); - for (TranslationUnitRequest* translationUnit : translationUnits) - { - RefPtr markup(new DocMarkup); - DocMarkupExtractor::extract(translationUnit->getModuleDecl(), getSourceManager(), getSink(), markup); + ISlangWriter* writer = getSink()->writer; - // Hmm.. we can have multiple sourcefiles. So fir now we just pick the first, so as to come up with - // a reasonable name - SourceFile* sourceFile = translationUnit->getSourceFiles()[0]; - - // Extract to a file - const String& path = sourceFile->getPathInfo().foundPath; - if (path.getLength()) + // Write output to the diagnostic writer + if (writer) + { + for (TranslationUnitRequest* translationUnit : translationUnits) { - String fileName = Path::getFileNameWithoutExt(path); - fileName.append(".md"); + RefPtr markup(new DocMarkup); + DocMarkupExtractor::extract(translationUnit->getModuleDecl(), getSourceManager(), getSink(), markup); - DocMarkdownWriter writer(markup, astBuilder); - writer.writeAll(); + // Convert to markdown + DocMarkdownWriter markdownWriter(markup, astBuilder); + markdownWriter.writeAll(); - File::writeAllText(fileName, writer.getOutput()); + UnownedStringSlice docText = markdownWriter.getOutput().getUnownedSlice(); + writer->write(docText.begin(), docText.getLength()); } } } diff --git a/tests/doc/doc-req.slang b/tests/doc/doc-req.slang new file mode 100644 index 000000000..bee11f7d4 --- /dev/null +++ b/tests/doc/doc-req.slang @@ -0,0 +1,15 @@ +//TEST:DOC:-module-name module -doc -no-codegen + +// To Test out documentation around intrinsics + +__target_intrinsic(hlsl, "someHLSLThing()") +[__requiresNVAPI] +__cuda_sm_version(2.0) +__target_intrinsic(cuda, "someCUDAThing()") +__target_intrinsic(glsl, "someGLSLThing()") +__glsl_extension(GL_EXT_NiftyExtension) +__spirv_version(1.3) +__target_intrinsic(cpp, "someCPPThing()") +void doThing() +{ +} diff --git a/tests/doc/doc-req.slang.expected b/tests/doc/doc-req.slang.expected new file mode 100644 index 000000000..51421cdc7 --- /dev/null +++ b/tests/doc/doc-req.slang.expected @@ -0,0 +1,18 @@ +result code = 0 +standard error = { +-------------------------------------------------------------------------------- +# `doThing` + +## Signature + +``` +void doThing(); +``` + +## Availability + +**GLSL** `GL_EXT_NiftyExtension`, `SPIR-V 1.3` **HLSL** `NVAPI` **CPP** **CUDA** `SM 2.0` + +} +standard output = { +} diff --git a/tests/doc/doc.slang b/tests/doc/doc.slang index 447b6f6f3..1e04171a0 100644 --- a/tests/doc/doc.slang +++ b/tests/doc/doc.slang @@ -1,4 +1,4 @@ -//DISABLE_TEST:SIMPLE:-entry computeMain -target spirv -stage compute -doc +//TEST:DOC:-entry computeMain -target hlsl -stage compute -doc -no-codegen void outFunc(out int v) { diff --git a/tests/doc/doc.slang.expected b/tests/doc/doc.slang.expected new file mode 100644 index 000000000..95b3381b5 --- /dev/null +++ b/tests/doc/doc.slang.expected @@ -0,0 +1,375 @@ +result code = 0 +standard error = { +-------------------------------------------------------------------------------- +# `outFunc` + +## Signature + +``` +void outFunc(out int v); +``` + +## Parameters + +* `v` + +-------------------------------------------------------------------------------- +# `struct ParentStruct` + +## Description + +Testing out nested generics + +## Generic Parameters + +* `T` Some type + +-------------------------------------------------------------------------------- +# `struct ParentStruct.ChildStruct` + +## Description + +Testing out a child + +## Generic Parameters + +* `S` Some other type + +## Methods + +* `getValue` + +-------------------------------------------------------------------------------- +# `ParentStruct.ChildStruct.getValue` + +## Description + +A useless method hey ho + +## Signature + +``` +T ParentStruct.ChildStruct.getValue(S v); +``` + +## Parameters + +* `v` + +-------------------------------------------------------------------------------- +# `struct GenericStruct` + +## Generic Parameters + +* `T` + +## Fields + +* `value` + +## Methods + +* `getValue` + +-------------------------------------------------------------------------------- +# `GenericStruct.getValue` + +## Signature + +``` +T GenericStruct.getValue(); +``` + +-------------------------------------------------------------------------------- +# `addInts` + +## Description + +A rather silly generic function to test out doc extraction + +## Signature + +``` +T addInts( + T z, + T b); +``` + +## Parameters + +* `T` The type we are operating on +* `U` Just testing out a non type based generic +* `V` And another one +* `z` CHECKING!! +* `b` The B parameter + +-------------------------------------------------------------------------------- +# `Hey::doAnotherThing` + +## Signature + +``` +void Hey::doAnotherThing(int a); +``` + +## Parameters + +* `a` + +-------------------------------------------------------------------------------- +# inputBuffer + +``` +RWStructuredBuffer inputBuffer +``` + +## Description + +Let's test indent + +``` +{ + imIndented(); +} +``` + +-------------------------------------------------------------------------------- +# `interface IDoThing` + +## Description + +An interface to do things + +# Associated types + +* _V_ An associated type + + +## Methods + +* `add` +* `sub` + +-------------------------------------------------------------------------------- +# `IDoThing.add` + +## Description + +Add two integers + +## Signature + +``` +V IDoThing.add( + V a, + V b); +``` + +## Parameters + +* `a` First parameter +* `b` Second parameter + +-------------------------------------------------------------------------------- +# `IDoThing.sub` + +## Description + +Subtract +Multi-line + +## Signature + +``` +int IDoThing.sub( + int a, + int b); +``` + +## Parameters + +* `a` First +* `b` Second + +-------------------------------------------------------------------------------- +# `interface IThing` + +## Methods + +* `getValue` + +-------------------------------------------------------------------------------- +# `IThing.getValue` + +## Signature + +``` +float IThing.getValue(); +``` + +-------------------------------------------------------------------------------- +# `extension float : IThing` + +*Implements:* `IThing` + +## Description + +Implement IThing on float + +## Methods + +* `getValue` + +-------------------------------------------------------------------------------- +# `float.getValue` + +## Description + +Just return the float itself! + +## Signature + +``` +float float.getValue(); +``` + +-------------------------------------------------------------------------------- +# `struct Thing` + +*Implements:* `IThing`, `IDoThing` + +## Methods + +* `add` +* `sub` +* `getValue` + +-------------------------------------------------------------------------------- +# `Thing.add` + +## Signature + +``` +int Thing.add( + int a, + int b); +``` + +## Parameters + +* `a` +* `b` + +-------------------------------------------------------------------------------- +# `Thing.sub` + +## Signature + +``` +int Thing.sub( + int a, + int b); +``` + +## Parameters + +* `a` +* `b` + +-------------------------------------------------------------------------------- +# `Thing.getValue` + +## Signature + +``` +float Thing.getValue(); +``` + +-------------------------------------------------------------------------------- +# `struct SomeStruct` + +## Description + +A struct with some fields + +## Fields + +* `aField` A field +* `anotherField` Multi-line is a thing +* `yetAnother` A field with stuff + +## Methods + +* `getMethod` + +-------------------------------------------------------------------------------- +# `SomeStruct.getMethod` + +## Description + +Get a value + +## Signature + +``` +int SomeStruct.getMethod(); +``` + +-------------------------------------------------------------------------------- +# enum AnEnum + +## Values + +* _Value_ A value +* _AnotherValue_ Another value With a multi-line comment +## Description + +An enum + +-------------------------------------------------------------------------------- +# outputBuffer + +``` +RWStructuredBuffer outputBuffer +``` + +## Description + +An output buffer + +-------------------------------------------------------------------------------- +# `doThing` + +## Description + +doThing! + +## Signature + +``` +int doThing( + int a, + int b); +``` + +## Parameters + +* `a` a parameter +* `b` b parameter + +-------------------------------------------------------------------------------- +# `computeMain` + +## Signature + +``` +void computeMain(vector dispatchThreadID); +``` + +## Parameters + +* `dispatchThreadID` + +} +standard output = { +} diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index b49dedd2d..8586ed108 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -1065,6 +1065,71 @@ static bool _areResultsEqual(TestOptions::Type type, const String& a, const Stri } } +TestResult runDocTest(TestContext* context, TestInput& input) +{ + // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect + auto outputStem = input.outputStem; + + CommandLine cmdLine; + _initSlangCompiler(context, cmdLine); + + cmdLine.addArg(input.filePath); + + for (auto arg : input.testOptions->args) + { + cmdLine.addArg(arg); + } + + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); + + if (context->isCollectingRequirements()) + { + return TestResult::Pass; + } + + String actualOutput = getOutput(exeRes); + + String expectedOutputPath = outputStem + ".expected"; + String expectedOutput; + try + { + expectedOutput = Slang::File::readAllText(expectedOutputPath); + } + catch (const Slang::IOException&) + { + } + + // If no expected output file was found, then we + // expect everything to be empty + if (expectedOutput.getLength() == 0) + { + expectedOutput = "result code = 0\nstandard error = {\n}\nstandard output = {\n}\n"; + } + + TestResult result = TestResult::Pass; + + // Otherwise we compare to the expected output + if (!_areResultsEqual(input.testOptions->type, expectedOutput, actualOutput)) + { + context->reporter->dumpOutputDifference(expectedOutput, actualOutput); + result = TestResult::Fail; + } + + // If the test failed, then we write the actual output to a file + // so that we can easily diff it from the command line and + // diagnose the problem. + if (result == TestResult::Fail) + { + String actualOutputPath = outputStem + ".actual"; + Slang::File::writeAllText(actualOutputPath, actualOutput); + + context->reporter->dumpOutputDifference(expectedOutput, actualOutput); + } + + return result; +} + TestResult runSimpleTest(TestContext* context, TestInput& input) { // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect @@ -2740,6 +2805,7 @@ static const TestCommandInfo s_testCommandInfos[] = { "CPP_COMPILER_COMPILE", &runCPPCompilerCompile, RenderApiFlag::CPU}, { "PERFORMANCE_PROFILE", &runPerformanceProfile, 0 }, { "COMPILE", &runCompile, 0 }, + { "DOC", &runDocTest, 0 }, }; const TestCommandInfo* _findTestCommandInfoByCommand(const UnownedStringSlice& name) -- cgit v1.2.3