forked from square/dagger
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add EarlyTestEntryPoints to allow entry points to be called in tests …
…before the test instance is instantiated. See #2016 RELNOTES=Add EarlyTestEntryPoints to allow entry points to be called in tests before the test instance is instantiated. PiperOrigin-RevId: 359350904
- Loading branch information
Showing
52 changed files
with
1,758 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright (C) 2021 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.hilt.android; | ||
|
||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* An escape hatch for when a Hilt entry point usage needs to be called before the singleton | ||
* component is available in a Hilt test. | ||
* | ||
* <p>Warning: Please see documentation for more details: | ||
* https://dagger.dev/hilt/early-entry-point | ||
* | ||
* <h1>Usage: | ||
* | ||
* <p>To use, annotate an existing entry point with {@link EarlyEntryPoint} as shown below: | ||
* | ||
* <pre>{@code | ||
* @EarlyEntryPoint | ||
* @EntryPoint | ||
* @InstallIn(SingletonComponent.class) | ||
* interface FooEntryPoint { | ||
* Foo getFoo(); | ||
* } | ||
* }</pre> | ||
* | ||
* <p>Then, replace any usages of {@link EntryPoints} with {@link EarlyEntryPoints}, as shown below: | ||
* | ||
* <pre>{@code | ||
* // EarlyEntryPoints.get() must be used with entry points annotated with @EarlyEntryPoint | ||
* // This entry point can now be called at any point during a test, e.g. in Application.onCreate(). | ||
* Foo foo = EarlyEntryPoints.get(appContext, FooEntryPoint.class).getFoo(); | ||
* }</pre> | ||
*/ | ||
@Retention(RUNTIME) // Needs to be runtime for checks in EntryPoints and EarlyEntryPoints. | ||
@Target(ElementType.TYPE) | ||
public @interface EarlyEntryPoint {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* Copyright (C) 2021 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.hilt.android; | ||
|
||
import android.content.Context; | ||
import dagger.hilt.EntryPoints; | ||
import dagger.hilt.internal.GeneratedComponentManagerHolder; | ||
import dagger.hilt.internal.Preconditions; | ||
import dagger.hilt.internal.TestSingletonComponentManager; | ||
import java.lang.annotation.Annotation; | ||
import javax.annotation.Nonnull; | ||
|
||
/** Static utility methods for accessing entry points annotated with {@link EarlyEntryPoint}. */ | ||
public final class EarlyEntryPoints { | ||
|
||
/** | ||
* Returns the early entry point interface given a component manager holder. Note that this | ||
* performs an unsafe cast and so callers should be sure that the given component/component | ||
* manager matches the early entry point interface that is given. | ||
* | ||
* @param applicationContext The application context. | ||
* @param entryPoint The interface marked with {@link EarlyEntryPoint}. The {@link | ||
* dagger.hilt.InstallIn} annotation on this entry point should match the component argument | ||
* above. | ||
*/ | ||
// Note that the input is not statically declared to be a Component or ComponentManager to make | ||
// this method easier to use, since most code will use this with an Application or Context type. | ||
@Nonnull | ||
public static <T> T get(Context applicationContext, Class<T> entryPoint) { | ||
applicationContext = applicationContext.getApplicationContext(); | ||
Preconditions.checkState( | ||
applicationContext instanceof GeneratedComponentManagerHolder, | ||
"Expected application context to implement GeneratedComponentManagerHolder. " | ||
+ "Check that you're passing in an application context that uses Hilt."); | ||
Object componentManager = | ||
((GeneratedComponentManagerHolder) applicationContext).componentManager(); | ||
if (componentManager instanceof TestSingletonComponentManager) { | ||
Preconditions.checkState( | ||
hasAnnotationReflection(entryPoint, EarlyEntryPoint.class), | ||
"%s should be called with EntryPoints.get() rather than EarlyEntryPoints.get()", | ||
entryPoint.getCanonicalName()); | ||
Object earlyComponent = | ||
((TestSingletonComponentManager) componentManager).earlySingletonComponent(); | ||
return entryPoint.cast(earlyComponent); | ||
} | ||
|
||
// @EarlyEntryPoint only has an effect in test environment, so if this is not a test we | ||
// delegate to EntryPoints. | ||
return EntryPoints.get(applicationContext, entryPoint); | ||
} | ||
|
||
// Note: This method uses reflection but it should only be called in test environments. | ||
private static boolean hasAnnotationReflection( | ||
Class<?> clazz, Class<? extends Annotation> annotationClazz) { | ||
for (Annotation annotation : clazz.getAnnotations()) { | ||
if (annotation.annotationType().equals(annotationClazz)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
private EarlyEntryPoints() {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
java/dagger/hilt/android/internal/testing/EarlySingletonComponentCreator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* Copyright (C) 2021 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.hilt.android.internal.testing; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
|
||
/** Creates a test's early component. */ | ||
public abstract class EarlySingletonComponentCreator { | ||
private static final String EARLY_SINGLETON_COMPONENT_CREATOR_IMPL = | ||
"dagger.hilt.android.internal.testing.EarlySingletonComponentCreatorImpl"; | ||
|
||
static Object createComponent() { | ||
try { | ||
return Class.forName(EARLY_SINGLETON_COMPONENT_CREATOR_IMPL) | ||
.asSubclass(EarlySingletonComponentCreator.class) | ||
.getDeclaredConstructor() | ||
.newInstance() | ||
.create(); | ||
} catch (ClassNotFoundException | ||
| NoSuchMethodException | ||
| IllegalAccessException | ||
| InstantiationException | ||
| InvocationTargetException e) { | ||
throw new RuntimeException( | ||
"The EarlyComponent was requested but does not exist. Check that you have annotated " | ||
+ "your test class with @HiltAndroidTest and that the processor is running over your " | ||
+ "test.", | ||
e); | ||
} | ||
} | ||
|
||
/** Creates the early test component. */ | ||
abstract Object create(); | ||
} |
Oops, something went wrong.