1 /*
2  * Copyright (C) 2020 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.cts.install.host;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assume.assumeFalse;
22 import static org.junit.Assume.assumeTrue;
23 
24 import android.cts.install.INSTALL_TYPE;
25 import android.platform.test.annotations.LargeTest;
26 
27 import com.android.tradefed.device.DeviceNotAvailableException;
28 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
29 
30 import org.junit.After;
31 import org.junit.Before;
32 import org.junit.Rule;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.junit.runners.Parameterized.Parameter;
36 import org.junit.runners.Parameterized.Parameters;
37 import org.junit.runners.Parameterized.UseParametersRunnerFactory;
38 
39 import java.util.ArrayList;
40 import java.util.Collection;
41 import java.util.List;
42 
43 @RunWith(DeviceParameterized.class)
44 @UseParametersRunnerFactory(DeviceParameterized.RunnerFactory.class)
45 public final class DowngradeTest extends BaseHostJUnit4Test {
46     private static final String PACKAGE_NAME = "android.cts.install";
47     private static final String PHASE_FORMAT_SUFFIX = "[%s_Staged%b_Rollback%b]";
48 
49     private static final String CLEAN_UP_PHASE = "cleanUp_phase";
50     private static final String ARRANGE_PHASE = "arrange_phase";
51     private static final String ASSERT_POST_ARRANGE_PHASE = "assert_postArrange_phase";
52     private static final String ACTION_PHASE = "action_phase";
53     private static final String ASSERT_DOWNGRADE_SUCCESS_PHASE = "assert_downgradeSuccess_phase";
54     private static final String ASSERT_POST_REBOOT_PHASE = "assert_postReboot_phase";
55     private static final String ASSERT_PRE_REBOOT_PHASE = "assert_preReboot_phase";
56     private static final String ASSERT_DOWNGRADE_NOT_ALLOWED_PHASE =
57             "assert_downgradeNotAllowed_phase";
58     private static final String ASSERT_DOWNGRADE_NOT_REQUESTED_PHASE =
59             "assert_downgradeNotRequested_phase";
60 
61     @Rule
62     public ShimApexRule mShimApexRule = new ShimApexRule(this);
63 
64     @Parameter(0)
65     public INSTALL_TYPE mInstallType;
66 
67     @Parameter(1)
68     public boolean mEnableRollback;
69 
70     @Parameters(name = "{0}_Rollback{1}")
combinations()71     public static Collection<Object[]> combinations() {
72         boolean[] booleanValues = new boolean[]{true, false};
73         List<Object[]> temp = new ArrayList<>();
74         for (INSTALL_TYPE installType : INSTALL_TYPE.values()) {
75             for (boolean enableRollback : booleanValues) {
76                 temp.add(new Object[]{installType, enableRollback});
77             }
78         }
79         return temp;
80     }
81 
82     @Before
83     @After
cleanUp()84     public void cleanUp() throws Exception {
85         runPhase(CLEAN_UP_PHASE);
86     }
87 
88     @Before
assumeApexSupported()89     public void assumeApexSupported() throws DeviceNotAvailableException {
90         if (mInstallType.containsApex()) {
91             assumeTrue("Device does not support updating APEX",
92                     mShimApexRule.isUpdatingApexSupported());
93         }
94     }
95 
96     @Test
testNonStagedDowngrade_downgradeNotRequested_fails()97     public void testNonStagedDowngrade_downgradeNotRequested_fails() throws Exception {
98         // Apex should not be committed in non-staged install, such logic covered in InstallTest.
99         assumeFalse(mInstallType.containsApex());
100         runPhase(ARRANGE_PHASE);
101         runPhase(ASSERT_POST_ARRANGE_PHASE);
102 
103         runPhase(ASSERT_DOWNGRADE_NOT_REQUESTED_PHASE);
104     }
105 
106     @Test
testNonStagedDowngrade_debugBuild()107     public void testNonStagedDowngrade_debugBuild() throws Exception {
108         // Apex should not be committed in non-staged install, such logic covered in InstallTest.
109         assumeFalse(mInstallType.containsApex());
110         assumeTrue("Device is not debuggable", isDebuggable());
111         runPhase(ARRANGE_PHASE);
112         runPhase(ASSERT_POST_ARRANGE_PHASE);
113 
114         runPhase(ACTION_PHASE);
115 
116         runPhase(ASSERT_DOWNGRADE_SUCCESS_PHASE);
117     }
118 
119     @Test
testNonStagedDowngrade_nonDebugBuild_fail()120     public void testNonStagedDowngrade_nonDebugBuild_fail() throws Exception {
121         // Apex should not be committed in non-staged install, such logic covered in InstallTest.
122         assumeFalse(mInstallType.containsApex());
123         assumeFalse("Device is debuggable", isDebuggable());
124         runPhase(ARRANGE_PHASE);
125         runPhase(ASSERT_POST_ARRANGE_PHASE);
126 
127         runPhase(ASSERT_DOWNGRADE_NOT_ALLOWED_PHASE);
128     }
129 
130     @Test
131     @LargeTest
testStagedDowngrade_downgradeNotRequested_fails()132     public void testStagedDowngrade_downgradeNotRequested_fails() throws Exception {
133         runStagedPhase(ARRANGE_PHASE);
134         getDevice().reboot();
135         runStagedPhase(ASSERT_POST_ARRANGE_PHASE);
136 
137         runStagedPhase(ASSERT_DOWNGRADE_NOT_REQUESTED_PHASE);
138     }
139 
140     @Test
141     @LargeTest
testStagedDowngrade_debugBuild()142     public void testStagedDowngrade_debugBuild() throws Exception {
143         assumeTrue("Device is not debuggable", isDebuggable());
144         runStagedPhase(ARRANGE_PHASE);
145         getDevice().reboot();
146         runStagedPhase(ASSERT_POST_ARRANGE_PHASE);
147 
148         runStagedPhase(ACTION_PHASE);
149 
150         runStagedPhase(ASSERT_PRE_REBOOT_PHASE);
151         getDevice().reboot();
152         runStagedPhase(ASSERT_POST_REBOOT_PHASE);
153     }
154 
155     @Test
156     @LargeTest
testStagedDowngrade_nonDebugBuild_fail()157     public void testStagedDowngrade_nonDebugBuild_fail() throws Exception {
158         assumeFalse("Device is debuggable", isDebuggable());
159         runStagedPhase(ARRANGE_PHASE);
160         getDevice().reboot();
161         runStagedPhase(ASSERT_POST_ARRANGE_PHASE);
162 
163         runStagedPhase(ASSERT_DOWNGRADE_NOT_ALLOWED_PHASE);
164     }
165 
runPhase(String phase)166     private void runPhase(String phase) throws DeviceNotAvailableException {
167         runPhase(phase, false /* staged */);
168     }
169 
runStagedPhase(String phase)170     private void runStagedPhase(String phase) throws DeviceNotAvailableException {
171         runPhase(phase, true /* staged */);
172     }
173 
174     /**
175      * Runs the given phase of a test with parameters by calling into the device.
176      * Throws an exception if the test phase fails.
177      * <p>
178      * For example, <code>runPhase("action_phase", true);</code>
179      */
runPhase(String phase, boolean staged)180     private void runPhase(String phase, boolean staged) throws DeviceNotAvailableException {
181         assertThat(runDeviceTests(PACKAGE_NAME,
182                 String.format("%s.%s", PACKAGE_NAME, this.getClass().getSimpleName()),
183                 String.format(phase + PHASE_FORMAT_SUFFIX, mInstallType, staged, mEnableRollback)))
184                 .isTrue();
185     }
186 
isDebuggable()187     private boolean isDebuggable() throws Exception {
188         return getDevice().getIntProperty("ro.debuggable", 0) == 1;
189     }
190 }
191