Skip to content

Commit 4a69788

Browse files
[release/dev17.14] Add serialization tests back (#11695)
Backport of #11694 to release/dev17.14 /cc @DustinCampbell ## Customer Impact None ## Regression - [ ] Yes - [x] No ## Testing Test run succeeds with tests added back in the release/dev17.14 branch. ## Risk Low - test only change.
2 parents e5361d0 + 4ccba78 commit 4a69788

6 files changed

+752
-0
lines changed

src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Microsoft.CodeAnalysis.Razor.Workspaces.Test.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
<PropertyGroup>
44
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
55
<PreserveCompilationContext>true</PreserveCompilationContext>
6+
7+
<!-- To generate baselines, run tests with /p:GenerateJsonFiles=true -->
8+
<DefineConstants Condition="'$(GenerateJsonFiles)'=='true'">$(DefineConstants);GENERATE_JSON_FILES</DefineConstants>
69
</PropertyGroup>
710

811
<ItemGroup>
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT license. See License.txt in the project root for license information.
3+
4+
// Uncomment to easily generate new JSON files
5+
//#define GENERATE_JSON_FILES
6+
7+
using System;
8+
using System.Collections.Immutable;
9+
using System.IO;
10+
using Microsoft.AspNetCore.Razor.Language;
11+
using Microsoft.AspNetCore.Razor.Serialization.Json;
12+
using Microsoft.AspNetCore.Razor.Test.Common;
13+
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
14+
using Xunit;
15+
using Xunit.Abstractions;
16+
17+
///////////////////////////////////////////////////////////////////////////////
18+
//
19+
// Note: The JSON files used for testing are very large. When making
20+
// significant changes to the JSON format for tag helpers or RazorProjectInfo, it
21+
// can be helpful to update the ObjectWriters first and the write new JSON files
22+
// before updating the ObjectReaders. This avoids having to make a series of
23+
// manual edits to the JSON resource files.
24+
//
25+
// 1. Update ObjectWriters to write the new JSON format.
26+
// 2. Uncomment the GENERATE_JSON_FILES #define above.
27+
// 3. Run the GenerateNewJsonFiles test below.
28+
// 4. Update ObjectReaders to read the new JSON format.
29+
// 5. Comment the GENERATE_JSON_FILES #define again.
30+
// 6. Run all of the tests in SerializerValidationTest to ensure that the
31+
// new JSON files deserialize correctly.
32+
//
33+
///////////////////////////////////////////////////////////////////////////////
34+
35+
namespace Microsoft.CodeAnalysis.Razor.Serialization;
36+
37+
public class GenerateJsonFiles(ITestOutputHelper testOutput) : ToolingTestBase(testOutput)
38+
{
39+
#if GENERATE_JSON_FILES
40+
internal static readonly bool ShouldGenerate = true;
41+
#else
42+
internal static readonly bool ShouldGenerate = false;
43+
#endif
44+
45+
// This is to prevent you from accidentally checking in with GenerateJsonFiles = true
46+
[Fact]
47+
public void GenerateJsonFilesMustBeFalse()
48+
{
49+
Assert.False(ShouldGenerate, "GenerateJsonFiles should be set back to false before you check in!");
50+
}
51+
52+
// This updates shared JSON files
53+
#if GENERATE_JSON_FILES
54+
[Theory]
55+
#else
56+
[Theory(Skip = "Run with /p:GenerateJsonFiles=true or uncomment #define GENERATE_JSON_FILES to run this test.")]
57+
#endif
58+
[MemberData(nameof(JsonFiles))]
59+
public void GenerateNewJsonFiles(JsonFile jsonFile)
60+
{
61+
var filePath = Path.Combine([GetSharedFilesRoot(), .. jsonFile.PathParts]);
62+
63+
if (jsonFile.IsRazorProjectInfo)
64+
{
65+
var original = DeserializeProjectInfoFromFile(filePath);
66+
JsonDataConvert.SerializeToFile(original, filePath, indented: true);
67+
}
68+
else
69+
{
70+
var original = DeserializeTagHelperArrayFromFile(filePath);
71+
JsonDataConvert.SerializeToFile(original, filePath, indented: true);
72+
}
73+
}
74+
75+
public readonly record struct JsonFile(string[] PathParts, bool IsRazorProjectInfo)
76+
{
77+
public static JsonFile TagHelpers(params string[] pathParts)
78+
=> new(pathParts, IsRazorProjectInfo: false);
79+
80+
public static JsonFile RazorProjectInfo(params string[] pathParts)
81+
=> new(pathParts, IsRazorProjectInfo: true);
82+
}
83+
84+
public static TheoryData<JsonFile> JsonFiles =>
85+
new()
86+
{
87+
JsonFile.TagHelpers("Compiler", "taghelpers.json"),
88+
JsonFile.TagHelpers("Tooling", "BlazorServerApp.TagHelpers.json"),
89+
JsonFile.TagHelpers("Tooling", "taghelpers.json"),
90+
JsonFile.TagHelpers("Tooling", "Telerik", "Kendo.Mvc.Examples.taghelpers.json"),
91+
JsonFile.RazorProjectInfo("Tooling", "project.razor.json"),
92+
JsonFile.RazorProjectInfo("Tooling", "Telerik", "Kendo.Mvc.Examples.project.razor.json")
93+
};
94+
95+
private static RazorProjectInfo DeserializeProjectInfoFromFile(string filePath)
96+
{
97+
using var reader = new StreamReader(filePath);
98+
return JsonDataConvert.DeserializeProjectInfo(reader);
99+
}
100+
101+
private static ImmutableArray<TagHelperDescriptor> DeserializeTagHelperArrayFromFile(string filePath)
102+
{
103+
using var reader = new StreamReader(filePath);
104+
return JsonDataConvert.DeserializeTagHelperArray(reader);
105+
}
106+
107+
private static string GetSharedFilesRoot()
108+
{
109+
var current = new DirectoryInfo(AppContext.BaseDirectory);
110+
while (current != null && !File.Exists(Path.Combine(current.FullName, "Razor.sln")))
111+
{
112+
current = current.Parent;
113+
}
114+
115+
Assert.NotNull(current);
116+
117+
return Path.Combine(current.FullName, "src", "Shared", "files");
118+
}
119+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT license. See License.txt in the project root for license information.
3+
4+
using System.Linq;
5+
using Microsoft.AspNetCore.Razor.Language;
6+
using Microsoft.AspNetCore.Razor.Serialization.Json;
7+
using Microsoft.AspNetCore.Razor.Test.Common;
8+
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
9+
using Newtonsoft.Json.Linq;
10+
using Xunit;
11+
using Xunit.Abstractions;
12+
13+
namespace Microsoft.CodeAnalysis.Razor.Serialization;
14+
15+
public class JsonSerializationTest(ITestOutputHelper testOutput) : ToolingTestBase(testOutput)
16+
{
17+
private readonly RazorConfiguration _configuration = new(RazorLanguageVersion.Experimental, ConfigurationName: "Custom", [new("TestExtension")]);
18+
19+
private readonly ProjectWorkspaceState _projectWorkspaceState = ProjectWorkspaceState.Create(
20+
tagHelpers: [TagHelperDescriptorBuilder.Create("Test", "TestAssembly").Build()]);
21+
22+
[Fact]
23+
public void RazorProjectInfo_InvalidVersionThrows()
24+
{
25+
// Arrange
26+
var projectInfo = new RazorProjectInfo(
27+
new ProjectKey("/path/to/obj/"),
28+
"/path/to/project.csproj",
29+
_configuration,
30+
rootNamespace: "TestProject",
31+
displayName: "project",
32+
_projectWorkspaceState,
33+
documents: []);
34+
35+
var jsonText = JsonDataConvert.Serialize(projectInfo);
36+
Assert.NotNull(jsonText);
37+
38+
var serializedJObject = JObject.Parse(jsonText);
39+
serializedJObject[WellKnownPropertyNames.Version] = -1;
40+
41+
var updatedJsonText = serializedJObject.ToString();
42+
Assert.NotNull(updatedJsonText);
43+
44+
// Act
45+
RazorProjectInfo? deserializedProjectInfo = null;
46+
Assert.Throws<RazorProjectInfoSerializationException>(() =>
47+
{
48+
deserializedProjectInfo = JsonDataConvert.DeserializeProjectInfo(updatedJsonText);
49+
});
50+
51+
// Assert
52+
Assert.Null(deserializedProjectInfo);
53+
}
54+
55+
[Fact]
56+
public void RazorProjectInfo_MissingVersionThrows()
57+
{
58+
// Arrange
59+
var projectInfo = new RazorProjectInfo(
60+
new ProjectKey("/path/to/obj/"),
61+
"/path/to/project.csproj",
62+
_configuration,
63+
rootNamespace: "TestProject",
64+
displayName: "project",
65+
_projectWorkspaceState,
66+
documents: []);
67+
68+
var jsonText = JsonDataConvert.Serialize(projectInfo);
69+
Assert.NotNull(jsonText);
70+
71+
var serializedJObject = JObject.Parse(jsonText);
72+
serializedJObject.Remove(WellKnownPropertyNames.Version);
73+
74+
var updatedJsonText = serializedJObject.ToString();
75+
Assert.NotNull(updatedJsonText);
76+
77+
// Act
78+
RazorProjectInfo? deserializedProjectInfo = null;
79+
Assert.Throws<RazorProjectInfoSerializationException>(() =>
80+
{
81+
deserializedProjectInfo = JsonDataConvert.DeserializeProjectInfo(updatedJsonText);
82+
});
83+
84+
// Assert
85+
Assert.Null(deserializedProjectInfo);
86+
}
87+
88+
[Fact]
89+
public void RazorProjectInfo_CanRoundTrip()
90+
{
91+
// Arrange
92+
var legacyDocument = new DocumentSnapshotHandle("/path/to/file.cshtml", "file.cshtml", FileKinds.Legacy);
93+
var componentDocument = new DocumentSnapshotHandle("/path/to/otherfile.razor", "otherfile.razor", FileKinds.Component);
94+
var projectInfo = new RazorProjectInfo(
95+
new ProjectKey("/path/to/obj/"),
96+
"/path/to/project.csproj",
97+
_configuration,
98+
rootNamespace: "TestProject",
99+
displayName: "project",
100+
_projectWorkspaceState,
101+
documents: [legacyDocument, componentDocument]);
102+
103+
var jsonText = JsonDataConvert.Serialize(projectInfo);
104+
Assert.NotNull(jsonText);
105+
106+
// Act
107+
var deserializedProjectInfo = JsonDataConvert.DeserializeProjectInfo(jsonText);
108+
Assert.NotNull(deserializedProjectInfo);
109+
110+
// Assert
111+
Assert.Equal(projectInfo.FilePath, deserializedProjectInfo.FilePath);
112+
Assert.Equal(projectInfo.Configuration, deserializedProjectInfo.Configuration);
113+
Assert.Equal(projectInfo.RootNamespace, deserializedProjectInfo.RootNamespace);
114+
Assert.Equal(projectInfo.ProjectWorkspaceState, deserializedProjectInfo.ProjectWorkspaceState);
115+
Assert.Collection(projectInfo.Documents.OrderBy(doc => doc.FilePath),
116+
document =>
117+
{
118+
Assert.Equal(legacyDocument.FilePath, document.FilePath);
119+
Assert.Equal(legacyDocument.TargetPath, document.TargetPath);
120+
Assert.Equal(legacyDocument.FileKind, document.FileKind);
121+
},
122+
document =>
123+
{
124+
Assert.Equal(componentDocument.FilePath, document.FilePath);
125+
Assert.Equal(componentDocument.TargetPath, document.TargetPath);
126+
Assert.Equal(componentDocument.FileKind, document.FileKind);
127+
});
128+
}
129+
130+
[Fact]
131+
public void RazorConfiguration_CanRoundTrip()
132+
{
133+
// Arrange
134+
var jsonText = JsonDataConvert.Serialize(_configuration);
135+
Assert.NotNull(jsonText);
136+
137+
// Act
138+
var deserializedConfiguration = JsonDataConvert.DeserializeConfiguration(jsonText);
139+
140+
// Assert
141+
Assert.Equal(_configuration, deserializedConfiguration);
142+
}
143+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT license. See License.txt in the project root for license information.
3+
4+
using System.Linq;
5+
using MessagePack;
6+
using MessagePack.Resolvers;
7+
using Microsoft.AspNetCore.Razor.Language;
8+
using Microsoft.AspNetCore.Razor.Test.Common;
9+
using Microsoft.CodeAnalysis.Razor.Serialization.MessagePack.Resolvers;
10+
using Xunit;
11+
using Xunit.Abstractions;
12+
13+
namespace Microsoft.CodeAnalysis.Razor.Serialization;
14+
15+
public class ProjectSnapshotHandleSerializationTest(ITestOutputHelper testOutput) : ToolingTestBase(testOutput)
16+
{
17+
private static readonly MessagePackSerializerOptions s_options = MessagePackSerializerOptions.Standard
18+
.WithResolver(CompositeResolver.Create(
19+
ProjectSnapshotHandleResolver.Instance,
20+
StandardResolver.Instance));
21+
22+
[Fact]
23+
public void ProjectSnapshotHandle_Serialization_CanKindaRoundTrip()
24+
{
25+
// Arrange
26+
var projectId = ProjectId.CreateNewId();
27+
var expectedSnapshot = new ProjectSnapshotHandle(
28+
projectId,
29+
new(RazorLanguageVersion.Version_1_1,
30+
"Test",
31+
[new("Test-Extension1"), new("Test-Extension2")],
32+
CodeAnalysis.CSharp.LanguageVersion.CSharp7,
33+
UseConsolidatedMvcViews: false,
34+
SuppressAddComponentParameter: true,
35+
UseRoslynTokenizer: true,
36+
PreprocessorSymbols: ["DEBUG", "TRACE", "DAVID"]),
37+
"Test");
38+
39+
// Act
40+
var bytes = MessagePackConvert.Serialize(expectedSnapshot, s_options);
41+
var actualSnapshot = MessagePackConvert.Deserialize<ProjectSnapshotHandle>(bytes, s_options);
42+
43+
// Assert
44+
Assert.NotNull(actualSnapshot);
45+
Assert.Equal(expectedSnapshot.ProjectId, actualSnapshot.ProjectId);
46+
Assert.NotNull(expectedSnapshot.Configuration);
47+
Assert.NotNull(actualSnapshot.Configuration);
48+
Assert.Equal(expectedSnapshot.Configuration.ConfigurationName, actualSnapshot.Configuration.ConfigurationName);
49+
Assert.Collection(
50+
expectedSnapshot.Configuration.Extensions.OrderBy(e => e.ExtensionName),
51+
e => Assert.Equal("Test-Extension1", e.ExtensionName),
52+
e => Assert.Equal("Test-Extension2", e.ExtensionName));
53+
Assert.Equal(expectedSnapshot.Configuration.LanguageVersion, actualSnapshot.Configuration.LanguageVersion);
54+
Assert.Equal(expectedSnapshot.RootNamespace, actualSnapshot.RootNamespace);
55+
Assert.Equal(expectedSnapshot.Configuration.CSharpLanguageVersion, actualSnapshot.Configuration.CSharpLanguageVersion);
56+
Assert.Equal(expectedSnapshot.Configuration.UseConsolidatedMvcViews, actualSnapshot.Configuration.UseConsolidatedMvcViews);
57+
Assert.Equal(expectedSnapshot.Configuration.SuppressAddComponentParameter, actualSnapshot.Configuration.SuppressAddComponentParameter);
58+
Assert.Equal(expectedSnapshot.Configuration.UseRoslynTokenizer, actualSnapshot.Configuration.UseRoslynTokenizer);
59+
Assert.Collection(actualSnapshot.Configuration.PreprocessorSymbols,
60+
s => Assert.Equal("DEBUG", s),
61+
s => Assert.Equal("TRACE", s),
62+
s => Assert.Equal("DAVID", s));
63+
}
64+
65+
[Fact]
66+
public void ProjectSnapshotHandle_SerializationWithNulls_CanKindaRoundTrip()
67+
{
68+
// Arrange
69+
var projectId = ProjectId.CreateNewId();
70+
var expectedSnapshot = new ProjectSnapshotHandle(projectId, RazorConfiguration.Default, null);
71+
72+
// Act
73+
var bytes = MessagePackConvert.Serialize(expectedSnapshot, s_options);
74+
var actualSnapshot = MessagePackConvert.Deserialize<ProjectSnapshotHandle>(bytes, s_options);
75+
76+
// Assert
77+
Assert.NotNull(actualSnapshot);
78+
Assert.Equal(expectedSnapshot.ProjectId, actualSnapshot.ProjectId);
79+
Assert.NotNull(actualSnapshot.Configuration);
80+
Assert.Null(actualSnapshot.RootNamespace);
81+
}
82+
}

0 commit comments

Comments
 (0)