diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CodeModelPlugin.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CodeModelPlugin.cs index 94a31abb8..0d00f6616 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CodeModelPlugin.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CodeModelPlugin.cs @@ -41,6 +41,14 @@ namespace Microsoft.Generator.CSharp _inputLibrary = new(() => new InputLibrary(Instance.Configuration.OutputDirectory)); } + // for mocking + protected CodeModelPlugin() + { + // should be mocked + Configuration = null!; + _inputLibrary = new(() => null!); + } + private Lazy _inputLibrary; // Extensibility points to be implemented by a plugin @@ -50,5 +58,9 @@ namespace Microsoft.Generator.CSharp public InputLibrary InputLibrary => _inputLibrary.Value; public virtual TypeProviderWriter GetWriter(TypeProvider provider) => new(provider); public virtual IReadOnlyList AdditionalMetadataReferences => Array.Empty(); + + public virtual void Configure() + { + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/OutputLibrary.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/OutputLibrary.cs index ffcca645e..6c35fd5e9 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/OutputLibrary.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/OutputLibrary.cs @@ -8,6 +8,7 @@ namespace Microsoft.Generator.CSharp { public class OutputLibrary { + private List _visitors = new(); public OutputLibrary() { } @@ -57,6 +58,11 @@ namespace Microsoft.Generator.CSharp } // TODO - make this more additive instead of replace https://github.com/microsoft/typespec/issues/3827 - protected internal virtual IEnumerable GetOutputLibraryVisitors() => []; + protected internal virtual IEnumerable GetOutputLibraryVisitors() => _visitors; + + public void AddVisitor(OutputLibraryVisitor visitor) + { + _visitors.Add(visitor); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/StartUp/PluginHandler.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/StartUp/PluginHandler.cs index e3def88ab..1a08cd3bb 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/StartUp/PluginHandler.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/StartUp/PluginHandler.cs @@ -13,26 +13,32 @@ namespace Microsoft.Generator.CSharp public void LoadPlugin(CommandLineOptions options) { using DirectoryCatalog directoryCatalog = new(AppContext.BaseDirectory); - using (CompositionContainer container = new(directoryCatalog)) - { - container.ComposeExportedValue(new GeneratorContext(Configuration.Load(options.OutputDirectory))); - container.ComposeParts(this); - bool loaded = false; - foreach (var plugin in Plugins!) - { - if (plugin.Metadata.PluginName == options.PluginName) - { - CodeModelPlugin.Instance = plugin.Value; - loaded = true; - break; - } - } + using CompositionContainer container = new(directoryCatalog); - if (!loaded) + container.ComposeExportedValue(new GeneratorContext(Configuration.Load(options.OutputDirectory))); + container.ComposeParts(this); + + SelectPlugin(options.PluginName!); + } + + internal void SelectPlugin(string pluginName) + { + bool loaded = false; + foreach (var plugin in Plugins!) + { + if (plugin.Metadata.PluginName == pluginName) { - throw new InvalidOperationException($"Plugin {options.PluginName} not found."); + CodeModelPlugin.Instance = plugin.Value; + loaded = true; + CodeModelPlugin.Instance.Configure(); + break; } } + + if (!loaded) + { + throw new InvalidOperationException($"Plugin {pluginName} not found."); + } } [ImportMany] diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/OutputLibraryTests.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/OutputLibraryTests.cs index 2ae539632..e5432155b 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/OutputLibraryTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/OutputLibraryTests.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.Generator.CSharp.Providers; using NUnit.Framework; @@ -16,7 +18,7 @@ namespace Microsoft.Generator.CSharp.Tests [SetUp] public void Setup() { - _outputLibrary = new MockOutputLibrary(); + _outputLibrary = new TestOutputLibrary(); } // Tests that the BuildTypeProviders method is successfully overridden. @@ -26,9 +28,54 @@ namespace Microsoft.Generator.CSharp.Tests Assert.Throws(() => { object shouldFail = _outputLibrary.TypeProviders; }); } - internal class MockOutputLibrary : OutputLibrary + [Test] + public void CanAddVisitors() { - public MockOutputLibrary() : base() { } + _outputLibrary.AddVisitor(new TestOutputLibraryVisitor()); + Assert.AreEqual(1, _outputLibrary.GetOutputLibraryVisitors().Count()); + } + + [Test] + public void CanOverrideGetOutputLibraryVisitors() + { + var outputLibrary = new TestOutputLibraryOverridingVisitors(new [] { new TestOutputLibraryVisitor() }); + Assert.AreEqual(1, outputLibrary.GetOutputLibraryVisitors().Count()); + + outputLibrary.AddVisitor(new TestOutputLibraryVisitor()); + Assert.AreEqual(2, outputLibrary.GetOutputLibraryVisitors().Count()); + } + + private class TestOutputLibraryVisitor : OutputLibraryVisitor + { + } + + private class TestOutputLibrary : OutputLibrary + { + protected override TypeProvider[] BuildTypeProviders() + { + throw new NotImplementedException(); + } + } + + private class TestOutputLibraryOverridingVisitors : OutputLibrary + { + private readonly IEnumerable? _visitors; + public TestOutputLibraryOverridingVisitors(IEnumerable? visitors = null) + { + _visitors = visitors; + } + + protected internal override IEnumerable GetOutputLibraryVisitors() + { + foreach (var visitor in base.GetOutputLibraryVisitors()) + { + yield return visitor; + } + foreach (var visitor in _visitors ?? []) + { + yield return visitor; + } + } protected override TypeProvider[] BuildTypeProviders() { diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/StartUp/PluginHandlerTests.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/StartUp/PluginHandlerTests.cs new file mode 100644 index 000000000..c6fb86615 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/StartUp/PluginHandlerTests.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using Moq; +using NUnit.Framework; + +namespace Microsoft.Generator.CSharp.Tests.StartUp +{ + public class PluginHandlerTests + { + [Test] + public void SelectPluginFindsMatchingPlugin() + { + var pluginHandler = new PluginHandler(); + var metadataMock = new Mock(); + metadataMock.SetupGet(m => m.PluginName).Returns("MockPlugin"); + var mockPlugin = new Mock(); + + pluginHandler.Plugins = new List> + { + new(() => mockPlugin.Object, metadataMock.Object), + }; + + Assert.DoesNotThrow(() => pluginHandler.SelectPlugin("MockPlugin")); + mockPlugin.Verify(p => p.Configure(), Times.Once); + } + + [Test] + public void SelectPluginThrowsWhenNoMatch() + { + var pluginHandler = new PluginHandler(); + var metadataMock = new Mock(); + metadataMock.SetupGet(m => m.PluginName).Returns("MockPlugin"); + var mockPlugin = new Mock(); + + pluginHandler.Plugins = new List> + { + new(() => mockPlugin.Object, metadataMock.Object), + }; + + Assert.Throws(() => pluginHandler.SelectPlugin("NonExistentPlugin")); + mockPlugin.Verify(p => p.Configure(), Times.Never); + } + } +} diff --git a/packages/http-client-csharp/generator/SamplePlugin/src/OutputTypes/SamplePluginOutputLibrary.cs b/packages/http-client-csharp/generator/SamplePlugin/src/OutputTypes/SamplePluginOutputLibrary.cs deleted file mode 100644 index f0b0fc203..000000000 --- a/packages/http-client-csharp/generator/SamplePlugin/src/OutputTypes/SamplePluginOutputLibrary.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Collections.Generic; -using Microsoft.Generator.CSharp.ClientModel; -namespace SamplePlugin -{ - public class SamplePluginOutputLibrary : ScmOutputLibrary - { - protected override IEnumerable GetOutputLibraryVisitors() => [new SamplePluginOutputLibraryVisitor()]; - } -} diff --git a/packages/http-client-csharp/generator/SamplePlugin/src/SampleCodeModelPlugin.cs b/packages/http-client-csharp/generator/SamplePlugin/src/SampleCodeModelPlugin.cs index f5eb3593e..387d7eea7 100644 --- a/packages/http-client-csharp/generator/SamplePlugin/src/SampleCodeModelPlugin.cs +++ b/packages/http-client-csharp/generator/SamplePlugin/src/SampleCodeModelPlugin.cs @@ -14,6 +14,9 @@ namespace SamplePlugin { public override SamplePluginTypeFactory TypeFactory { get; } = new SamplePluginTypeFactory(); - public override OutputLibrary OutputLibrary { get; } = new SamplePluginOutputLibrary(); + public override void Configure() + { + OutputLibrary.AddVisitor(new SamplePluginOutputLibraryVisitor()); + } } }