Skip to content
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,13 @@ Must.BeTrue(!list.Contains(x)); // ✅ Prefer: Must.HaveSameSequence(expectedLi

## 🧭 `FUnit.Directives`

With FUnit.Directives package, you can *include* external file into file-based app project by adding special directive comment `//:funit:include <path to the file>`.
With FUnit.Directives package, you can *include* external file into file-based app project by adding special directive `#warning funit include <path to the file>`.

```cs
#:package FUnit@*
#:package FUnit.Directives@*

//:funit:include ./path/to/external-file.cs
#warning funit include ./path/to/external-file.cs

return FUnit.Run( /* tests depending on 'external-file.cs */ );
```
Expand Down
26 changes: 16 additions & 10 deletions directives/FUnitSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ public void Execute(GeneratorExecutionContext context)
foreach (var syntaxTree in context.Compilation.SyntaxTrees)
{
var root = syntaxTree.GetCompilationUnitRoot();
var comments = root
var directives = root
.DescendantTrivia()
.Where(t => t.IsKind(SyntaxKind.SingleLineCommentTrivia))
.Where(t => t.IsKind(SyntaxKind.WarningDirectiveTrivia))
.ToList(); // ToList is better than ToImmutableList in this case

foreach (var trivia in comments)
foreach (var trivia in directives)
{
if (trivia.SyntaxTree is null)
{
Expand All @@ -72,26 +72,32 @@ public void Execute(GeneratorExecutionContext context)
continue;
}

if (!trivia.ToString().StartsWith(SR.DirectivePrefix, StringComparison.Ordinal))
// Check for no indentation
var line = trivia.GetLocation().GetLineSpan().StartLinePosition.Character;
if (line != 0)
{
continue;
}

// Check for no indentation
var line = trivia.GetLocation().GetLineSpan().StartLinePosition.Character;
if (line != 0)
var fullText = trivia.ToString();
if (!fullText.StartsWith("#warning", StringComparison.Ordinal))
{
continue;
}

var fullText = trivia.ToString().TrimEnd();
// 8 is the length of "#warning"
var textAfterWarning = fullText.Substring(8).TrimStart();
if (!textAfterWarning.StartsWith(SR.DirectivePrefix, StringComparison.Ordinal))
{
continue;
}

#if DEBUG
context.ReportDiagnostic(
Diagnostic.Create(SR.DebugDiagnostic, trivia.GetLocation(), fullText));
Diagnostic.Create(SR.DebugDiagnostic, trivia.GetLocation(), fullText.TrimEnd()));
#endif

var keywordAndArgs = fullText.Substring(SR.DirectivePrefix.Length);
var keywordAndArgs = textAfterWarning.Substring(SR.DirectivePrefix.Length);

var parts = keywordAndArgs.Split(SR.DirectiveSeparators, 2, StringSplitOptions.RemoveEmptyEntries);
var keyword = parts.FirstOrDefault()?.Trim() ?? string.Empty;
Expand Down
4 changes: 2 additions & 2 deletions directives/SR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ namespace FUnit.Directives
{
internal class SR
{
public const string DirectivePrefix = "//:funit:";
public const string DirectivePrefix = "funit";
public const string DiagnosticCategory = nameof(FUnit);

public static readonly char[] DirectiveSeparators = new[] { ' ' };
public static readonly char[] DirectiveSeparators = new[] { ' ', '\t' };
public static readonly char[] InvalidChars = new[] { '/', '\\', ':' }; // File.Exists will reject invalid path so remove only directory separators

public static DiagnosticDescriptor MissingFileNameDiagnostic = new(
Expand Down
30 changes: 15 additions & 15 deletions sandbox/Sandbox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,28 @@
/* uncomment to test FUnit.Directives

// multiple include of same file should be allowed
//:funit:include Sandbox.cs
//:funit:include Sandbox.cs
//:funit:include ./Sandbox.cs
//:funit:include ./Sandbox.cs

// IGNORED: prefix must be single line comment and placed at line beginning
// //:funit:
///:funit:
#warning funit include Sandbox.cs
#warning funit include Sandbox.cs
#warning funit include ./Sandbox.cs
#warning funit include ./Sandbox.cs

// IGNORED: prefix must be #warning funit and placed at line beginning
// #warning funit
//#warning funit
// leading space is not allowed
//:funit:
#warning funit

// ERRORS
//:funit:include
//:funit:include NotFound.cs
//:funit:unknown
//:funit:
//:funit:include file not supported
#warning funit include
#warning funit include NotFound.cs
#warning funit unknown
#warning funit
#warning funit include file not supported

*/


// IGNORED: in multiline comment
/*
//:funit:
#warning funit
*/
16 changes: 11 additions & 5 deletions test/Directives_test.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
#:project ../src
#:package FUnit.Directives@*

#if true
return 0;
#else

// cannot...? --> #:project ../directives

// [TEST] allow multiple include directives scattered in project
//:funit:include Directives_TestClass.cs
//:funit:include Directives_TestClass.cs
#warning funit include Directives_TestClass.cs
#warning funit include Directives_TestClass.cs

// [TEST] no duplicate even if same file is specified in different way
//:funit:include ./Directives_TestClass.cs
//:funit:include ./Directives_TestClass.cs
#warning funit include ./Directives_TestClass.cs
#warning funit include ./Directives_TestClass.cs

#warning THIS WARNING IS EMITTED BY PREPROCESSOR DIRECTIVE

return FUnit.Run(args, describe =>
{
describe("FUnit.Directives", it =>
{
it("should work (:funit:include)", () =>
it("should work (#warning funit include)", () =>
{
Must.BeEqual(310, Tests.TestClass.TestMethod());
});
});
});

#endif