forked from Azure/bicep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ParserTests.cs
147 lines (124 loc) · 5.56 KB
/
ParserTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Bicep.Core.Extensions;
using Bicep.Core.Navigation;
using Bicep.Core.UnitTests.Assertions;
using Bicep.Core.Parsing;
using Bicep.Core.Samples;
using Bicep.Core.Syntax;
using Bicep.Core.UnitTests.Utils;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
namespace Bicep.Core.IntegrationTests
{
[TestClass]
public class ParserTests
{
[NotNull]
public TestContext? TestContext { get; set; }
[DataTestMethod]
[DynamicData(nameof(GetData), DynamicDataSourceType.Method, DynamicDataDisplayNameDeclaringType = typeof(DataSet), DynamicDataDisplayName = nameof(DataSet.GetDisplayName))]
public void FilesShouldRoundTripSuccessfully(DataSet dataSet)
{
RunRoundTripTest(dataSet.Bicep);
}
[DataTestMethod]
[DynamicData(nameof(GetData), DynamicDataSourceType.Method, DynamicDataDisplayNameDeclaringType = typeof(DataSet), DynamicDataDisplayName = nameof(DataSet.GetDisplayName))]
public void FileTreeNodesShouldHaveConsistentSpans(DataSet dataSet)
{
RunSpanConsistencyTest(dataSet.Bicep);
}
[DataTestMethod]
[DataRow("")]
[DataRow("param")]
[DataRow("param\r\n")]
[DataRow("param ")]
[DataRow("param foo")]
[DataRow("param foo bar")]
[DataRow("param foo bar =")]
[DataRow("param foo bar = 1")]
public void Oneliners_ShouldRoundTripSuccessfully(string contents)
{
RunRoundTripTest(contents);
RunSpanConsistencyTest(contents);
}
[DataTestMethod]
[DynamicData(nameof(GetData), DynamicDataSourceType.Method, DynamicDataDisplayNameDeclaringType = typeof(DataSet), DynamicDataDisplayName = nameof(DataSet.GetDisplayName))]
[TestCategory(BaselineHelper.BaselineTestCategory)]
public void Parser_should_produce_expected_syntax(DataSet dataSet)
{
var program = ParserHelper.Parse(dataSet.Bicep);
var syntaxList = SyntaxCollectorVisitorHelper.SyntaxCollectorVisitor.Build(program);
var syntaxByParent = syntaxList.ToLookup(x => x.Parent);
string getLoggingString(SyntaxCollectorVisitorHelper.SyntaxCollectorVisitor.SyntaxItem data)
{
// Build a visual graph with lines to help understand the syntax hierarchy
var graphPrefix = "";
if (data.Depth > 0)
{
var lastSibling = syntaxByParent[data.Parent].Last();
var isLast = data.Syntax == lastSibling.Syntax;
graphPrefix = string.Concat(Enumerable.Repeat("| ", data.Depth - 1));
graphPrefix += isLast switch {
true => "└─",
_ => "├─",
};
}
return data.Syntax switch {
Token token => $"{graphPrefix}Token({token.Type}) |{OutputHelper.EscapeWhitespace(token.Text)}|",
_ => $"{graphPrefix}{data.Syntax.GetType().Name}",
};
}
TextSpan getSpan(SyntaxCollectorVisitorHelper.SyntaxCollectorVisitor.SyntaxItem data) => data.Syntax.Span;
var sourceTextWithDiags = DataSet.AddDiagsToSourceText(dataSet, syntaxList, getSpan, getLoggingString);
var resultsFile = FileHelper.SaveResultFile(this.TestContext, Path.Combine(dataSet.Name, DataSet.TestFileMainSyntax), sourceTextWithDiags);
sourceTextWithDiags.Should().EqualWithLineByLineDiffOutput(
TestContext,
dataSet.Syntax,
expectedLocation: DataSet.GetBaselineUpdatePath(dataSet, DataSet.TestFileMainSyntax),
actualLocation: resultsFile);
}
private static IEnumerable<object[]> GetData()
{
return DataSets.AllDataSets.ToDynamicTestData();
}
private static void RunRoundTripTest(string contents)
{
var program = ParserHelper.Parse(contents);
program.Should().BeOfType<ProgramSyntax>();
program.ToTextPreserveFormatting().Should().Be(contents);
}
private static void RunSpanConsistencyTest(string text)
{
var program = ParserHelper.Parse(text);
program.Should().BeOfType<ProgramSyntax>();
var visitor = new SpanConsistencyVisitor();
visitor.Visit(program);
}
private sealed class SpanConsistencyVisitor : SyntaxVisitor
{
private int maxPosition = 0;
protected override void VisitInternal(SyntaxBase node)
{
// trivia cause the spans of any particular node to be discontinuous
// but should be consistently increasing as we visit the tree
var span = node.Span;
span.Position.Should().BeGreaterOrEqualTo(this.maxPosition);
this.maxPosition = node.Span.Position;
base.VisitInternal(node);
this.maxPosition.Should().Be(span.GetEndPosition());
}
public override void VisitToken(Token token)
{
base.VisitToken(token);
var span = token.Span;
span.Position.Should().BeGreaterOrEqualTo(this.maxPosition);
this.maxPosition = span.GetEndPosition();
}
}
}
}