public void VerifyResult(MetadataAsSourceFile file, string expected)
            {
                var actual     = File.ReadAllText(file.FilePath).Trim();
                var actualSpan = file.IdentifierLocation.SourceSpan;

                // Compare exact texts and verify that the location returned is exactly that
                // indicated by expected
                MarkupTestFile.GetSpan(expected, out expected, out var expectedSpan);
                Assert.Equal(expected, actual);
                Assert.Equal(expectedSpan.Start, actualSpan.Start);
                Assert.Equal(expectedSpan.End, actualSpan.End);
            }
Ejemplo n.º 2
0
        protected async Task AssertFormatWithBaseIndentAsync(string expected, string markupCode, int baseIndentation)
        {
            MarkupTestFile.GetSpan(markupCode, out var code, out var span);

            await AssertFormatAsync(
                expected,
                code,
                new List <TextSpan> {
                span
            },
                baseIndentation : baseIndentation);
        }
Ejemplo n.º 3
0
        public async Task FollowTypeForwards_NestedType()
        {
            var source            = @"
public class C
{
    public class D
    {
        // A change
        public event System.EventHandler [|E|] { add { } remove { } }
    }
}";
            var typeForwardSource = @"
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C))]
";

            await RunTestAsync(async path =>
            {
                MarkupTestFile.GetSpan(source, out var metadataSource, out var expectedSpan);

                // Compile reference assembly
                var sourceText       = SourceText.From(metadataSource, encoding: Encoding.UTF8);
                var(project, symbol) = await CompileAndFindSymbolAsync(path, Location.Embedded, Location.Embedded, sourceText, c => c.GetMember("C.D.E"), buildReferenceAssembly: true);

                // Compile implementation assembly to a different DLL
                var dllFilePath    = Path.Combine(path, "implementation.dll");
                var sourceCodePath = Path.Combine(path, "implementation.cs");
                var pdbFilePath    = Path.Combine(path, "implementation.pdb");
                var assemblyName   = "implementation";

                var workspace = TestWorkspace.Create(@$ "
<Workspace>
    <Project Language=" "{LanguageNames.CSharp}" " CommonReferences=" "true" " ReferencesOnDisk=" "true" ">
    </Project>
</Workspace>", composition: GetTestComposition());

                var implProject = workspace.CurrentSolution.Projects.First();
                CompileTestSource(dllFilePath, sourceCodePath, pdbFilePath, assemblyName, sourceText, implProject, Location.Embedded, Location.Embedded, buildReferenceAssembly: false, windowsPdb: false);

                // Compile type forwarding implementation DLL
                var typeForwardDllFilePath = Path.Combine(path, "typeforward.dll");
                assemblyName = "typeforward";

                implProject = implProject.AddMetadataReference(MetadataReference.CreateFromFile(dllFilePath));
                sourceText  = SourceText.From(typeForwardSource, Encoding.UTF8);
                CompileTestSource(typeForwardDllFilePath, sourceCodePath, pdbFilePath, assemblyName, sourceText, implProject, Location.Embedded, Location.Embedded, buildReferenceAssembly: false, windowsPdb: false);

                var service = workspace.GetService <IImplementationAssemblyLookupService>();

                Assert.Equal(dllFilePath, service.FollowTypeForwards(symbol, typeForwardDllFilePath, new NoDuplicatesLogger()));
            });
        }
Ejemplo n.º 4
0
        public void ServiceTest2()
        {
            var markupCode =
                @"class A
{

#if true
    [|/* test */ public|] void Test(int i, int b, int c)
    {

    }
#endif

}";

            MarkupTestFile.GetSpan(markupCode, out var code, out var span);

            var root   = SyntaxFactory.ParseCompilationUnit(code);
            var result = CSharpSyntaxTriviaService.Instance.SaveTriviaAroundSelection(root, span);

            var rootWithAnnotation = result.Root;

            // find token to replace
            var publicToken = rootWithAnnotation
                              .DescendantTokens()
                              .First(t => t.Kind() == SyntaxKind.PublicKeyword);

            // replace the token with new one
            var newRoot = rootWithAnnotation.ReplaceToken(
                publicToken,
                SyntaxFactory.Token(SyntaxKind.PrivateKeyword)
                );

            // restore trivia around it
            var rootWithTriviaRestored = result.RestoreTrivia(newRoot);

            var expected =
                @"class A
{

#if true
    private void Test(int i, int b, int c)
    {

    }
#endif

}";

            Assert.Equal(expected, rootWithTriviaRestored.ToFullString());
        }
