File: WorkspaceServiceTests\TemporaryStorageServiceTests.cs
Web Access
Project: src\src\Workspaces\CoreTest\Microsoft.CodeAnalysis.Workspaces.UnitTests.csproj (Microsoft.CodeAnalysis.Workspaces.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.
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.UnitTests
{
    using static TemporaryStorageService;
 
    [UseExportProvider]
#if NET
    [SupportedOSPlatform("windows")]
#endif
    [Trait(Traits.Feature, Traits.Features.Workspace)]
    public class TemporaryStorageServiceTests
    {
        [ConditionalFact(typeof(WindowsOnly))]
        public void TestTemporaryStorageText()
        {
            using var workspace = new AdhocWorkspace();
            var textFactory = Assert.IsType<TextFactoryService>(workspace.Services.GetService<ITextFactoryService>());
            var service = Assert.IsType<TemporaryStorageService>(workspace.Services.GetRequiredService<ITemporaryStorageServiceInternal>());
 
            // test normal string
            var text = SourceText.From(new string(' ', 4096) + "public class A {}");
            TestTemporaryStorage(service, text);
 
            // test empty string
            text = SourceText.From(string.Empty);
            TestTemporaryStorage(service, text);
 
            // test large string
            text = SourceText.From(new string(' ', 1024 * 1024) + "public class A {}");
            TestTemporaryStorage(service, text);
        }
 
        [ConditionalFact(typeof(WindowsOnly)), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531188")]
        public void TestTemporaryStorageStream()
        {
            using var workspace = new AdhocWorkspace();
            var textFactory = Assert.IsType<TextFactoryService>(workspace.Services.GetService<ITextFactoryService>());
            var service = Assert.IsType<TemporaryStorageService>(workspace.Services.GetRequiredService<ITemporaryStorageServiceInternal>());
 
            using var data = SerializableBytes.CreateWritableStream();
            for (var i = 0; i < SharedPools.ByteBufferSize; i++)
            {
                data.WriteByte((byte)(i % 2));
            }
 
            var handle = service.WriteToTemporaryStorage(data);
 
            using var result = handle.ReadFromTemporaryStorage();
            Assert.Equal(data.Length, result.Length);
 
            for (var i = 0; i < SharedPools.ByteBufferSize; i++)
            {
                Assert.Equal(i % 2, result.ReadByte());
            }
        }
 
        private static void TestTemporaryStorage(ITemporaryStorageServiceInternal temporaryStorageService, SourceText text)
        {
            // write text into it
            var handle = temporaryStorageService.WriteToTemporaryStorage(text, CancellationToken.None);
 
            // read text back from it
            var text2 = handle.ReadFromTemporaryStorage(CancellationToken.None);
 
            Assert.NotSame(text, text2);
            Assert.Equal(text.ToString(), text2.ToString());
            Assert.Equal(text.Encoding, text2.Encoding);
        }
 
        [ConditionalFact(typeof(WindowsOnly))]
        public void TestZeroLengthStreams()
        {
            using var workspace = new AdhocWorkspace();
            var textFactory = Assert.IsType<TextFactoryService>(workspace.Services.GetService<ITextFactoryService>());
            var service = Assert.IsType<TemporaryStorageService>(workspace.Services.GetRequiredService<ITemporaryStorageServiceInternal>());
 
            // 0 length streams are allowed
            TemporaryStorageStreamHandle handle;
            using (var stream1 = new MemoryStream())
            {
                handle = service.WriteToTemporaryStorage(stream1);
            }
 
            using var stream2 = handle.ReadFromTemporaryStorage();
            Assert.Equal(0, stream2.Length);
        }
 
        [ConditionalFact(typeof(WindowsOnly))]
        public void TestTemporaryStorageMemoryMappedFileManagement()
        {
            using var workspace = new AdhocWorkspace();
            var textFactory = Assert.IsType<TextFactoryService>(workspace.Services.GetService<ITextFactoryService>());
            var service = Assert.IsType<TemporaryStorageService>(workspace.Services.GetRequiredService<ITemporaryStorageServiceInternal>());
            var buffer = new MemoryStream(257 * 1024 + 1);
            for (var i = 0; i < buffer.Length; i++)
            {
                buffer.WriteByte((byte)i);
            }
 
            // Do a relatively cheap concurrent stress test of the backing MemoryMappedFile management
            var tasks = Enumerable.Range(1, 257).Select(async i =>
            {
                for (var j = 1; j < 5; j++)
                {
                    var handle1 = service.WriteToTemporaryStorage(new MemoryStream(buffer.GetBuffer(), 0, 1024 * i - 1));
                    var handle2 = service.WriteToTemporaryStorage(new MemoryStream(buffer.GetBuffer(), 0, 1024 * i));
                    var handle3 = service.WriteToTemporaryStorage(new MemoryStream(buffer.GetBuffer(), 0, 1024 * i + 1));
 
                    await Task.Yield();
 
                    using var s1 = handle1.ReadFromTemporaryStorage();
                    using var s2 = handle2.ReadFromTemporaryStorage();
                    using var s3 = handle3.ReadFromTemporaryStorage();
                    Assert.Equal(1024 * i - 1, s1.Length);
                    Assert.Equal(1024 * i, s2.Length);
                    Assert.Equal(1024 * i + 1, s3.Length);
                }
            });
 
            Task.WaitAll(tasks.ToArray());
            GC.Collect(2);
            GC.WaitForPendingFinalizers();
            GC.Collect(2);
        }
 
        [Fact(Skip = "This test exists so it can be locally executed for scale testing, when required. Do not remove this test or unskip it in CI.")]
        public void TestTemporaryStorageScaling()
        {
            // This will churn through 4GB of memory.  It validates that we don't
            // use up our address space in a 32 bit process.
            if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
            {
                using var workspace = new AdhocWorkspace();
                var textFactory = Assert.IsType<TextFactoryService>(workspace.Services.GetService<ITextFactoryService>());
                var service = Assert.IsType<TemporaryStorageService>(workspace.Services.GetRequiredService<ITemporaryStorageServiceInternal>());
 
                using var data = SerializableBytes.CreateWritableStream();
                for (var i = 0; i < 1024 * 128; i++)
                {
                    data.WriteByte(1);
                }
 
                // Create 4GB of memory mapped files
                var fileCount = (int)((long)4 * 1024 * 1024 * 1024 / data.Length);
                var storageHandles = new List<TemporaryStorageStreamHandle>(fileCount);
                for (var i = 0; i < fileCount; i++)
                {
                    var handle = service.WriteToTemporaryStorage(data);
                    storageHandles.Add(handle);
                }
 
                for (var i = 0; i < 1024 * 5; i++)
                {
                    using var s = storageHandles[i].ReadFromTemporaryStorage();
                    Assert.Equal(1, s.ReadByte());
                }
            }
        }
 
        [ConditionalFact(typeof(WindowsOnly))]
        public void StreamTest1()
        {
            using var workspace = new AdhocWorkspace();
            var textFactory = Assert.IsType<TextFactoryService>(workspace.Services.GetService<ITextFactoryService>());
            var service = Assert.IsType<TemporaryStorageService>(workspace.Services.GetRequiredService<ITemporaryStorageServiceInternal>());
 
            using var expected = new MemoryStream();
            for (var i = 0; i < 10000; i++)
            {
                expected.WriteByte((byte)(i % byte.MaxValue));
            }
 
            var handle = service.WriteToTemporaryStorage(expected);
 
            expected.Position = 0;
            using var stream = handle.ReadFromTemporaryStorage();
            Assert.Equal(expected.Length, stream.Length);
 
            for (var i = 0; i < expected.Length; i++)
            {
                Assert.Equal(expected.ReadByte(), stream.ReadByte());
            }
        }
 
        [ConditionalFact(typeof(WindowsOnly))]
        public void StreamTest2()
        {
            using var workspace = new AdhocWorkspace();
            var textFactory = Assert.IsType<TextFactoryService>(workspace.Services.GetService<ITextFactoryService>());
            var service = Assert.IsType<TemporaryStorageService>(workspace.Services.GetRequiredService<ITemporaryStorageServiceInternal>());
 
            using var expected = new MemoryStream();
            for (var i = 0; i < 10000; i++)
            {
                expected.WriteByte((byte)(i % byte.MaxValue));
            }
 
            var handle = service.WriteToTemporaryStorage(expected);
 
            expected.Position = 0;
            using var stream = handle.ReadFromTemporaryStorage();
            Assert.Equal(expected.Length, stream.Length);
 
            var index = 0;
            int count;
            var bytes = new byte[1000];
 
            while ((count = stream.Read(bytes, 0, bytes.Length)) > 0)
            {
                for (var i = 0; i < count; i++)
                {
                    Assert.Equal((byte)(index % byte.MaxValue), bytes[i]);
                    index++;
                }
            }
 
            Assert.Equal(index, stream.Length);
        }
 
        [ConditionalFact(typeof(WindowsOnly))]
        public void StreamTest3()
        {
            using var workspace = new AdhocWorkspace();
            var textFactory = Assert.IsType<TextFactoryService>(workspace.Services.GetService<ITextFactoryService>());
            var service = Assert.IsType<TemporaryStorageService>(workspace.Services.GetRequiredService<ITemporaryStorageServiceInternal>());
 
            using var expected = new MemoryStream();
            var random = new Random(Environment.TickCount);
            for (var i = 0; i < 100; i++)
            {
                var position = random.Next(10000);
                expected.Position = position;
 
                var value = (byte)(i % byte.MaxValue);
                expected.WriteByte(value);
            }
 
            var handle = service.WriteToTemporaryStorage(expected);
 
            expected.Position = 0;
            using var stream = handle.ReadFromTemporaryStorage();
            Assert.Equal(expected.Length, stream.Length);
 
            for (var i = 0; i < expected.Length; i++)
            {
                var value = expected.ReadByte();
                if (value != 0)
                {
                    stream.Position = i;
                    Assert.Equal(value, stream.ReadByte());
                }
            }
        }
 
        [ConditionalFact(typeof(WindowsOnly))]
        public void TestTemporaryStorageTextEncoding()
        {
            using var workspace = new AdhocWorkspace();
            var textFactory = Assert.IsType<TextFactoryService>(workspace.Services.GetService<ITextFactoryService>());
            var service = Assert.IsType<TemporaryStorageService>(workspace.Services.GetRequiredService<ITemporaryStorageServiceInternal>());
 
            // test normal string
            var text = SourceText.From(new string(' ', 4096) + "public class A {}", Encoding.ASCII);
            TestTemporaryStorage(service, text);
 
            // test empty string
            text = SourceText.From(string.Empty);
            TestTemporaryStorage(service, text);
 
            // test large string
            text = SourceText.From(new string(' ', 1024 * 1024) + "public class A {}");
            TestTemporaryStorage(service, text);
        }
    }
}