Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize and harden docs exporter #2082

Merged
merged 5 commits into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/Sarif.Driver/DriverResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Sarif.Driver/DriverResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@
<data name="NonOwningDisposableViewAccess" xml:space="preserve">
<value>Attempted to access a DisposableEnumerable which is non-owning.</value>
</data>
<data name="NoRuleDescription" xml:space="preserve">
<value>No description available.</value>
</data>
<data name="PREfastDefectBuilderCannotFreeze" xml:space="preserve">
<value>Invalid PREfast defect state.</value>
</data>
Expand Down
22 changes: 18 additions & 4 deletions src/Sarif.Driver/Sdk/ExportRulesDocumentationCommandBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Microsoft.CodeAnalysis.Sarif.Driver
{
public class ExportRulesDocumentationCommandBase<TContext> : PlugInDriverCommand<ExportRulesDocumentationOptions>
{
private const string DefaultOutputFileName = "Rules.md";
private readonly IFileSystem _fileSystem;
private static readonly Regex s_friendlyNameRegex = new Regex(@"(?<level>Error|Warning|Note|None)_(?<friendlyName>[^_]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

public ExportRulesDocumentationCommandBase(IFileSystem fileSystem = null)
{
Expand All @@ -31,7 +34,7 @@ public override int Run(ExportRulesDocumentationOptions options)
BuildRule(rule, sb);
}

_fileSystem.WriteAllText(options.OutputFilePath, sb.ToString());
_fileSystem.WriteAllText(options.OutputFilePath ?? DefaultOutputFileName, sb.ToString());
}
catch (Exception ex)
{
Expand All @@ -46,13 +49,24 @@ internal void BuildRule(Skimmer<TContext> rule, StringBuilder sb)
{
sb.AppendLine($"## Rule `{rule.Moniker}`{Environment.NewLine}");
sb.AppendLine($"### Description{Environment.NewLine}");
sb.AppendLine($"{rule.FullDescription.Text}{Environment.NewLine}");
sb.AppendLine(@$"{rule.FullDescription?.Markdown
?? rule.FullDescription?.Text
?? rule.ShortDescription?.Markdown
?? rule.ShortDescription?.Text
?? DriverResources.NoRuleDescription}{Environment.NewLine}");
sb.AppendLine($"### Messages{Environment.NewLine}");

foreach (KeyValuePair<string, MultiformatMessageString> message in rule.MessageStrings)
{
sb.AppendLine($"#### `{message.Key.Split('_').Last()}`: {rule.DefaultLevel}{Environment.NewLine}");
sb.AppendLine($"{message.Value.Text}{Environment.NewLine}");
string ruleName = message.Key;
Match match = s_friendlyNameRegex.Match(message.Key);
if (match.Success)
{
ruleName = match.Groups["friendlyName"].Value;
}

sb.AppendLine($"#### `{ruleName}`: {rule.DefaultLevel}{Environment.NewLine}");
sb.AppendLine($"{message.Value.Markdown ?? message.Value.Text}{Environment.NewLine}");
}

sb.AppendLine($"---{Environment.NewLine}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,30 @@ public class ExportValidationRulesDocumentationCommandTests
[Fact]
public void BuildRule_GeneratesExpectedMarkdown()
{
var tests = new Dictionary<string, SarifValidationSkimmerBase>
{
{ "MarkdownFullDescription.md", new TestRule4() },
{ "MarkdownShortDescription.md", new TestRule5() },
{ "NoDescription.md", new TestRule3() },
{ "NonStandardMessageStringKey.md", new TestRule6() },
{ "StandardMessageStringKey.md", new TestRule1() },
{ "TextShortDescription.md", new TestRule2() },
};

var resourceExtractor = new ResourceExtractor(this.GetType());
var sb = new StringBuilder();
var testRule = new TestRule();
var command = new ExportValidationDocumentationCommand();
command.BuildRule(testRule, sb);
foreach (KeyValuePair<string, SarifValidationSkimmerBase> test in tests)
{
var sb = new StringBuilder();
var command = new ExportValidationDocumentationCommand();
command.BuildRule(test.Value, sb);

string expectedMarkdown = resourceExtractor.GetResourceText("Test.md");
string expectedMarkdown = resourceExtractor.GetResourceText(test.Key);

sb.ToString().Should().Be(expectedMarkdown);
sb.ToString().Should().Be(expectedMarkdown);
}
}

private class TestRule : SarifValidationSkimmerBase
private class TestRule1 : SarifValidationSkimmerBase
{
public override string Id => "TEST0001";
public override string Name => "TEST";
Expand All @@ -36,5 +48,68 @@ private class TestRule : SarifValidationSkimmerBase
{ "TEST0001_TEST_Note_Default", new MultiformatMessageString{ Text="default text"} }
};
}

private class TestRule2 : SarifValidationSkimmerBase
{
public override string Id => "TEST0002";
public override string Name => "TEST2";
public override FailureLevel DefaultLevel => FailureLevel.Note;
public override MultiformatMessageString FullDescription => null;
public override MultiformatMessageString ShortDescription => new MultiformatMessageString { Text = "short description text" };
public override IDictionary<string, MultiformatMessageString> MessageStrings => new Dictionary<string, MultiformatMessageString>
{
{ "TEST0002_TEST2_Note_Default", new MultiformatMessageString{ Markdown = "# markdown example" } }
};
}

private class TestRule3 : SarifValidationSkimmerBase
{
public override string Id => "TEST0003";
public override string Name => "TEST3";
public override FailureLevel DefaultLevel => FailureLevel.Note;
public override MultiformatMessageString FullDescription => null;
public override MultiformatMessageString ShortDescription => null;
public override IDictionary<string, MultiformatMessageString> MessageStrings => new Dictionary<string, MultiformatMessageString>
{
{ "TEST0003_TEST3_Note_Default", new MultiformatMessageString{ Text="default text"} }
};
}

private class TestRule4 : SarifValidationSkimmerBase
{
public override string Id => "TEST0004";
public override string Name => "TEST";
public override FailureLevel DefaultLevel => FailureLevel.Note;
public override MultiformatMessageString FullDescription => new MultiformatMessageString { Text = "full description text", Markdown = "markdown text" };
public override IDictionary<string, MultiformatMessageString> MessageStrings => new Dictionary<string, MultiformatMessageString>
{
{ "TEST0004_TEST_Note_Default", new MultiformatMessageString{ Text = "default text"} }
};
}

private class TestRule5 : SarifValidationSkimmerBase
{
public override string Id => "TEST0005";
public override string Name => "TEST";
public override FailureLevel DefaultLevel => FailureLevel.Note;
public override MultiformatMessageString FullDescription => null;
public override MultiformatMessageString ShortDescription => new MultiformatMessageString { Text = "short description text", Markdown = "markdown text" };
public override IDictionary<string, MultiformatMessageString> MessageStrings => new Dictionary<string, MultiformatMessageString>
{
{ "TEST0005_TEST_Note_Default", new MultiformatMessageString{ Markdown = "# markdown example" } }
};
}

private class TestRule6 : SarifValidationSkimmerBase
{
public override string Id => "TEST0006";
public override string Name => "TEST";
public override FailureLevel DefaultLevel => FailureLevel.Note;
public override MultiformatMessageString FullDescription => new MultiformatMessageString { Text = "full description text" };
public override IDictionary<string, MultiformatMessageString> MessageStrings => new Dictionary<string, MultiformatMessageString>
{
{ "Default_Note_TEST_TEST0006", new MultiformatMessageString{ Text = "default text" } }
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@

<ItemGroup>
<None Remove="TestData\ConvertCommand\SemmleQlSample.csv" />
<None Remove="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\Test.md" />
<None Remove="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\MarkdownFullDescription.md" />
<None Remove="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\MarkdownShortDescription.md" />
<None Remove="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\NoDescription.md" />
<None Remove="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\NonStandardMessageStringKey.md" />
<None Remove="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\StandardMessageStringKey.md" />
<None Remove="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\TextShortDescription.md" />
<None Remove="TestData\MergeCommand\ExpectedOutputs\NoInputFiles.sarif" />
<None Remove="TestData\PageCommand\elfie-arriba - Copy.sarif" />
<None Remove="TestData\PageCommand\elfie-arriba.sarif" />
Expand All @@ -25,7 +30,12 @@

<ItemGroup>
<EmbeddedResource Include="TestData\ConvertCommand\SemmleQlSample.csv" />
<EmbeddedResource Include="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\Test.md" />
<EmbeddedResource Include="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\NonStandardMessageStringKey.md" />
<EmbeddedResource Include="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\StandardMessageStringKey.md" />
<EmbeddedResource Include="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\TextShortDescription.md" />
<EmbeddedResource Include="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\MarkdownShortDescription.md" />
<EmbeddedResource Include="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\MarkdownFullDescription.md" />
<EmbeddedResource Include="TestData\ExportRuleDocumentationCommand\ExpectedOutputs\NoDescription.md" />
<EmbeddedResource Include="TestData\MergeCommand\ExpectedOutputs\NoInputFiles.sarif" />
<EmbeddedResource Include="TestData\QueryCommand\elfie-arriba.CSCAN0020.sarif" />
<EmbeddedResource Include="TestData\PageCommand\elfie-arriba.sarif" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Rule `TEST0004.TEST`

### Description

markdown text

### Messages

#### `Default`: Note

default text

---

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Rule `TEST0005.TEST`

### Description

markdown text

### Messages

#### `Default`: Note

# markdown example

---

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Rule `TEST0003.TEST3`

### Description

No description available.

### Messages

#### `Default`: Note

default text

---

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Rule `TEST0006.TEST`

### Description

full description text

### Messages

#### `Default_Note_TEST_TEST0006`: Note

default text

---

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Rule `TEST0002.TEST2`

### Description

short description text

### Messages

#### `Default`: Note

# markdown example

---