Ejemplo n.º 5
0
        protected async Task TestAsync(string text, string expectedType, bool testNode = true, bool testPosition = true)
        {
            MarkupTestFile.GetSpan(text.NormalizeLineEndings(), out text, out var textSpan);

            if (testNode)
            {
                await TestWithAndWithoutSpeculativeSemanticModelAsync(text, textSpan, expectedType, useNodeStartPosition : false);
            }

            if (testPosition)
            {
                await TestWithAndWithoutSpeculativeSemanticModelAsync(text, textSpan, expectedType, useNodeStartPosition : true);
            }
        }
        protected void AssertFormatWithBaseIndent(string expected, string markupCode, int baseIndentation)
        {
            string   code;
            TextSpan span;

            MarkupTestFile.GetSpan(markupCode, out code, out span);

            AssertFormat(
                expected,
                code,
                new List <TextSpan> {
                span
            },
                baseIndentation: baseIndentation);
        }
Ejemplo n.º 7
0
        protected async Task TestAsync(string text, string expectedType, TestMode mode,
                                       SourceCodeKind sourceCodeKind = SourceCodeKind.Regular)
        {
            MarkupTestFile.GetSpan(text.NormalizeLineEndings(), out text, out var textSpan);

            var document = fixture.UpdateDocument(text, sourceCodeKind);

            await TestWorkerAsync(document, textSpan, expectedType, mode);

            if (await CanUseSpeculativeSemanticModelAsync(document, textSpan.Start))
            {
                var document2 = fixture.UpdateDocument(text, sourceCodeKind, cleanBeforeUpdate: false);
                await TestWorkerAsync(document2, textSpan, expectedType, mode);
            }
        }
Ejemplo n.º 8
0
        protected void Test(string text, string expectedType, bool testNode = true, bool testPosition = true)
        {
            TextSpan textSpan;

            MarkupTestFile.GetSpan(text.NormalizeLineEndings(), out text, out textSpan);

            if (testNode)
            {
                TestWithAndWithoutSpeculativeSemanticModel(text, textSpan, expectedType, useNodeStartPosition: false);
            }

            if (testPosition)
            {
                TestWithAndWithoutSpeculativeSemanticModel(text, textSpan, expectedType, useNodeStartPosition: true);
            }
        }
Ejemplo n.º 9
0
        private async Task TestDelegateAsync(string text, string expectedType)
        {
            MarkupTestFile.GetSpan(text, out text, out var textSpan);

            Document document = fixture.UpdateDocument(text, SourceCodeKind.Regular);

            var root = await document.GetSyntaxRootAsync();

            var node = FindExpressionSyntaxFromSpan(root, textSpan);

            var typeInference = document.GetLanguageService <ITypeInferenceService>();
            var delegateType  = typeInference.InferDelegateType(await document.GetSemanticModelAsync(), node, CancellationToken.None);

            Assert.NotNull(delegateType);
            Assert.Equal(expectedType, delegateType.ToNameDisplayString());
        }
