public void GenerateServiceFactoryForClass()
        {
            // Given
            var input = CompilationBuilder.CreateAssemblyWithCode(
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export]
                      public sealed class Foo
                      { }
                  }");
            var sourceGenerator = new ServiceFactoryGenerator();
            var testEnvironment = CSharpGeneratorDriver.Create(sourceGenerator);

            Assert.False(input.GetDiagnostics().HasErrors());

            // When
            testEnvironment.RunGeneratorsAndUpdateCompilation(
                compilation: input,
                outputCompilation: out var output,
                diagnostics: out var diagnostics);

            // Then
            Assert.False(diagnostics.HasErrors());
            Assert.True(output.ContainsTypeWithMethodImplementation(
                            "ServiceFactory",
                            @"Demo.Domain.Foo IServiceFactory<Demo.Domain.Foo>.CreateOrGetService()
                 {
                     var service = new Demo.Domain.Foo();
                     return service;
                 }"));
        }
        public void GenerateServiceFactoryForTransientClassWithSingleDependencyFactory()
        {
            // Given
            var input = CompilationBuilder.CreateAssemblyWithCode(
                @"namespace Demo.Domain
                  {
                      public interface IBar
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export]
                      public sealed class Bar : IBar
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      public interface IFoo
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;
                      using System;

                      [Export(Lifetime.Transient)]
                      public sealed class Foo : IFoo
                      {
                          public Foo(Func<IBar> barFactory)
                          {
                              BarFactory = barFactory;
                          }

                          private Func<IBar> BarFactory { get; }
                      }
                  }");
            var sourceGenerator = new ServiceFactoryGenerator();
            var testEnvironment = CSharpGeneratorDriver.Create(sourceGenerator);

            // When
            testEnvironment.RunGeneratorsAndUpdateCompilation(
                compilation: input,
                outputCompilation: out var output,
                diagnostics: out var diagnostics);

            // Then
            Assert.False(diagnostics.HasErrors());
            Assert.True(output.ContainsTypeWithMethodImplementation(
                            "ServiceFactory",
                            @"Demo.Domain.IFoo IServiceFactory<Demo.Domain.IFoo>.CreateOrGetService()
                 {
                     var dependency1 = new Func<Demo.Domain.IBar>(((IServiceFactory<Demo.Domain.IBar>)this).CreateOrGetService);
                     var service = new Demo.Domain.Foo(dependency1);
                     return service;
                 }"));
        }
        public void GenerateServiceFactoryClass()
        {
            // Given
            var input           = CompilationBuilder.CreateEmptyAssembly();
            var sourceGenerator = new ServiceFactoryGenerator();
            var testEnvironment = CSharpGeneratorDriver.Create(sourceGenerator);

            // When
            testEnvironment.RunGeneratorsAndUpdateCompilation(
                compilation: input,
                outputCompilation: out var output,
                diagnostics: out var diagnostics);

            // Then
            Assert.False(diagnostics.HasErrors());
            Assert.True(output.ContainsClass("ServiceFactory"));
        }
        public void GenerateServiceFactoryForStructWithSingleGenericInterfaceWithOneGenericParameter()
        {
            // Given
            var input = CompilationBuilder.CreateAssemblyWithCode(
                @"namespace Demo.Domain
                  {
                      public interface IFoo<T>
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export]
                      public struct Foo<T> : IFoo<T>
                      { }
                  }");
            var sourceGenerator = new ServiceFactoryGenerator();
            var testEnvironment = CSharpGeneratorDriver.Create(sourceGenerator);

            // When
            testEnvironment.RunGeneratorsAndUpdateCompilation(
                compilation: input,
                outputCompilation: out var output,
                diagnostics: out var diagnostics);

            // Then
            Assert.False(diagnostics.HasErrors());
            Assert.True(output.ContainsTypeWithMethodImplementation(
                            "ServiceFactory",
                            @"Demo.Domain.IFoo<T> IServiceFactory<Demo.Domain.IFoo<T>>.CreateOrGetService()
                 {
                     var service = new Demo.Domain.Foo<T>();
                     return service;
                 }"));
        }
        public void GenerateServiceFactoryForTransientClassWithTwoDependencies()
        {
            // Given
            var input = CompilationBuilder.CreateAssemblyWithCode(
                @"namespace Demo.Domain
                  {
                      public interface IFirstBar
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export]
                      public sealed class FirstBar : IFirstBar
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      public interface ISecondBar
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export]
                      public sealed class SecondBar : ISecondBar
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      public interface IFoo
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export(Lifetime.Transient)]
                      public sealed class Foo : IFoo
                      {
                          public Foo(IFirstBar firstBar, ISecondBar secondBar)
                          {
                              FirstBar = firstBar;
                              SecondBar = secondBar;
                          }

                          private IFirstBar FirstBar { get; }

                          private ISecondBar SecondBar { get; }
                      }
                  }");
            var sourceGenerator = new ServiceFactoryGenerator();
            var testEnvironment = CSharpGeneratorDriver.Create(sourceGenerator);

            // When
            testEnvironment.RunGeneratorsAndUpdateCompilation(
                compilation: input,
                outputCompilation: out var output,
                diagnostics: out var diagnostics);

            // Then
            Assert.False(diagnostics.HasErrors());
            Assert.True(output.ContainsTypeWithMethodImplementation(
                            "ServiceFactory",
                            @"Demo.Domain.IFoo IServiceFactory<Demo.Domain.IFoo>.CreateOrGetService()
                 {
                     var dependency1 = ((IServiceFactory<Demo.Domain.IFirstBar>)this).CreateOrGetService();
                     var dependency2 = ((IServiceFactory<Demo.Domain.ISecondBar>)this).CreateOrGetService();
                     var service = new Demo.Domain.Foo(dependency1, dependency2);
                     return service;
                 }"));
        }
        public void GenerateServiceFactoryForSingletonClassWithDependencyCollection()
        {
            // Given
            var input = CompilationBuilder.CreateAssemblyWithCode(
                @"namespace Demo.Domain
                  {
                      public interface IBar
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export]
                      public sealed class FirstBar : IBar
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export]
                      public sealed class SecondBar : IBar
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      public interface IFoo
                      { }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export(Lifetime.Singleton)]
                      public sealed class Foo : IFoo
                      {
                          public Foo(IEnumerable<IBar> bar)
                          {
                              Bar = bar;
                          }

                          private IEnumerable<IBar> Bar { get; }
                      }
                  }");
            var sourceGenerator = new ServiceFactoryGenerator();
            var testEnvironment = CSharpGeneratorDriver.Create(sourceGenerator);

            // When
            testEnvironment.RunGeneratorsAndUpdateCompilation(
                compilation: input,
                outputCompilation: out var output,
                diagnostics: out var diagnostics);

            // Then
            Assert.False(diagnostics.HasErrors());
            Assert.True(output.ContainsTypeWithMethodImplementation(
                            "ServiceFactory",
                            @"Demo.Domain.IFoo IServiceFactory<Demo.Domain.IFoo>.CreateOrGetService()
                 {
                     var service = (Demo.Domain.IFoo)SingletonInstances.GetOrAdd(typeof(Demo.Domain.IFoo), _ =>
                         {
                             var dependency1 = ((IServiceFactory<IEnumerable<Demo.Domain.IBar>>)this).CreateOrGetService();
                             var service = new Demo.Domain.Foo(dependency1);
                             return service;
                         });
                    return service;
                 }"));
        }
        public void GenerateTwoScopedNamedServices()
        {
            // Given
            var input = CompilationBuilder.CreateAssemblyWithCode(
                @"namespace Demo.Domain
                  {
                      public interface IFoo
                      {
                          string Id { get; }
                      }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export(Lifetime.Scoped, ServiceId = ""1"")]
                      public sealed class FirstFoo : IFoo
                      {
                          public string Id { get; } = ""1"";
                      }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export(Lifetime.Scoped, ServiceId = ""2"")]
                      public sealed class SecondFoo : IFoo
                      {
                          public string Id { get; } = ""2"";
                      }
                  }");
            var sourceGenerator = new ServiceFactoryGenerator();
            var testEnvironment = CSharpGeneratorDriver.Create(sourceGenerator);

            // When
            testEnvironment.RunGeneratorsAndUpdateCompilation(
                compilation: input,
                outputCompilation: out var output,
                diagnostics: out var diagnostics);

            // Then
            Assert.False(diagnostics.HasErrors());
            Assert.True(output.ContainsTypeWithMethodImplementation(
                            "ServiceFactory",
                            @"Demo.Domain.IFoo? INamedServiceFactory<Demo.Domain.IFoo>.CreateOrGetNamedService(string serviceId)
                 {
                     if (string.Equals(serviceId, ""1"", StringComparison.Ordinal))
                     {
                         var service = (Demo.Domain.IFoo)ScopedInstances.GetOrAdd(typeof(Demo.Domain.IFoo), serviceId, _ =>
                             {
                                 var service = new Demo.Domain.FirstFoo();
                                 return service;
                             });
                         return service;
                     }

                     if (string.Equals(serviceId, ""2"", StringComparison.Ordinal))
                     {
                         var service = (Demo.Domain.IFoo)ScopedInstances.GetOrAdd(typeof(Demo.Domain.IFoo), serviceId, _ =>
                             {
                                 var service = new Demo.Domain.SecondFoo();
                                 return service;
                             });
                         return service;
                     }

                     return default;
                 }"));
        }
        public void ImportNamedDependencyFactory()
        {
            // Given
            var input = CompilationBuilder.CreateAssemblyWithCode(
                @"namespace Demo.Domain
                  {
                      public interface IFoo
                      {
                          IBar Dependency { get; }
                      }
                  }",
                @"namespace Demo.Domain
                  {
                      public interface IBar
                      {
                          string Id { get; }
                      }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export]
                      public sealed class Foo : IFoo
                      {
                          public Foo([Import(""1"")] Func<IBar> factory)
                          {
                              Dependency = factory();
                          }

                          public IBar Dependency { get; }
                      }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export(ServiceId = ""1"")]
                      public sealed class FirstBar : IBar
                      {
                          public string Id { get; } = ""1"";
                      }
                  }",
                @"namespace Demo.Domain
                  {
                      using CustomCode.CompileTimeInject.Annotations;

                      [Export(ServiceId = ""2"")]
                      public sealed class SecondBar : IBar
                      {
                          public string Id { get; } = ""2"";
                      }
                  }");
            var sourceGenerator = new ServiceFactoryGenerator();
            var testEnvironment = CSharpGeneratorDriver.Create(sourceGenerator);

            // When
            testEnvironment.RunGeneratorsAndUpdateCompilation(
                compilation: input,
                outputCompilation: out var output,
                diagnostics: out var diagnostics);

            // Then
            Assert.False(diagnostics.HasErrors());
            Assert.True(output.ContainsTypeWithMethodImplementation(
                            "ServiceFactory",
                            @"Demo.Domain.IFoo IServiceFactory<Demo.Domain.IFoo>.CreateOrGetService()
                 {
                     var dependency1 = new Func<Demo.Domain.IBar>(((INamedServiceFactory<Demo.Domain.IBar>)this).CreateOrGetNamedService(""1""));
                     var service = new Demo.Domain.Foo(dependency1);
                     return service;
                 }"));
        }