Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / file_system / file_system_api_unittest.cc
blobe6df024d23bb7bd3347623d97d3757f86d2334b8
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.
5 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
7 #include <vector>
9 #include "base/files/file_path.h"
10 #include "base/run_loop.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/shell_dialogs/select_file_dialog.h"
17 #if defined(OS_CHROMEOS)
18 #include "base/memory/ref_counted.h"
19 #include "base/memory/weak_ptr.h"
20 #include "base/prefs/testing_pref_service.h"
21 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
22 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
23 #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
24 #include "chrome/test/base/testing_browser_process.h"
25 #include "content/public/test/test_browser_thread_bundle.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/extension_builder.h"
28 #include "extensions/common/manifest.h"
29 #include "extensions/common/test_util.h"
30 #include "extensions/common/value_builder.h"
31 #endif
33 using extensions::FileSystemChooseEntryFunction;
34 using extensions::api::file_system::AcceptOption;
36 #if defined(OS_CHROMEOS)
37 using extensions::file_system_api::ConsentProvider;
38 using file_manager::Volume;
39 #endif
41 namespace extensions {
42 namespace {
44 void CheckExtensions(const std::vector<base::FilePath::StringType>& expected,
45 const std::vector<base::FilePath::StringType>& actual) {
46 EXPECT_EQ(expected.size(), actual.size());
47 if (expected.size() != actual.size())
48 return;
50 for (size_t i = 0; i < expected.size(); ++i) {
51 EXPECT_EQ(expected[i], actual[i]);
55 AcceptOption* BuildAcceptOption(const std::string& description,
56 const std::string& mime_types,
57 const std::string& extensions) {
58 AcceptOption* option = new AcceptOption();
60 if (!description.empty())
61 option->description.reset(new std::string(description));
63 if (!mime_types.empty()) {
64 option->mime_types.reset(new std::vector<std::string>(
65 base::SplitString(mime_types, ",",
66 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)));
69 if (!extensions.empty()) {
70 option->extensions.reset(new std::vector<std::string>(
71 base::SplitString(extensions, ",",
72 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)));
75 return option;
78 #if defined(OS_WIN)
79 #define ToStringType base::UTF8ToWide
80 #else
81 #define ToStringType
82 #endif
84 #if defined(OS_CHROMEOS)
85 class TestingConsentProviderDelegate
86 : public ConsentProvider::DelegateInterface {
87 public:
88 TestingConsentProviderDelegate()
89 : show_dialog_counter_(0),
90 show_notification_counter_(0),
91 dialog_button_(ui::DIALOG_BUTTON_NONE),
92 is_auto_launched_(false) {}
94 ~TestingConsentProviderDelegate() {}
96 // Sets a fake dialog response.
97 void SetDialogButton(ui::DialogButton button) { dialog_button_ = button; }
99 // Sets a fake result of detection the auto launch kiosk mode.
100 void SetIsAutoLaunched(bool is_auto_launched) {
101 is_auto_launched_ = is_auto_launched;
104 // Sets a whitelisted components list with a single id.
105 void SetComponentWhitelist(const std::string& extension_id) {
106 whitelisted_component_id_ = extension_id;
109 int show_dialog_counter() const { return show_dialog_counter_; }
110 int show_notification_counter() const { return show_notification_counter_; }
112 private:
113 // ConsentProvider::DelegateInterface overrides:
114 void ShowDialog(
115 const extensions::Extension& extension,
116 const base::WeakPtr<Volume>& volume,
117 bool writable,
118 const ConsentProvider::ShowDialogCallback& callback) override {
119 ++show_dialog_counter_;
120 callback.Run(dialog_button_);
123 void ShowNotification(const extensions::Extension& extension,
124 const base::WeakPtr<Volume>& volume,
125 bool writable) override {
126 ++show_notification_counter_;
129 bool IsAutoLaunched(const extensions::Extension& extension) override {
130 return is_auto_launched_;
133 bool IsWhitelistedComponent(const extensions::Extension& extension) override {
134 return whitelisted_component_id_.compare(extension.id()) == 0;
137 int show_dialog_counter_;
138 int show_notification_counter_;
139 ui::DialogButton dialog_button_;
140 bool is_auto_launched_;
141 std::string whitelisted_component_id_;
143 DISALLOW_COPY_AND_ASSIGN(TestingConsentProviderDelegate);
146 // Rewrites result of a consent request from |result| to |log|.
147 void OnConsentReceived(ConsentProvider::Consent* log,
148 const ConsentProvider::Consent result) {
149 *log = result;
151 #endif
153 } // namespace
155 #if defined(OS_CHROMEOS)
156 class FileSystemApiConsentProviderTest : public testing::Test {
157 public:
158 FileSystemApiConsentProviderTest() {}
160 void SetUp() override {
161 testing_pref_service_.reset(new TestingPrefServiceSimple);
162 TestingBrowserProcess::GetGlobal()->SetLocalState(
163 testing_pref_service_.get());
164 user_manager_ = new chromeos::FakeChromeUserManager;
165 scoped_user_manager_enabler_.reset(
166 new chromeos::ScopedUserManagerEnabler(user_manager_));
169 void TearDown() override {
170 scoped_user_manager_enabler_.reset();
171 user_manager_ = nullptr;
172 testing_pref_service_.reset();
173 TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
176 protected:
177 base::WeakPtr<Volume> volume_;
178 scoped_ptr<TestingPrefServiceSimple> testing_pref_service_;
179 chromeos::FakeChromeUserManager*
180 user_manager_; // Owned by the scope enabler.
181 scoped_ptr<chromeos::ScopedUserManagerEnabler> scoped_user_manager_enabler_;
182 content::TestBrowserThreadBundle thread_bundle_;
184 #endif
186 TEST(FileSystemApiUnitTest, FileSystemChooseEntryFunctionFileTypeInfoTest) {
187 // AcceptsAllTypes is ignored when no other extensions are available.
188 ui::SelectFileDialog::FileTypeInfo file_type_info;
189 bool acceptsAllTypes = false;
190 FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
191 base::FilePath::StringType(), NULL, &acceptsAllTypes);
192 EXPECT_TRUE(file_type_info.include_all_files);
193 EXPECT_TRUE(file_type_info.extensions.empty());
195 // Test grouping of multiple types.
196 file_type_info = ui::SelectFileDialog::FileTypeInfo();
197 std::vector<linked_ptr<AcceptOption> > options;
198 options.push_back(linked_ptr<AcceptOption>(BuildAcceptOption(
199 std::string(), "application/x-chrome-extension", "jso")));
200 acceptsAllTypes = false;
201 FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
202 base::FilePath::StringType(), &options, &acceptsAllTypes);
203 EXPECT_FALSE(file_type_info.include_all_files);
204 ASSERT_EQ(file_type_info.extensions.size(), (size_t) 1);
205 EXPECT_TRUE(file_type_info.extension_description_overrides[0].empty()) <<
206 "No override must be specified for boring accept types";
207 // Note here (and below) that the expectedTypes are sorted, because we use a
208 // set internally to generate the output: thus, the output is sorted.
209 std::vector<base::FilePath::StringType> expectedTypes;
210 expectedTypes.push_back(ToStringType("crx"));
211 expectedTypes.push_back(ToStringType("jso"));
212 CheckExtensions(expectedTypes, file_type_info.extensions[0]);
214 // Test that not satisfying the extension will force all types.
215 file_type_info = ui::SelectFileDialog::FileTypeInfo();
216 options.clear();
217 options.push_back(linked_ptr<AcceptOption>(BuildAcceptOption(
218 std::string(), std::string(), "unrelated")));
219 acceptsAllTypes = false;
220 FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
221 ToStringType(".jso"), &options, &acceptsAllTypes);
222 EXPECT_TRUE(file_type_info.include_all_files);
224 // Test multiple list entries, all containing their own types.
225 file_type_info = ui::SelectFileDialog::FileTypeInfo();
226 options.clear();
227 options.push_back(linked_ptr<AcceptOption>(
228 BuildAcceptOption(std::string(), std::string(), "jso,js")));
229 options.push_back(linked_ptr<AcceptOption>(
230 BuildAcceptOption(std::string(), std::string(), "cpp,cc")));
231 acceptsAllTypes = false;
232 FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
233 base::FilePath::StringType(), &options, &acceptsAllTypes);
234 ASSERT_EQ(file_type_info.extensions.size(), options.size());
236 expectedTypes.clear();
237 expectedTypes.push_back(ToStringType("js"));
238 expectedTypes.push_back(ToStringType("jso"));
239 CheckExtensions(expectedTypes, file_type_info.extensions[0]);
241 expectedTypes.clear();
242 expectedTypes.push_back(ToStringType("cc"));
243 expectedTypes.push_back(ToStringType("cpp"));
244 CheckExtensions(expectedTypes, file_type_info.extensions[1]);
246 // Test accept type that causes description override.
247 file_type_info = ui::SelectFileDialog::FileTypeInfo();
248 options.clear();
249 options.push_back(linked_ptr<AcceptOption>(
250 BuildAcceptOption(std::string(), "image/*", "html")));
251 acceptsAllTypes = false;
252 FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
253 base::FilePath::StringType(), &options, &acceptsAllTypes);
254 ASSERT_EQ(file_type_info.extension_description_overrides.size(), (size_t) 1);
255 EXPECT_FALSE(file_type_info.extension_description_overrides[0].empty()) <<
256 "Accept type \"image/*\" must generate description override";
258 // Test multiple accept types that cause description override causes us to
259 // still present the default.
260 file_type_info = ui::SelectFileDialog::FileTypeInfo();
261 options.clear();
262 options.push_back(linked_ptr<AcceptOption>(BuildAcceptOption(
263 std::string(), "image/*,audio/*,video/*", std::string())));
264 acceptsAllTypes = false;
265 FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
266 base::FilePath::StringType(), &options, &acceptsAllTypes);
267 ASSERT_EQ(file_type_info.extension_description_overrides.size(), (size_t) 1);
268 EXPECT_TRUE(file_type_info.extension_description_overrides[0].empty());
270 // Test explicit description override.
271 file_type_info = ui::SelectFileDialog::FileTypeInfo();
272 options.clear();
273 options.push_back(linked_ptr<AcceptOption>(
274 BuildAcceptOption("File Types 101", "image/jpeg", std::string())));
275 acceptsAllTypes = false;
276 FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
277 base::FilePath::StringType(), &options, &acceptsAllTypes);
278 EXPECT_EQ(file_type_info.extension_description_overrides[0],
279 base::UTF8ToUTF16("File Types 101"));
282 TEST(FileSystemApiUnitTest, FileSystemChooseEntryFunctionSuggestionTest) {
283 std::string opt_name;
284 base::FilePath suggested_name;
285 base::FilePath::StringType suggested_extension;
287 opt_name = std::string("normal_path.txt");
288 FileSystemChooseEntryFunction::BuildSuggestion(&opt_name, &suggested_name,
289 &suggested_extension);
290 EXPECT_FALSE(suggested_name.IsAbsolute());
291 EXPECT_EQ(suggested_name.MaybeAsASCII(), "normal_path.txt");
292 EXPECT_EQ(suggested_extension, ToStringType("txt"));
294 // We should provide just the basename, i.e., "path".
295 opt_name = std::string("/a/bad/path");
296 FileSystemChooseEntryFunction::BuildSuggestion(&opt_name, &suggested_name,
297 &suggested_extension);
298 EXPECT_FALSE(suggested_name.IsAbsolute());
299 EXPECT_EQ(suggested_name.MaybeAsASCII(), "path");
300 EXPECT_TRUE(suggested_extension.empty());
302 #if !defined(OS_WIN)
303 // TODO(thorogood): Fix this test on Windows.
304 // Filter out absolute paths with no basename.
305 opt_name = std::string("/");
306 FileSystemChooseEntryFunction::BuildSuggestion(&opt_name, &suggested_name,
307 &suggested_extension);
308 EXPECT_FALSE(suggested_name.IsAbsolute());
309 EXPECT_TRUE(suggested_name.MaybeAsASCII().empty());
310 EXPECT_TRUE(suggested_extension.empty());
311 #endif
314 #if defined(OS_CHROMEOS)
315 TEST_F(FileSystemApiConsentProviderTest, ForNonKioskApps) {
316 // Component apps are not granted unless they are whitelisted.
318 scoped_refptr<Extension> component_extension(
319 test_util::BuildApp(ExtensionBuilder().SetLocation(Manifest::COMPONENT))
320 .Build());
321 TestingConsentProviderDelegate delegate;
322 ConsentProvider provider(&delegate);
323 EXPECT_FALSE(provider.IsGrantable(*component_extension));
326 // Whitelitsed component apps are instantly granted access without asking
327 // user.
329 scoped_refptr<Extension> whitelisted_component_extension(
330 test_util::BuildApp(ExtensionBuilder().SetLocation(Manifest::COMPONENT))
331 .Build());
332 TestingConsentProviderDelegate delegate;
333 delegate.SetComponentWhitelist(whitelisted_component_extension->id());
334 ConsentProvider provider(&delegate);
335 EXPECT_TRUE(provider.IsGrantable(*whitelisted_component_extension));
337 ConsentProvider::Consent result = ConsentProvider::CONSENT_IMPOSSIBLE;
338 provider.RequestConsent(*whitelisted_component_extension.get(), volume_,
339 true /* writable */,
340 base::Bind(&OnConsentReceived, &result));
341 base::RunLoop().RunUntilIdle();
343 EXPECT_EQ(0, delegate.show_dialog_counter());
344 EXPECT_EQ(0, delegate.show_notification_counter());
345 EXPECT_EQ(ConsentProvider::CONSENT_GRANTED, result);
348 // Non-component apps in non-kiosk mode will be rejected instantly, without
349 // asking for user consent.
351 scoped_refptr<Extension> non_component_extension(
352 test_util::CreateEmptyExtension());
353 TestingConsentProviderDelegate delegate;
354 ConsentProvider provider(&delegate);
355 EXPECT_FALSE(provider.IsGrantable(*non_component_extension));
359 TEST_F(FileSystemApiConsentProviderTest, ForKioskApps) {
360 // Non-component apps in auto-launch kiosk mode will be granted access
361 // instantly without asking for user consent, but with a notification.
363 scoped_refptr<Extension> auto_launch_kiosk_app(
364 test_util::BuildApp(ExtensionBuilder().Pass())
365 .MergeManifest(DictionaryBuilder()
366 .SetBoolean("kiosk_enabled", true)
367 .SetBoolean("kiosk_only", true))
368 .Build());
369 user_manager_->AddKioskAppUser(auto_launch_kiosk_app->id());
370 user_manager_->LoginUser(auto_launch_kiosk_app->id());
372 TestingConsentProviderDelegate delegate;
373 delegate.SetIsAutoLaunched(true);
374 ConsentProvider provider(&delegate);
375 EXPECT_TRUE(provider.IsGrantable(*auto_launch_kiosk_app));
377 ConsentProvider::Consent result = ConsentProvider::CONSENT_IMPOSSIBLE;
378 provider.RequestConsent(*auto_launch_kiosk_app.get(), volume_,
379 true /* writable */,
380 base::Bind(&OnConsentReceived, &result));
381 base::RunLoop().RunUntilIdle();
383 EXPECT_EQ(0, delegate.show_dialog_counter());
384 EXPECT_EQ(1, delegate.show_notification_counter());
385 EXPECT_EQ(ConsentProvider::CONSENT_GRANTED, result);
388 // Non-component apps in manual-launch kiosk mode will be granted access after
389 // receiving approval from the user.
390 scoped_refptr<Extension> manual_launch_kiosk_app(
391 test_util::BuildApp(ExtensionBuilder().Pass())
392 .MergeManifest(DictionaryBuilder()
393 .SetBoolean("kiosk_enabled", true)
394 .SetBoolean("kiosk_only", true))
395 .Build());
396 user_manager_->KioskAppLoggedIn(manual_launch_kiosk_app->id());
398 TestingConsentProviderDelegate delegate;
399 delegate.SetDialogButton(ui::DIALOG_BUTTON_OK);
400 ConsentProvider provider(&delegate);
401 EXPECT_TRUE(provider.IsGrantable(*manual_launch_kiosk_app));
403 ConsentProvider::Consent result = ConsentProvider::CONSENT_IMPOSSIBLE;
404 provider.RequestConsent(*manual_launch_kiosk_app.get(), volume_,
405 true /* writable */,
406 base::Bind(&OnConsentReceived, &result));
407 base::RunLoop().RunUntilIdle();
409 EXPECT_EQ(1, delegate.show_dialog_counter());
410 EXPECT_EQ(0, delegate.show_notification_counter());
411 EXPECT_EQ(ConsentProvider::CONSENT_GRANTED, result);
414 // Non-component apps in manual-launch kiosk mode will be rejected access
415 // after rejecting by a user.
417 TestingConsentProviderDelegate delegate;
418 ConsentProvider provider(&delegate);
419 delegate.SetDialogButton(ui::DIALOG_BUTTON_CANCEL);
420 EXPECT_TRUE(provider.IsGrantable(*manual_launch_kiosk_app));
422 ConsentProvider::Consent result = ConsentProvider::CONSENT_IMPOSSIBLE;
423 provider.RequestConsent(*manual_launch_kiosk_app.get(), volume_,
424 true /* writable */,
425 base::Bind(&OnConsentReceived, &result));
426 base::RunLoop().RunUntilIdle();
428 EXPECT_EQ(1, delegate.show_dialog_counter());
429 EXPECT_EQ(0, delegate.show_notification_counter());
430 EXPECT_EQ(ConsentProvider::CONSENT_REJECTED, result);
433 #endif
435 } // namespace extensions