Ejemplo n.º 10
0
        protected async Task TestRenameTypeToMatchFileAsync(
            string originalCode,
            string expectedCode               = null,
            bool expectedCodeAction           = true,
            bool ignoreTrivia                 = true,
            string fixAllActionEquivalenceKey = null,
            object fixProviderData            = null)
        {
            var testOptions = new TestParameters(
                fixAllActionEquivalenceKey: fixAllActionEquivalenceKey,
                fixProviderData: fixProviderData);

            using (var workspace = await CreateWorkspaceFromOptionsAsync(originalCode, testOptions))
            {
                if (expectedCodeAction)
                {
                    Assert.True(expectedCode != null, $"{nameof(expectedCode)} should be present if {nameof(expectedCodeAction)} is true.");

                    var documentId   = workspace.Documents[0].Id;
                    var documentName = workspace.Documents[0].Name;
                    MarkupTestFile.GetSpan(expectedCode, out var expectedText, out var span);

                    var codeActionTitle = string.Format(RenameTypeCodeActionTitle, expectedText.Substring(span.Start, span.Length));

                    var oldSolutionAndNewSolution = await TestOperationAsync(
                        testOptions, workspace, expectedText, codeActionTitle, ignoreTrivia);

                    // the original source document does not exist in the new solution.
                    var newSolution = oldSolutionAndNewSolution.Item2;

                    var document = newSolution.GetDocument(documentId);
                    Assert.NotNull(document);
                    Assert.Equal(documentName, document.Name);
                }
                else
                {
                    var actions = await GetCodeActionsAsync(workspace, testOptions);

                    if (actions != null)
                    {
                        var renameFileAction = actions.Any(action => action.Title.StartsWith(RenameTypeCodeActionTitle));
                        Assert.False(renameFileAction, "Rename Type to match file name code action was not expected, but shows up.");
                    }
                }
            }
        }
        public async Task WindowsPdb_NullResult()
        {
            var source = @"
public class C
{
    public event System.EventHandler [|E|] { add { } remove { } }
}";

            await RunTestAsync(async path =>
            {
                MarkupTestFile.GetSpan(source, out var metadataSource, out var expectedSpan);

                var(project, symbol) = await CompileAndFindSymbolAsync(path, Location.OnDisk, Location.OnDisk, metadataSource, c => c.GetMember("C.E"), windowsPdb: true);

                //TODO: This should not be a null result: https://github.com/dotnet/roslyn/issues/55834
                await GenerateFileAndVerifyAsync(project, symbol, Location.OnDisk, source, expectedSpan, expectNullResult: true);
            });
        }
