1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | package org.chromium.chrome.testshell; |
6 | |
7 | import android.content.ComponentName; |
8 | import android.content.Context; |
9 | import android.content.Intent; |
10 | import android.net.Uri; |
11 | import android.test.ActivityInstrumentationTestCase2; |
12 | import android.text.TextUtils; |
13 | |
14 | import org.chromium.content.browser.test.util.Criteria; |
15 | import org.chromium.content.browser.test.util.CriteriaHelper; |
16 | |
17 | import java.io.File; |
18 | import java.util.concurrent.atomic.AtomicBoolean; |
19 | |
20 | /** |
21 | * Base test class for all ChromiumTestShell based tests. |
22 | */ |
23 | public class ChromiumTestShellTestBase extends |
24 | ActivityInstrumentationTestCase2<ChromiumTestShellActivity> { |
25 | /** The maximum time the waitForActiveShellToBeDoneLoading method will wait. */ |
26 | private static final long WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT = 10000; |
27 | |
28 | public ChromiumTestShellTestBase() { |
29 | super(ChromiumTestShellActivity.class); |
30 | } |
31 | |
32 | /** |
33 | * Starts the ChromiumTestShell activity and loads the given URL. |
34 | */ |
35 | protected ChromiumTestShellActivity launchChromiumTestShellWithUrl(String url) { |
36 | Intent intent = new Intent(Intent.ACTION_MAIN); |
37 | intent.addCategory(Intent.CATEGORY_LAUNCHER); |
38 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
39 | if (url != null) intent.setData(Uri.parse(url)); |
40 | intent.setComponent(new ComponentName(getInstrumentation().getTargetContext(), |
41 | ChromiumTestShellActivity.class)); |
42 | setActivityIntent(intent); |
43 | return getActivity(); |
44 | } |
45 | |
46 | /** |
47 | * Starts the ChromiumTestShell activity and loads a blank page. |
48 | */ |
49 | protected ChromiumTestShellActivity launchChromiumTestShellWithBlankPage() { |
50 | return launchChromiumTestShellWithUrl("about:blank"); |
51 | } |
52 | |
53 | /** |
54 | * Waits for the Active shell to finish loading. This times out after |
55 | * WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT milliseconds and it shouldn't be used for long |
56 | * loading pages. Instead it should be used more for test initialization. The proper way |
57 | * to wait is to use a TestCallbackHelperContainer after the initial load is completed. |
58 | * @return Whether or not the Shell was actually finished loading. |
59 | * @throws Exception |
60 | */ |
61 | protected boolean waitForActiveShellToBeDoneLoading() throws InterruptedException { |
62 | final ChromiumTestShellActivity activity = getActivity(); |
63 | |
64 | // Wait for the Content Shell to be initialized. |
65 | return CriteriaHelper.pollForCriteria(new Criteria() { |
66 | @Override |
67 | public boolean isSatisfied() { |
68 | try { |
69 | final AtomicBoolean isLoaded = new AtomicBoolean(false); |
70 | runTestOnUiThread(new Runnable() { |
71 | @Override |
72 | public void run() { |
73 | TestShellTab tab = activity.getActiveTab(); |
74 | if (tab != null) { |
75 | isLoaded.set(!tab.isLoading() |
76 | && !TextUtils.isEmpty(tab.getContentView().getUrl())); |
77 | } else { |
78 | isLoaded.set(false); |
79 | } |
80 | } |
81 | }); |
82 | |
83 | return isLoaded.get(); |
84 | } catch (Throwable e) { |
85 | return false; |
86 | } |
87 | } |
88 | }, WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL); |
89 | } |
90 | |
91 | /** |
92 | * Clear all files in the testshell's directory except 'lib'. |
93 | * |
94 | * @return Whether clearing the application data was successful. |
95 | */ |
96 | protected boolean clearAppData() throws Exception { |
97 | final int MAX_TIMEOUT_MS = 3000; |
98 | final String appDir = getAppDir(); |
99 | return CriteriaHelper.pollForCriteria( |
100 | new Criteria() { |
101 | private boolean mDataRemoved = false; |
102 | @Override |
103 | public boolean isSatisfied() { |
104 | if (!mDataRemoved && !removeAppData(appDir)) { |
105 | return false; |
106 | } |
107 | mDataRemoved = true; |
108 | // We have to make sure the cache directory still exists, as the framework |
109 | // will try to create it otherwise and will fail for sandbox processes with |
110 | // an NPE. |
111 | File cacheDir = new File(appDir, "cache"); |
112 | if (cacheDir.exists()) { |
113 | return true; |
114 | } |
115 | return cacheDir.mkdir(); |
116 | } |
117 | }, |
118 | MAX_TIMEOUT_MS, MAX_TIMEOUT_MS / 10); |
119 | } |
120 | |
121 | /** |
122 | * Remove all files and directories under the given appDir except 'lib' |
123 | * |
124 | * @param appDir the app directory to remove. |
125 | * |
126 | * @return true if succeeded. |
127 | */ |
128 | private boolean removeAppData(String appDir) { |
129 | File[] files = new File(appDir).listFiles(); |
130 | if (files == null) { |
131 | return true; |
132 | } |
133 | for (File file : files) { |
134 | if (!file.getAbsolutePath().endsWith("/lib") && !removeFile(file)) { |
135 | return false; |
136 | } |
137 | } |
138 | return true; |
139 | } |
140 | |
141 | private String getAppDir() { |
142 | Context target_ctx = getInstrumentation().getTargetContext(); |
143 | String cacheDir = target_ctx.getCacheDir().getAbsolutePath(); |
144 | return cacheDir.substring(0, cacheDir.lastIndexOf('/')); |
145 | } |
146 | |
147 | /** |
148 | * Remove the given file or directory. |
149 | * |
150 | * @param file the file or directory to remove. |
151 | * |
152 | * @return true if succeeded. |
153 | */ |
154 | private static boolean removeFile(File file) { |
155 | if (file.isDirectory()) { |
156 | File[] files = file.listFiles(); |
157 | if (files == null) { |
158 | return true; |
159 | } |
160 | for (File sub_file : files) { |
161 | if (!removeFile(sub_file)) |
162 | return false; |
163 | } |
164 | } |
165 | return file.delete(); |
166 | } |
167 | |
168 | |
169 | /** |
170 | * Navigates the currently active tab to a sanitized version of {@code url}. |
171 | * @param url The potentially unsanitized URL to navigate to. |
172 | */ |
173 | public void loadUrlWithSanitization(final String url) throws InterruptedException { |
174 | getInstrumentation().runOnMainSync(new Runnable() { |
175 | @Override |
176 | public void run() { |
177 | getActivity().getActiveTab().loadUrlWithSanitization(url); |
178 | } |
179 | }); |
180 | waitForActiveShellToBeDoneLoading(); |
181 | } |
182 | } |