File: Syntax\SerializationTests.cs
Web Access
Project: ..\..\..\src\Compilers\CSharp\Test\Syntax\Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class SerializationTests
    {
        [Fact]
        public void RoundTripSyntaxNode()
        {
            var text = "public class C {}";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            var root = tree.GetCompilationUnitRoot();
 
            var stream = new MemoryStream();
            root.SerializeTo(stream);
 
            stream.Position = 0;
 
            var droot = CSharpSyntaxNode.DeserializeFrom(stream);
            var dtext = droot.ToFullString();
 
            Assert.True(droot.IsEquivalentTo(tree.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void RoundTripSyntaxNodeWithDiagnostics()
        {
            var text = "public class C {";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            var root = tree.GetCompilationUnitRoot();
            Assert.Equal(1, root.Errors().Length);
 
            var stream = new MemoryStream();
            root.SerializeTo(stream);
 
            stream.Position = 0;
 
            var droot = CSharpSyntaxNode.DeserializeFrom(stream);
            var dtext = droot.ToFullString();
 
            Assert.Equal(text, dtext);
            Assert.Equal(1, droot.Errors().Length);
            Assert.True(droot.IsEquivalentTo(tree.GetCompilationUnitRoot()));
            Assert.Equal(root.Errors()[0].GetMessage(), droot.Errors()[0].GetMessage());
        }
 
        [Fact]
        public void RoundTripSyntaxNodeWithAnnotation()
        {
            var text = "public class C {}";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            var annotation = new SyntaxAnnotation();
            var root = tree.GetCompilationUnitRoot().WithAdditionalAnnotations(annotation);
            Assert.True(root.ContainsAnnotations);
            Assert.True(root.HasAnnotation(annotation));
 
            var stream = new MemoryStream();
            root.SerializeTo(stream);
 
            stream.Position = 0;
 
            var droot = CSharpSyntaxNode.DeserializeFrom(stream);
            var dtext = droot.ToFullString();
 
            Assert.Equal(text, dtext);
            Assert.True(droot.ContainsAnnotations);
            Assert.True(droot.HasAnnotation(annotation));
            Assert.True(droot.IsEquivalentTo(tree.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void RoundTripSyntaxNodeWithMultipleReferencesToSameAnnotation()
        {
            var text = "public class C {}";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            var annotation = new SyntaxAnnotation();
            var root = tree.GetCompilationUnitRoot().WithAdditionalAnnotations(annotation, annotation);
            Assert.True(root.ContainsAnnotations);
            Assert.True(root.HasAnnotation(annotation));
 
            var stream = new MemoryStream();
            root.SerializeTo(stream);
 
            stream.Position = 0;
 
            var droot = CSharpSyntaxNode.DeserializeFrom(stream);
            var dtext = droot.ToFullString();
 
            Assert.Equal(text, dtext);
            Assert.True(droot.ContainsAnnotations);
            Assert.True(droot.HasAnnotation(annotation));
            Assert.True(droot.IsEquivalentTo(tree.GetCompilationUnitRoot()));
        }
 
        [Fact]
        public void RoundTripSyntaxNodeWithSpecialAnnotation()
        {
            var text = "public class C {}";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            var annotation = new SyntaxAnnotation("TestAnnotation", "this is a test");
            var root = tree.GetCompilationUnitRoot().WithAdditionalAnnotations(annotation);
            Assert.True(root.ContainsAnnotations);
            Assert.True(root.HasAnnotation(annotation));
 
            var stream = new MemoryStream();
            root.SerializeTo(stream);
 
            stream.Position = 0;
 
            var droot = CSharpSyntaxNode.DeserializeFrom(stream);
            var dtext = droot.ToFullString();
 
            Assert.Equal(text, dtext);
            Assert.True(droot.ContainsAnnotations);
            Assert.True(droot.HasAnnotation(annotation));
            Assert.True(droot.IsEquivalentTo(tree.GetCompilationUnitRoot()));
 
            var dannotation = droot.GetAnnotations("TestAnnotation").SingleOrDefault();
            Assert.NotNull(dannotation);
            Assert.NotSame(annotation, dannotation); // not exact same instance
            Assert.Equal(annotation, dannotation); // equivalent though
        }
 
        [Fact]
        public void RoundTripSyntaxNodeWithAnnotationsRemoved()
        {
            var text = "public class C {}";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            var annotation1 = new SyntaxAnnotation("annotation1");
            var root = tree.GetCompilationUnitRoot().WithAdditionalAnnotations(annotation1);
            Assert.True(root.ContainsAnnotations);
            Assert.True(root.HasAnnotation(annotation1));
            var removedRoot = root.WithoutAnnotations(annotation1);
            Assert.False(removedRoot.ContainsAnnotations);
            Assert.False(removedRoot.HasAnnotation(annotation1));
 
            var stream = new MemoryStream();
            removedRoot.SerializeTo(stream);
 
            stream.Position = 0;
 
            var droot = CSharpSyntaxNode.DeserializeFrom(stream);
 
            Assert.False(droot.ContainsAnnotations);
            Assert.False(droot.HasAnnotation(annotation1));
 
            var annotation2 = new SyntaxAnnotation("annotation2");
 
            var doubleAnnoRoot = droot.WithAdditionalAnnotations(annotation1, annotation2);
            Assert.True(doubleAnnoRoot.ContainsAnnotations);
            Assert.True(doubleAnnoRoot.HasAnnotation(annotation1));
            Assert.True(doubleAnnoRoot.HasAnnotation(annotation2));
            var removedDoubleAnnoRoot = doubleAnnoRoot.WithoutAnnotations(annotation1, annotation2);
            Assert.False(removedDoubleAnnoRoot.ContainsAnnotations);
            Assert.False(removedDoubleAnnoRoot.HasAnnotation(annotation1));
            Assert.False(removedDoubleAnnoRoot.HasAnnotation(annotation2));
 
            stream = new MemoryStream();
            removedRoot.SerializeTo(stream);
 
            stream.Position = 0;
 
            droot = CSharpSyntaxNode.DeserializeFrom(stream);
 
            Assert.False(droot.ContainsAnnotations);
            Assert.False(droot.HasAnnotation(annotation1));
            Assert.False(droot.HasAnnotation(annotation2));
        }
 
        [Fact]
        public void RoundTripSyntaxNodeWithAnnotationRemovedWithMultipleReference()
        {
            var text = "public class C {}";
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            var annotation1 = new SyntaxAnnotation("MyAnnotationId", "SomeData");
            var root = tree.GetCompilationUnitRoot().WithAdditionalAnnotations(annotation1, annotation1);
            Assert.True(root.ContainsAnnotations);
            Assert.True(root.HasAnnotation(annotation1));
            var removedRoot = root.WithoutAnnotations(annotation1);
            Assert.False(removedRoot.ContainsAnnotations);
            Assert.False(removedRoot.HasAnnotation(annotation1));
 
            var stream = new MemoryStream();
            removedRoot.SerializeTo(stream);
 
            stream.Position = 0;
 
            var droot = CSharpSyntaxNode.DeserializeFrom(stream);
 
            Assert.False(droot.ContainsAnnotations);
            Assert.False(droot.HasAnnotation(annotation1));
        }
 
        private static void RoundTrip(string text, bool expectRecursive = true)
        {
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            var root = tree.GetCompilationUnitRoot();
            var originalText = root.ToFullString();
 
            var stream = new MemoryStream();
            root.SerializeTo(stream);
 
            stream.Position = 0;
            var newRoot = CSharpSyntaxNode.DeserializeFrom(stream);
            var newText = newRoot.ToFullString();
 
            Assert.True(newRoot.IsEquivalentTo(tree.GetCompilationUnitRoot()));
            Assert.Equal(originalText, newText);
        }
 
        [Fact]
        public void RoundTripXmlDocComment()
        {
            RoundTrip(@"/// <summary>XML Doc comment</summary>
class C { }");
        }
 
        [Fact]
        public void RoundTripCharLiteralWithIllegalUnicodeValue()
        {
            RoundTrip(@"public class C { char c = '\uDC00'; }");
        }
 
        [Fact]
        public void RoundTripCharLiteralWithIllegalUnicodeValue2()
        {
            RoundTrip(@"public class C { char c = '\");
        }
 
        [Fact]
        public void RoundTripCharLiteralWithIllegalUnicodeValue3()
        {
            RoundTrip(@"public class C { char c = '\u");
        }
 
        [Fact]
        public void RoundTripCharLiteralWithIllegalUnicodeValue4()
        {
            RoundTrip(@"public class C { char c = '\uDC00DC");
        }
 
        [Fact]
        public void RoundTripStringLiteralWithIllegalUnicodeValue()
        {
            RoundTrip(@"public class C { string s = ""\uDC00""; }");
        }
 
        [Fact]
        public void RoundTripStringLiteralWithUnicodeCharacters()
        {
            RoundTrip(@"public class C { string s = ""Юникод""; }");
        }
 
        [Fact]
        public void RoundTripStringLiteralWithUnicodeCharacters2()
        {
            RoundTrip(@"public class C { string c = ""\U0002A6A5𪚥""; }");
        }
 
        [Fact, WorkItem(1038237, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1038237")]
        public void RoundTripPragmaDirective()
        {
            var text = @"#pragma disable warning CS0618";
 
            var tree = SyntaxFactory.ParseSyntaxTree(text);
            var root = tree.GetCompilationUnitRoot();
            Assert.True(root.ContainsDirectives);
 
            var stream = new MemoryStream();
            root.SerializeTo(stream);
 
            stream.Position = 0;
 
            var newRoot = CSharpSyntaxNode.DeserializeFrom(stream);
            Assert.True(newRoot.ContainsDirectives);
        }
 
        [Fact]
        public void RoundTripDeepSyntaxNode()
        {
            // trees with excessively deep expressions tend to overflow the stack when using recursive encoding.
            // test that the tree is successfully serialized using non-recursive encoding.
            var text = @"
public class C
{
    public string B = " + string.Join(" + ", Enumerable.Range(0, 1000).Select(i => "\"" + i.ToString() + "\"").ToArray()) + @";
}";
 
            // serialization should fail to encode stream using recursive object encoding and
            // succeed with non-recursive object encoding.
            RoundTrip(text, expectRecursive: false);
        }
    }
}