Migrate Dagger's factory tests to Kotlin sources.

RELNOTES=N/A
PiperOrigin-RevId: 493672279
diff --git a/javatests/dagger/functional/kotlinsrc/factory/AbstractModule.kt b/javatests/dagger/functional/kotlinsrc/factory/AbstractModule.kt
new file mode 100644
index 0000000..5dec18d
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/AbstractModule.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import dagger.Module
+import dagger.Provides
+
+@Module
+object AbstractModule {
+  @Provides fun provideString(): String = "foo"
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/BUILD b/javatests/dagger/functional/kotlinsrc/factory/BUILD
new file mode 100644
index 0000000..f4a0328
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/BUILD
@@ -0,0 +1,44 @@
+# Copyright (C) 2022 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+#   Functional tests for Dagger factories
+
+load(
+    "//:build_defs.bzl",
+    "DOCLINT_HTML_AND_SYNTAX",
+    "DOCLINT_REFERENCES",
+    "JAVA_RELEASE_MIN",
+)
+load("//:test_defs.bzl", "GenJavaTests")
+
+package(default_visibility = ["//:src"])
+
+GenJavaTests(
+    name = "factory",
+    srcs = glob(["*.kt"]),
+    javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+    lib_javacopts = JAVA_RELEASE_MIN,
+    deps = [
+        "//:dagger_with_compiler",
+        "//third_party/java/auto:factory",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+    ],
+)
diff --git a/javatests/dagger/functional/kotlinsrc/factory/ConcreteModuleThatCouldBeAbstract.kt b/javatests/dagger/functional/kotlinsrc/factory/ConcreteModuleThatCouldBeAbstract.kt
new file mode 100644
index 0000000..20193b5
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/ConcreteModuleThatCouldBeAbstract.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import dagger.Module
+import dagger.Provides
+
+@Suppress("ClassShouldBeObject")
+@Module
+class ConcreteModuleThatCouldBeAbstract {
+  companion object {
+    @Provides fun provideDouble(): Double = 42.0
+  }
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/Dependency.kt b/javatests/dagger/functional/kotlinsrc/factory/Dependency.kt
new file mode 100644
index 0000000..4061e29
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/Dependency.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+class Dependency {
+  fun someObject(): Any = "bar"
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/FactoryBindsInstanceTest.kt b/javatests/dagger/functional/kotlinsrc/factory/FactoryBindsInstanceTest.kt
new file mode 100644
index 0000000..9e82292
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/FactoryBindsInstanceTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/** Tests for component factories with [BindsInstance] parameters. */
+@RunWith(JUnit4::class)
+class FactoryBindsInstanceTest {
+  @Component
+  internal interface BindsInstanceComponent {
+    fun string(): String
+
+    @Component.Factory
+    interface Factory {
+      fun create(@BindsInstance string: String): BindsInstanceComponent
+    }
+  }
+
+  @Test
+  fun bindsInstance() {
+    val component = DaggerFactoryBindsInstanceTest_BindsInstanceComponent.factory().create("baz")
+    assertThat(component.string()).isEqualTo("baz")
+  }
+
+  @Target(AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER)
+  @Retention(AnnotationRetention.BINARY)
+  internal annotation class Nullable
+
+  @Component
+  internal interface NullableBindsInstanceComponent {
+    @Nullable fun string(): String?
+
+    @Component.Factory
+    interface Factory {
+      fun create(@BindsInstance @Nullable string: String?): NullableBindsInstanceComponent
+    }
+  }
+
+  @Test
+  fun nullableBindsInstance_doesNotFailOnNull() {
+    val component =
+      DaggerFactoryBindsInstanceTest_NullableBindsInstanceComponent.factory().create(null)
+    assertThat(component.string()).isEqualTo(null)
+  }
+
+  @Component
+  internal interface PrimitiveBindsInstanceComponent {
+    val int: Int
+
+    @Component.Factory
+    interface Factory {
+      fun create(@BindsInstance i: Int): PrimitiveBindsInstanceComponent
+    }
+  }
+
+  @Test
+  fun primitiveBindsInstance() {
+    val component =
+      DaggerFactoryBindsInstanceTest_PrimitiveBindsInstanceComponent.factory().create(1)
+    assertThat(component.int).isEqualTo(1)
+  }
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/FactoryDependenciesTest.kt b/javatests/dagger/functional/kotlinsrc/factory/FactoryDependenciesTest.kt
new file mode 100644
index 0000000..884a476
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/FactoryDependenciesTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import com.google.common.truth.Truth.assertThat
+import dagger.Component
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/** Tests for factories for components with a dependency. */
+@RunWith(JUnit4::class)
+class FactoryDependenciesTest {
+  @Component(dependencies = [Dependency::class])
+  internal interface DependencyComponent {
+    fun someObject(): Any
+
+    @Component.Factory
+    interface Factory {
+      fun create(dependency: Dependency?): DependencyComponent
+    }
+  }
+
+  @Test
+  fun dependency() {
+    val component = DaggerFactoryDependenciesTest_DependencyComponent.factory().create(Dependency())
+    assertThat(component.someObject()).isEqualTo("bar")
+  }
+
+  @Test
+  fun dependency_failsOnNull() {
+    try {
+      DaggerFactoryDependenciesTest_DependencyComponent.factory().create(null)
+      fail()
+    } catch (expected: NullPointerException) {}
+  }
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/FactoryImplicitModulesTest.kt b/javatests/dagger/functional/kotlinsrc/factory/FactoryImplicitModulesTest.kt
new file mode 100644
index 0000000..52e1fe0
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/FactoryImplicitModulesTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import com.google.common.truth.Truth.assertThat
+import dagger.Component
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/**
+ * Tests for factories for components with modules that do not require an instance to be passed to
+ * the factory. Includes both tests where the module does not have a corresponding parameter in the
+ * factory method and where it does have a parameter, for cases where that's allowed.
+ */
+@RunWith(JUnit4::class)
+class FactoryImplicitModulesTest {
+  @Component(modules = [AbstractModule::class])
+  internal interface AbstractModuleComponent {
+    fun string(): String
+
+    @Component.Factory
+    interface Factory {
+      fun create(): AbstractModuleComponent
+    }
+  }
+
+  @Test
+  fun abstractModule() {
+    val component = DaggerFactoryImplicitModulesTest_AbstractModuleComponent.factory().create()
+    assertThat(component.string()).isEqualTo("foo")
+  }
+
+  @Component(modules = [InstantiableConcreteModule::class])
+  internal interface InstantiableConcreteModuleComponent {
+    val int: Int
+
+    @Component.Factory
+    interface Factory {
+      fun create(): InstantiableConcreteModuleComponent
+    }
+  }
+
+  @Test
+  fun instantiableConcreteModule() {
+    val component =
+      DaggerFactoryImplicitModulesTest_InstantiableConcreteModuleComponent.factory().create()
+    assertThat(component.int).isEqualTo(42)
+  }
+
+  @Component(modules = [InstantiableConcreteModule::class])
+  internal interface InstantiableConcreteModuleWithFactoryParameterComponent {
+    val int: Int
+
+    @Component.Factory
+    interface Factory {
+      fun create(
+        module: InstantiableConcreteModule?
+      ): InstantiableConcreteModuleWithFactoryParameterComponent
+    }
+  }
+
+  @Test
+  fun instantiableConcreteModule_withFactoryParameter() {
+    val component =
+      DaggerFactoryImplicitModulesTest_InstantiableConcreteModuleWithFactoryParameterComponent
+        .factory()
+        .create(InstantiableConcreteModule())
+    assertThat(component.int).isEqualTo(42)
+  }
+
+  @Test
+  fun instantiableConcreteModule_withFactoryParameter_failsOnNull() {
+    try {
+      DaggerFactoryImplicitModulesTest_InstantiableConcreteModuleWithFactoryParameterComponent
+        .factory()
+        .create(null)
+      fail()
+    } catch (expected: NullPointerException) {}
+  }
+
+  @Component(modules = [ConcreteModuleThatCouldBeAbstract::class])
+  internal interface ConcreteModuleThatCouldBeAbstractComponent {
+    val double: Double
+
+    @Component.Factory
+    interface Factory {
+      fun create(): ConcreteModuleThatCouldBeAbstractComponent
+    }
+  }
+
+  @Test
+  fun concreteModuleThatCouldBeAbstract() {
+    val component =
+      DaggerFactoryImplicitModulesTest_ConcreteModuleThatCouldBeAbstractComponent.factory().create()
+    assertThat(component.double).isEqualTo(42.0)
+  }
+
+  @Component(modules = [ConcreteModuleThatCouldBeAbstract::class])
+  internal interface ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent {
+    val double: Double
+
+    @Component.Factory
+    interface Factory {
+      fun create(
+        module: ConcreteModuleThatCouldBeAbstract?
+      ): ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent
+    }
+  }
+
+  @Test
+  fun concreteModuleThatCouldBeAbstract_withFactoryParameter() {
+    val component: ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent =
+      DaggerFactoryImplicitModulesTest_ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent
+        .factory()
+        .create(ConcreteModuleThatCouldBeAbstract())
+    assertThat(component.double).isEqualTo(42.0)
+  }
+
+  @Test
+  fun concreteModuleThatCouldBeAbstract_withFactoryParameter_failsOnNull() {
+    // This matches what builders do when there's a setter for such a module; the setter checks that
+    // the argument is not null but otherwise ignores it.
+    // It's possible that we shouldn't even allow such a parameter for a factory, since unlike a
+    // builder, where the setter can just not be called, a factory doesn't give the option of not
+    // passing *something* for the unused parameter.
+    try {
+      DaggerFactoryImplicitModulesTest_ConcreteModuleThatCouldBeAbstractWithFactoryParameterComponent
+          .factory()
+          .create(null)
+      fail()
+    } catch (expected: NullPointerException) {}
+  }
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/FactoryMixedParametersTest.kt b/javatests/dagger/functional/kotlinsrc/factory/FactoryMixedParametersTest.kt
new file mode 100644
index 0000000..a4fda0e
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/FactoryMixedParametersTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import java.util.Random
+import javax.inject.Provider
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/** Tests for component factories with multiple parameters. */
+@RunWith(JUnit4::class)
+class FactoryMixedParametersTest {
+  @Component(
+    modules =
+      [
+        AbstractModule::class,
+        UninstantiableConcreteModule::class,
+        InstantiableConcreteModule::class
+      ],
+    dependencies = [Dependency::class]
+  )
+  internal interface MixedArgComponent {
+    fun getString(): String
+    val getInt: Int
+    val getLong: Long
+    fun getSomeObject(): Any
+    val getDouble: Double
+    fun getRandomProvider(): Provider<Random>
+
+    @Component.Factory
+    interface Factory {
+      fun create(
+        @BindsInstance d: Double,
+        dependency: Dependency,
+        module: UninstantiableConcreteModule,
+        @BindsInstance random: Random
+      ): MixedArgComponent
+    }
+  }
+
+  @Test
+  fun mixedArgComponent() {
+    val random = Random()
+    val component =
+      DaggerFactoryMixedParametersTest_MixedArgComponent.factory()
+        .create(3.0, Dependency(), UninstantiableConcreteModule(2L), random)
+    assertThat(component.getString()).isEqualTo("foo")
+    assertThat(component.getInt).isEqualTo(42)
+    assertThat(component.getDouble).isEqualTo(3.0)
+    assertThat(component.getSomeObject()).isEqualTo("bar")
+    assertThat(component.getLong).isEqualTo(2L)
+    assertThat(component.getRandomProvider().get()).isSameInstanceAs(random)
+    assertThat(component.getRandomProvider().get()).isSameInstanceAs(random)
+  }
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/FactoryRequiredModulesTest.kt b/javatests/dagger/functional/kotlinsrc/factory/FactoryRequiredModulesTest.kt
new file mode 100644
index 0000000..45d0280
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/FactoryRequiredModulesTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import com.google.common.truth.Truth.assertThat
+import dagger.Component
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/**
+ * Tests for factories for components that have a module that must have an instance provided by the
+ * user.
+ */
+@RunWith(JUnit4::class)
+class FactoryRequiredModulesTest {
+  @Component(modules = [UninstantiableConcreteModule::class])
+  internal interface UninstantiableConcreteModuleComponent {
+    val long: Long
+
+    @Component.Factory
+    interface Factory {
+      fun create(module: UninstantiableConcreteModule?): UninstantiableConcreteModuleComponent
+    }
+  }
+
+  @Test
+  fun uninstantiableConcreteModule() {
+    val component =
+      DaggerFactoryRequiredModulesTest_UninstantiableConcreteModuleComponent.factory()
+        .create(UninstantiableConcreteModule(42L))
+    assertThat(component.long).isEqualTo(42L)
+  }
+
+  @Test
+  fun uninstantiableConcreteModule_failsOnNull() {
+    try {
+      DaggerFactoryRequiredModulesTest_UninstantiableConcreteModuleComponent.factory().create(null)
+      fail()
+    } catch (expected: NullPointerException) {}
+  }
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/InstantiableConcreteModule.kt b/javatests/dagger/functional/kotlinsrc/factory/InstantiableConcreteModule.kt
new file mode 100644
index 0000000..a5be07a
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/InstantiableConcreteModule.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import dagger.Module
+import dagger.Provides
+
+// intentionally non-static
+@Module
+class InstantiableConcreteModule {
+  @Provides fun provideInt(): Int = 42
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/SubcomponentFactoryTest.kt b/javatests/dagger/functional/kotlinsrc/factory/SubcomponentFactoryTest.kt
new file mode 100644
index 0000000..9cdbb7c
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/SubcomponentFactoryTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import dagger.Subcomponent
+import javax.inject.Inject
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/**
+ * Tests for [subcomponent factories][Subcomponent.Factory].
+ *
+ * Most things are tested in `FactoryTest`; this is just intended to test some things like injecting
+ * subcomponent factories and returning them from component methods.
+ */
+@RunWith(JUnit4::class)
+class SubcomponentFactoryTest {
+  @Component
+  internal interface ParentWithSubcomponentFactory {
+    fun subcomponentFactory(): Sub.Factory
+
+    @Component.Factory
+    interface Factory {
+      fun create(@BindsInstance i: Int): ParentWithSubcomponentFactory
+    }
+  }
+
+  @Subcomponent
+  internal interface Sub {
+    fun i(): Int
+    fun s(): String
+
+    @Subcomponent.Factory
+    interface Factory {
+      fun create(@BindsInstance s: String): Sub
+    }
+  }
+
+  @Test
+  fun parentComponentWithSubcomponentFactoryEntryPoint() {
+    val parent = DaggerSubcomponentFactoryTest_ParentWithSubcomponentFactory.factory().create(3)
+    val subcomponent = parent.subcomponentFactory().create("foo")
+    assertThat(subcomponent.i()).isEqualTo(3)
+    assertThat(subcomponent.s()).isEqualTo("foo")
+  }
+
+  @Module(subcomponents = [Sub::class])
+  internal object ModuleWithSubcomponent {
+    @Provides fun provideInt(): Int = 42
+  }
+
+  internal class UsesSubcomponentFactory @Inject constructor(private val subFactory: Sub.Factory) {
+    fun getSubcomponent(s: String): Sub {
+      return subFactory.create(s)
+    }
+  }
+
+  @Component(modules = [ModuleWithSubcomponent::class])
+  internal interface ParentWithModuleWithSubcomponent {
+    fun usesSubcomponentFactory(): UsesSubcomponentFactory
+  }
+
+  @Test
+  fun parentComponentWithModuleWithSubcomponent() {
+    val parent = DaggerSubcomponentFactoryTest_ParentWithModuleWithSubcomponent.create()
+    val usesSubcomponentFactory = parent.usesSubcomponentFactory()
+    val subcomponent1 = usesSubcomponentFactory.getSubcomponent("foo")
+    assertThat(subcomponent1.i()).isEqualTo(42)
+    assertThat(subcomponent1.s()).isEqualTo("foo")
+    val subcomponent2 = usesSubcomponentFactory.getSubcomponent("bar")
+    assertThat(subcomponent2.i()).isEqualTo(42)
+    assertThat(subcomponent2.s()).isEqualTo("bar")
+  }
+}
diff --git a/javatests/dagger/functional/kotlinsrc/factory/UninstantiableConcreteModule.kt b/javatests/dagger/functional/kotlinsrc/factory/UninstantiableConcreteModule.kt
new file mode 100644
index 0000000..d6533a9
--- /dev/null
+++ b/javatests/dagger/functional/kotlinsrc/factory/UninstantiableConcreteModule.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlinsrc.factory
+
+import dagger.Module
+import dagger.Provides
+
+@Module
+class UninstantiableConcreteModule(private val l: Long) {
+  @Provides fun provideLong(): Long = l
+}