• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.textclassifier.testing;
18 
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.ArgumentMatchers.anyInt;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.when;
23 
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.ContextWrapper;
27 import android.content.Intent;
28 import android.content.pm.ActivityInfo;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.ResolveInfo;
32 import androidx.test.core.app.ApplicationProvider;
33 import com.google.common.base.Preconditions;
34 import com.google.errorprone.annotations.CanIgnoreReturnValue;
35 import java.util.HashMap;
36 import java.util.Map;
37 import java.util.UUID;
38 import javax.annotation.Nullable;
39 import org.mockito.stubbing.Answer;
40 
41 /** A builder used to build a fake context for testing. */
42 public final class FakeContextBuilder {
43 
44   /** A component name that can be used for tests. */
45   public static final ComponentName DEFAULT_COMPONENT = new ComponentName("pkg", "cls");
46 
47   private final PackageManager packageManager;
48   private final ContextWrapper context;
49   private final Map<String, ComponentName> components = new HashMap<>();
50   private final Map<String, CharSequence> appLabels = new HashMap<>();
51   @Nullable private ComponentName allIntentComponent;
52 
FakeContextBuilder()53   public FakeContextBuilder() {
54     packageManager = mock(PackageManager.class);
55     when(packageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(null);
56     context =
57         new ContextWrapper(ApplicationProvider.getApplicationContext()) {
58           @Override
59           public PackageManager getPackageManager() {
60             return packageManager;
61           }
62         };
63   }
64 
65   /**
66    * Sets the component name of an activity to handle the specified intent action.
67    *
68    * <p><strong>NOTE: </strong>By default, no component is set to handle any intent.
69    */
70   @CanIgnoreReturnValue
setIntentComponent( String intentAction, @Nullable ComponentName component)71   public FakeContextBuilder setIntentComponent(
72       String intentAction, @Nullable ComponentName component) {
73     Preconditions.checkNotNull(intentAction);
74     components.put(intentAction, component);
75     return this;
76   }
77 
78   /** Sets the app label res for a specified package. */
79   @CanIgnoreReturnValue
setAppLabel(String packageName, @Nullable CharSequence appLabel)80   public FakeContextBuilder setAppLabel(String packageName, @Nullable CharSequence appLabel) {
81     Preconditions.checkNotNull(packageName);
82     appLabels.put(packageName, appLabel);
83     return this;
84   }
85 
86   /**
87    * Sets the component name of an activity to handle all intents.
88    *
89    * <p><strong>NOTE: </strong>By default, no component is set to handle any intent.
90    */
91   @CanIgnoreReturnValue
setAllIntentComponent(@ullable ComponentName component)92   public FakeContextBuilder setAllIntentComponent(@Nullable ComponentName component) {
93     allIntentComponent = component;
94     return this;
95   }
96 
97   /** Builds and returns a fake context. */
build()98   public Context build() {
99     when(packageManager.resolveActivity(any(Intent.class), anyInt()))
100         .thenAnswer(
101             (Answer<ResolveInfo>)
102                 invocation -> {
103                   final String action = ((Intent) invocation.getArgument(0)).getAction();
104                   final ComponentName component =
105                       components.containsKey(action) ? components.get(action) : allIntentComponent;
106                   return getResolveInfo(component);
107                 });
108     when(packageManager.getApplicationLabel(any(ApplicationInfo.class)))
109         .thenAnswer(
110             (Answer<CharSequence>)
111                 invocation -> {
112                   ApplicationInfo applicationInfo = invocation.getArgument(0);
113                   return appLabels.get(applicationInfo.packageName);
114                 });
115     return context;
116   }
117 
118   /** Returns a component name with random package and class names. */
newComponent()119   public static ComponentName newComponent() {
120     return new ComponentName(UUID.randomUUID().toString(), UUID.randomUUID().toString());
121   }
122 
getResolveInfo(ComponentName component)123   private static ResolveInfo getResolveInfo(ComponentName component) {
124     final ResolveInfo info;
125     if (component == null) {
126       info = null;
127     } else {
128       // NOTE: If something breaks in TextClassifier because we expect more fields to be set
129       // in here, just add them.
130       info = new ResolveInfo();
131       info.activityInfo = new ActivityInfo();
132       info.activityInfo.packageName = component.getPackageName();
133       info.activityInfo.name = component.getClassName();
134       info.activityInfo.exported = true;
135       info.activityInfo.applicationInfo = new ApplicationInfo();
136       info.activityInfo.applicationInfo.packageName = component.getPackageName();
137       info.activityInfo.applicationInfo.icon = 0;
138     }
139     return info;
140   }
141 }
142