Ejemplo n.º 12
0
        public async Task Net6SdkLayout_WithOtherReferences()
        {
            var source = @"
public class C
{
    public void [|M|](string d)
    {
    }
}
";

            await RunTestAsync(async path =>
            {
                MarkupTestFile.GetSpan(source, out var metadataSource, out var expectedSpan);

                var packDir   = Directory.CreateDirectory(Path.Combine(path, "packs", "MyPack.Ref", "1.0", "ref", "net6.0")).FullName;
                var dataDir   = Directory.CreateDirectory(Path.Combine(path, "packs", "MyPack.Ref", "1.0", "data")).FullName;
                var sharedDir = Directory.CreateDirectory(Path.Combine(path, "shared", "MyPack", "1.0")).FullName;

                var sourceText       = SourceText.From(metadataSource, Encoding.UTF8);
                var(project, symbol) = await CompileAndFindSymbolAsync(packDir, Location.Embedded, Location.Embedded, sourceText, c => c.GetMember("C.M"), buildReferenceAssembly: true);

                var workspace = TestWorkspace.Create(@$ "
<Workspace>
    <Project Language=" "{LanguageNames.CSharp}" " CommonReferences=" "true" " ReferencesOnDisk=" "true" ">
    </Project>
</Workspace>", composition: GetTestComposition());

                var implProject = workspace.CurrentSolution.Projects.First();

                // Compile implementation assembly
                CompileTestSource(sharedDir, sourceText, project, Location.Embedded, Location.Embedded, buildReferenceAssembly: false, windowsPdb: false);

                // Create FrameworkList.xml
                File.WriteAllText(Path.Combine(dataDir, "FrameworkList.xml"), "" "
                    <FileList FrameworkName=" MyPack ">
                    </FileList>
                    " "");

                await GenerateFileAndVerifyAsync(project, symbol, Location.Embedded, metadataSource.ToString(), expectedSpan, expectNullResult: false);
            });
        }
        public async Task EmptyPdb_NullResult()
        {
            var source = @"
public class C
{
    public event System.EventHandler [|E|] { add { } remove { } }
}";

            await RunTestAsync(async path =>
            {
                MarkupTestFile.GetSpan(source, out var metadataSource, out var expectedSpan);

                var(project, symbol) = await CompileAndFindSymbolAsync(path, Location.OnDisk, Location.OnDisk, metadataSource, c => c.GetMember("C.E"));

                // Now make the PDB a zero byte file
                File.WriteAllBytes(GetPdbPath(path), new byte[0]);

                await GenerateFileAndVerifyAsync(project, symbol, Location.OnDisk, source, expectedSpan, expectNullResult: true);
            });
        }
        public async Task NoSource_NullResult()
        {
            var source = @"
public class C
{
    public event System.EventHandler [|E|] { add { } remove { } }
}";

            await RunTestAsync(async path =>
            {
                MarkupTestFile.GetSpan(source, out var metadataSource, out var expectedSpan);

                var(project, symbol) = await CompileAndFindSymbolAsync(path, Location.OnDisk, Location.OnDisk, metadataSource, c => c.GetMember("C.E"));

                // Now delete the source
                File.Delete(GetSourceFilePath(path));

                await GenerateFileAndVerifyAsync(project, symbol, Location.OnDisk, source, expectedSpan, expectNullResult: true);
            });
        }
Ejemplo n.º 15
0
        public static async Task SingleErrorTestAsync(IFormattingRule rule, string code, DiagnosticDescriptor expectedDescriptor, string expectedMessage = null)
        {
            MarkupTestFile.GetSpan(code, out code, out var expectedLocation);

            var actualDiagnostics = await EvaluateRuleAsync(rule, code);

            Assert.Collection(actualDiagnostics,
                              diagnostic =>
            {
                // Check ID specially so that we get a friendly error if the descriptor doesn't match
                Assert.Equal(expectedDescriptor.Id, diagnostic.Id);
                Assert.Same(expectedDescriptor, diagnostic.Descriptor);

                if (expectedMessage != null)
                {
                    Assert.Equal(expectedMessage, diagnostic.GetMessage());
                }

                Assert.Equal(expectedLocation, diagnostic.Location.SourceSpan);
            });
        }
Ejemplo n.º 16
0
        public void Directive_character_ranges_can_be_read()
        {
            var markupCode = @"
[|#!csharp|] 
var x = 123;
x
";

            MarkupTestFile.GetSpan(markupCode, out var code, out var span);

            var tree = CreateSubmissionParser("csharp").Parse(code);

            var textSpan = tree.GetRoot()
                           .FindNode(span)
                           .ChildTokens
                           .OfType <DirectiveToken>()
                           .Single()
                           .Span;

            textSpan.Should().BeEquivalentTo(span);
        }
Ejemplo n.º 17
0
        public async Task NoUrlFoundReturnsNull()
        {
            var source = @"
public class C
{
    public event System.EventHandler [|E|] { add { } remove { } }
}";

            await RunTestAsync(async path =>
            {
                MarkupTestFile.GetSpan(source, out var metadataSource, out var expectedSpan);

                var(project, symbol) = await CompileAndFindSymbolAsync(path, Location.OnDisk, Location.OnDisk, metadataSource, c => c.GetMember("C.E"));

                // Move the source file to a path that only our fake debugger service knows about
                var sourceFilePath = Path.Combine(path, "SourceLink.cs");
                File.Move(GetSourceFilePath(path), sourceFilePath);

                var sourceLinkService = new Lazy <ISourceLinkService?>(() => new TestSourceLinkService(sourceFilePath: sourceFilePath));
                var service           = new PdbSourceDocumentLoaderService(sourceLinkService, logger: null);

                var sourceDocument = new SourceDocument("goo.cs", Text.SourceHashAlgorithm.None, default, null, SourceLinkUrl: null);
        public async Task CorruptPdb_NullResult()
        {
            var source = @"
public class C
{
    public event System.EventHandler [|E|] { add { } remove { } }
}";

            await RunTestAsync(async path =>
            {
                MarkupTestFile.GetSpan(source, out var metadataSource, out var expectedSpan);

                var(project, symbol) = await CompileAndFindSymbolAsync(path, Location.OnDisk, Location.OnDisk, metadataSource, c => c.GetMember("C.E"));

                // The first four bytes of this are BSJB so it is identified as a portable PDB.
                // The next two bytes are unimportant, they're just not valid PDB data.
                var corruptPdb = new byte[] { 66, 83, 74, 66, 68, 87 };
                File.WriteAllBytes(GetPdbPath(path), corruptPdb);

                await GenerateFileAndVerifyAsync(project, symbol, Location.OnDisk, source, expectedSpan, expectNullResult: true);
            });
        }
Ejemplo n.º 19
0
        public async Task Net6SdkLayout_PacksInPath()
        {
            var source = @"
public class C
{
    // A change
    public event System.EventHandler [|E|] { add { } remove { } }
}";

            await RunTestAsync(async path =>
            {
                MarkupTestFile.GetSpan(source, out var metadataSource, out var expectedSpan);

                path = Path.Combine(path, "packs", "installed", "here");

                var packDir   = Directory.CreateDirectory(Path.Combine(path, "packs", "MyPack.Ref", "1.0", "ref", "net6.0")).FullName;
                var dataDir   = Directory.CreateDirectory(Path.Combine(path, "packs", "MyPack.Ref", "1.0", "data")).FullName;
                var sharedDir = Directory.CreateDirectory(Path.Combine(path, "shared", "MyPack", "1.0")).FullName;

                // Compile reference assembly
                var sourceText       = SourceText.From(metadataSource, encoding: Encoding.UTF8);
                var(project, symbol) = await CompileAndFindSymbolAsync(packDir, Location.Embedded, Location.Embedded, sourceText, c => c.GetMember("C.E"), buildReferenceAssembly: true);

                // Compile implementation assembly
                CompileTestSource(sharedDir, sourceText, project, Location.Embedded, Location.Embedded, buildReferenceAssembly: false, windowsPdb: false);

                // Create FrameworkList.xml
                File.WriteAllText(Path.Combine(dataDir, "FrameworkList.xml"), "" "
                    <FileList FrameworkName=" MyPack ">
                    </FileList>
                    " "");

                var workspace = (TestWorkspace)project.Solution.Workspace;
                var service   = workspace.GetService <IImplementationAssemblyLookupService>();

                Assert.True(service.TryFindImplementationAssemblyPath(GetDllPath(packDir), out var implementationDll));
                Assert.Equal(GetDllPath(sharedDir), implementationDll);
            });
        }
Ejemplo n.º 20
0
            public void VerifyResult(MetadataAsSourceFile file, string expected, bool ignoreTrivia = true)
            {
                var actual     = File.ReadAllText(file.FilePath).Trim();
                var actualSpan = file.IdentifierLocation.SourceSpan;

                if (ignoreTrivia)
                {
                    // Compare tokens and verify location relative to the generated tokens
                    expected = GetSpaceSeparatedTokens(expected);
                    actual   = GetSpaceSeparatedTokens(actual.Insert(actualSpan.Start, "[|").Insert(actualSpan.End + 2, "|]"));
                }
                else
                {
                    // Compare exact texts and verify that the location returned is exactly that
                    // indicated by expected
                    MarkupTestFile.GetSpan(expected.TrimStart().TrimEnd(), out expected, out var expectedSpan);
                    Assert.Equal(expectedSpan.Start, actualSpan.Start);
                    Assert.Equal(expectedSpan.End, actualSpan.End);
                }

                Assert.Equal(expected, actual);
            }
Ejemplo n.º 21
0
        protected async Task TestRenameTypeToMatchFileAsync(
            string originalCode,
            string expectedCode     = null,
            bool expectedCodeAction = true,
            object fixProviderData  = null)
        {
            var testOptions = new TestParameters(fixProviderData: fixProviderData);

            using (var workspace = CreateWorkspaceFromOptions(originalCode, testOptions))
            {
                if (expectedCodeAction)
                {
                    Assert.True(expectedCode != null, $"{nameof(expectedCode)} should be present if {nameof(expectedCodeAction)} is true.");

                    var documentId   = workspace.Documents[0].Id;
                    var documentName = workspace.Documents[0].Name;
                    MarkupTestFile.GetSpan(expectedCode, out var expectedText, out var span);

                    var codeActionTitle = string.Format(RenameTypeCodeActionTitle, expectedText.Substring(span.Start, span.Length));

                    var oldSolutionAndNewSolution = await TestOperationAsync(
                        testOptions, workspace, expectedText, codeActionTitle);

                    // the original source document does not exist in the new solution.
                    var newSolution = oldSolutionAndNewSolution.Item2;

                    var document = newSolution.GetDocument(documentId);
                    Assert.NotNull(document);
                    Assert.Equal(documentName, document.Name);
                }
                else
                {
                    var(actions, _) = await GetCodeActionsAsync(workspace, testOptions);

                    if (actions.Length > 0)
                    {
                        var renameFileAction = actions.Any(static (action, self) => action.Title.StartsWith(self.RenameTypeCodeActionTitle), this);
Ejemplo n.º 22
0
        protected static async Task TestAsync(
            string path,
            Location pdbLocation,
            Location sourceLocation,
            string metadataSource,
            Func <Compilation, ISymbol> symbolMatcher,
            string[]?preprocessorSymbols,
            bool buildReferenceAssembly,
            bool expectNullResult)
        {
            MarkupTestFile.GetSpan(metadataSource, out var source, out var expectedSpan);

            var(project, symbol) = await CompileAndFindSymbolAsync(
                path,
                pdbLocation,
                sourceLocation,
                source,
                symbolMatcher,
                preprocessorSymbols,
                buildReferenceAssembly,
                windowsPdb : false);

            await GenerateFileAndVerifyAsync(project, symbol, sourceLocation, source, expectedSpan, expectNullResult);
        }
Ejemplo n.º 23
0
        internal static async Task TestGenerateFromSourceSymbolAsync(
            string symbolSource,
            string initial,
            string expected,
            bool onlyGenerateMembers = false,
            CodeGenerationOptions codeGenerationOptions = default(CodeGenerationOptions),
            bool compareTokens   = true,
            string forceLanguage = null)
        {
            using (var context = await TestContext.CreateAsync(initial, expected, compareTokens, forceLanguage))
            {
                TextSpan destSpan = new TextSpan();
                MarkupTestFile.GetSpan(symbolSource.NormalizeLineEndings(), out symbolSource, out destSpan);

                var projectId  = ProjectId.CreateNewId();
                var documentId = DocumentId.CreateNewId(projectId);

                var semanticModel = await context.Solution
                                    .AddProject(projectId, "GenerationSource", "GenerationSource", TestContext.GetLanguage(symbolSource))
                                    .AddDocument(documentId, "Source.cs", symbolSource)
                                    .GetDocument(documentId)
                                    .GetSemanticModelAsync();

                var symbol      = context.GetSelectedSymbol <INamespaceOrTypeSymbol>(destSpan, semanticModel);
                var destination = context.GetDestination();
                if (destination.IsType)
                {
                    var members = onlyGenerateMembers ? symbol.GetMembers().ToArray() : new[] { symbol };
                    context.Result = await context.Service.AddMembersAsync(context.Solution, (INamedTypeSymbol)destination, members, codeGenerationOptions);
                }
                else
                {
                    context.Result = await context.Service.AddNamespaceOrTypeAsync(context.Solution, (INamespaceSymbol)destination, symbol, codeGenerationOptions);
                }
            }
        }
Ejemplo n.º 24
0
        public async Task FollowTypeForwards_MultipleHops_Cache()
        {
            var source            = @"
public class C
{
    // A change
    public event System.EventHandler [|E|] { add { } remove { } }
}";
            var typeForwardSource = @"
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C))]
";

            await RunTestAsync(async path =>
            {
                MarkupTestFile.GetSpan(source, out var metadataSource, out var expectedSpan);

                // Compile reference assembly
                var sourceText       = SourceText.From(metadataSource, encoding: Encoding.UTF8);
                var(project, symbol) = await CompileAndFindSymbolAsync(path, Location.Embedded, Location.Embedded, sourceText, c => c.GetMember("C.E"), buildReferenceAssembly: true);

                // Compile implementation assembly to a different DLL
                var dllFilePath    = Path.Combine(path, "implementation.dll");
                var sourceCodePath = Path.Combine(path, "implementation.cs");
                var pdbFilePath    = Path.Combine(path, "implementation.pdb");
                var assemblyName   = "implementation";

                var workspace = TestWorkspace.Create(@$ "
<Workspace>
    <Project Language=" "{LanguageNames.CSharp}" " CommonReferences=" "true" " ReferencesOnDisk=" "true" ">
    </Project>
</Workspace>", composition: GetTestComposition());

                var implProject = workspace.CurrentSolution.Projects.First();
                CompileTestSource(dllFilePath, sourceCodePath, pdbFilePath, assemblyName, sourceText, implProject, Location.Embedded, Location.Embedded, buildReferenceAssembly: false, windowsPdb: false);

                // Compile type forwarding implementation DLL
                var typeForwardDllFilePath = Path.Combine(path, "typeforward.dll");
                assemblyName = "typeforward";

                implProject = workspace.CurrentSolution.Projects.First().AddMetadataReference(MetadataReference.CreateFromFile(dllFilePath));
                var typeForwardSourceText = SourceText.From(typeForwardSource, Encoding.UTF8);
                CompileTestSource(typeForwardDllFilePath, sourceCodePath, pdbFilePath, assemblyName, typeForwardSourceText, implProject, Location.Embedded, Location.Embedded, buildReferenceAssembly: false, windowsPdb: false);

                // Now compile a new implementation in realimplementation.dll
                var realImplementationDllFilePath = Path.Combine(path, "realimplementation.dll");
                assemblyName = "realimplementation";

                implProject = workspace.CurrentSolution.Projects.First();
                CompileTestSource(realImplementationDllFilePath, sourceCodePath, pdbFilePath, assemblyName, sourceText, implProject, Location.Embedded, Location.Embedded, buildReferenceAssembly: false, windowsPdb: false);

                // Now compile a new implementation.dll that typeforwards to realimplementation.dll
                assemblyName = "implementation";

                implProject = workspace.CurrentSolution.Projects.First().AddMetadataReference(MetadataReference.CreateFromFile(realImplementationDllFilePath));
                CompileTestSource(dllFilePath, sourceCodePath, pdbFilePath, assemblyName, typeForwardSourceText, implProject, Location.Embedded, Location.Embedded, buildReferenceAssembly: false, windowsPdb: false);

                var service = workspace.GetService <IImplementationAssemblyLookupService>();

                var foundImplementationFilePath = service.FollowTypeForwards(symbol, typeForwardDllFilePath, new NoDuplicatesLogger());
                Assert.Equal(realImplementationDllFilePath, foundImplementationFilePath);

                // We need the DLLs to exist, in order for some checks to pass correct, but to ensure
                // that the file isn't read, we just zero it out.
                File.WriteAllBytes(typeForwardDllFilePath, Array.Empty <byte>());
                File.WriteAllBytes(realImplementationDllFilePath, Array.Empty <byte>());
                File.WriteAllBytes(dllFilePath, Array.Empty <byte>());

                foundImplementationFilePath = service.FollowTypeForwards(symbol, typeForwardDllFilePath, new NoDuplicatesLogger());
                Assert.Equal(realImplementationDllFilePath, foundImplementationFilePath);
            });
        }