1 /*
2  * Copyright (C) 2022 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 android.car.test;
18 
19 import static android.car.test.JUnitHelper.newTestMethod;
20 
21 import static com.google.common.truth.Truth.assertWithMessage;
22 
23 import static org.junit.Assert.assertThrows;
24 import static org.mockito.Mockito.any;
25 import static org.mockito.Mockito.never;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.app.UiAutomation;
30 import android.car.test.JUnitHelper.SimpleStatement;
31 import android.car.test.PermissionsCheckerRule.EnsureHasPermission;
32 
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.junit.runner.Description;
36 import org.junit.runner.RunWith;
37 import org.mockito.ArgumentCaptor;
38 import org.mockito.Mock;
39 import org.mockito.junit.MockitoJUnitRunner;
40 
41 import java.lang.annotation.Annotation;
42 import java.util.Arrays;
43 import java.util.Set;
44 
45 @RunWith(MockitoJUnitRunner.class)
46 public final class PermissionsCheckerRuleTest {
47 
48     private static final String TAG = PermissionsCheckerRuleTest.class.getSimpleName();
49 
50     @Mock
51     private UiAutomation mUiAutomation;
52 
53     private final SimpleStatement<Exception> mBaseStatement = new SimpleStatement<>();
54 
55     private PermissionsCheckerRule mRule;
56 
57     @Before
setFixtures()58     public void setFixtures() {
59         mRule = new PermissionsCheckerRule(mUiAutomation);
60     }
61 
62     @Test
testNoAnnotation()63     public void testNoAnnotation() throws Throwable {
64         Description testMethod = newTestMethod();
65 
66         mRule.apply(mBaseStatement, testMethod).evaluate();
67 
68         mBaseStatement.assertEvaluated();
69         verify(mUiAutomation, never()).adoptShellPermissionIdentity(any(String[].class));
70         verify(mUiAutomation, never()).dropShellPermissionIdentity();
71     }
72 
73     @Test
testEnsureHasPermission_onePermission()74     public void testEnsureHasPermission_onePermission() throws Throwable {
75         Description testMethod = newTestMethod(new EnsureHasPermissionAnnotation("To Kill"));
76 
77         mRule.apply(mBaseStatement, testMethod).evaluate();
78 
79         mBaseStatement.assertEvaluated();
80         verify(mUiAutomation).adoptShellPermissionIdentity("To Kill");
81         verify(mUiAutomation).dropShellPermissionIdentity();
82     }
83 
84     @Test
testEnsureHasPermission_onePermissionTestThrows()85     public void testEnsureHasPermission_onePermissionTestThrows() throws Throwable {
86         Description testMethod = newTestMethod(new EnsureHasPermissionAnnotation("To Kill"));
87         Throwable exception = new Throwable("D'OH!");
88         mBaseStatement.failWith(exception);
89 
90         Throwable actualException = assertThrows(Throwable.class,
91                 () -> mRule.apply(mBaseStatement, testMethod).evaluate());
92 
93         assertWithMessage("exception thrown").that(actualException).isSameInstanceAs(exception);
94         verify(mUiAutomation).adoptShellPermissionIdentity("To Kill");
95         verify(mUiAutomation).dropShellPermissionIdentity();
96     }
97 
98     @Test
testEnsureHasPermission_multiplePermissions()99     public void testEnsureHasPermission_multiplePermissions() throws Throwable {
100         Description testMethod = newTestMethod(new EnsureHasPermissionAnnotation("To", "Kill"));
101 
102         mRule.apply(mBaseStatement, testMethod).evaluate();
103 
104         mBaseStatement.assertEvaluated();
105         verify(mUiAutomation).adoptShellPermissionIdentity("To", "Kill");
106         verify(mUiAutomation).dropShellPermissionIdentity();
107     }
108 
109     @Test
testEnsureHasPermission_permissionsAdoptedBefore()110     public void testEnsureHasPermission_permissionsAdoptedBefore() throws Throwable {
111         Description testMethod = newTestMethod(new EnsureHasPermissionAnnotation("To Kill"));
112         when(mUiAutomation.getAdoptedShellPermissions()).thenReturn(Set.of("Thou shalt not kill!"));
113 
114         mRule.apply(mBaseStatement, testMethod).evaluate();
115 
116         mBaseStatement.assertEvaluated();
117         verify(mUiAutomation).adoptShellPermissionIdentity("To Kill");
118         verify(mUiAutomation).dropShellPermissionIdentity();
119         verify(mUiAutomation).adoptShellPermissionIdentity("Thou shalt not kill!");
120     }
121 
122     @Test
testEnsureHasPermission_annotatedClass()123     public void testEnsureHasPermission_annotatedClass() throws Throwable {
124         Description testMethod = Description.createTestDescription(SuperClass.class, "testIAm",
125                 new EnsureHasPermissionAnnotation("To", "Kill"));
126 
127         mRule.apply(mBaseStatement, testMethod).evaluate();
128 
129         mBaseStatement.assertEvaluated();
130 
131         // Must use a captor as there's no guarantee of the order in the set
132         ArgumentCaptor<String[]> permissionsCaptor = ArgumentCaptor.forClass(String[].class);
133         verify(mUiAutomation).adoptShellPermissionIdentity(permissionsCaptor.capture());
134         assertWithMessage("arguments of adoptShellPermissionIdentity() call")
135                 .that(permissionsCaptor.getAllValues())
136                 .containsExactly("SuPermission", "Common", "To", "Kill");
137         verify(mUiAutomation).dropShellPermissionIdentity();
138     }
139 
140     @Test
testEnsureHasPermission_annotatedClassAndParent()141     public void testEnsureHasPermission_annotatedClassAndParent() throws Throwable {
142         Description testMethod = Description.createTestDescription(SubClass.class, "testIAm",
143                 new EnsureHasPermissionAnnotation("To", "Kill"));
144 
145         mRule.apply(mBaseStatement, testMethod).evaluate();
146 
147 
148         // Must use a captor as there's no guarantee of the order in the set
149         ArgumentCaptor<String[]> permissionsCaptor = ArgumentCaptor.forClass(String[].class);
150         verify(mUiAutomation).adoptShellPermissionIdentity(permissionsCaptor.capture());
151         assertWithMessage("arguments of adoptShellPermissionIdentity() call")
152                 .that(permissionsCaptor.getAllValues())
153                 .containsExactly("SuPermission", "Common", "WhatSub", "To", "Kill");
154         verify(mUiAutomation).dropShellPermissionIdentity();
155     }
156 
157     @EnsureHasPermission(value = {"SuPermission", "Common"})
158     private static class SuperClass {
159 
160     }
161 
162     @EnsureHasPermission(value = {"Common", "WhatSub"})
163     private static class SubClass extends SuperClass {
164 
165     }
166 
167     @SuppressWarnings("BadAnnotationImplementation") // We don't care about equals() / hashCode()
168     private static final class EnsureHasPermissionAnnotation implements EnsureHasPermission {
169 
170         private final String[] mValue;
171 
EnsureHasPermissionAnnotation(String... value)172         EnsureHasPermissionAnnotation(String... value) {
173             mValue = value;
174         }
175 
176         @Override
annotationType()177         public Class<? extends Annotation> annotationType() {
178             return EnsureHasPermission.class;
179         }
180 
181         @Override
value()182         public String[] value() {
183             return mValue;
184         }
185 
186         @Override
toString()187         public String toString() {
188             return "@EnsureHasPermissionAnnotation(" + Arrays.toString(mValue) + ")";
189         }
190     }
191 }
192