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#include "base/files/file_path.h"
6#include "base/strings/string_split.h"
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/extensions/api/file_system/file_system_api.h"
9#include "testing/gtest/include/gtest/gtest.h"
10#include "ui/shell_dialogs/select_file_dialog.h"
11
12using extensions::FileSystemChooseEntryFunction;
13using extensions::api::file_system::AcceptOption;
14
15namespace {
16
17void CheckExtensions(const std::vector<base::FilePath::StringType>& expected,
18    std::vector<base::FilePath::StringType>& actual) {
19  EXPECT_EQ(expected.size(), actual.size());
20  if (expected.size() != actual.size())
21    return;
22
23  for (size_t i = 0; i < expected.size(); ++i) {
24    EXPECT_EQ(expected[i], actual[i]);
25  }
26}
27
28AcceptOption* BuildAcceptOption(std::string description,
29    std::string mime_types,
30    std::string extensions) {
31  AcceptOption* option = new AcceptOption();
32
33  if (!description.empty())
34    option->description.reset(new std::string(description));
35
36  if (!mime_types.empty()) {
37    option->mime_types.reset(new std::vector<std::string>());
38    base::SplitString(mime_types, ',', option->mime_types.get());
39  }
40
41  if (!extensions.empty()) {
42    option->extensions.reset(new std::vector<std::string>());
43    base::SplitString(extensions, ',', option->extensions.get());
44  }
45
46  return option;
47}
48
49#if defined(OS_WIN)
50#define ToStringType UTF8ToWide
51#else
52#define ToStringType
53#endif
54
55}  // namespace
56
57class FileSystemApiUnitTest : public testing::Test {
58};
59
60TEST_F(FileSystemApiUnitTest, FileSystemChooseEntryFunctionFileTypeInfoTest) {
61  // AcceptsAllTypes is ignored when no other extensions are available.
62  ui::SelectFileDialog::FileTypeInfo file_type_info;
63  bool acceptsAllTypes = false;
64  FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
65      base::FilePath::StringType(), NULL, &acceptsAllTypes);
66  EXPECT_TRUE(file_type_info.include_all_files);
67  EXPECT_TRUE(file_type_info.extensions.empty());
68
69  // Test grouping of multiple types.
70  file_type_info = ui::SelectFileDialog::FileTypeInfo();
71  std::vector<linked_ptr<AcceptOption> > options;
72  options.push_back(linked_ptr<AcceptOption>(BuildAcceptOption(
73      std::string(), "application/x-chrome-extension", "jso")));
74  acceptsAllTypes = false;
75  FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
76      base::FilePath::StringType(), &options, &acceptsAllTypes);
77  EXPECT_FALSE(file_type_info.include_all_files);
78  ASSERT_EQ(file_type_info.extensions.size(), (size_t) 1);
79  EXPECT_TRUE(file_type_info.extension_description_overrides[0].empty()) <<
80      "No override must be specified for boring accept types";
81  // Note here (and below) that the expectedTypes are sorted, because we use a
82  // set internally to generate the output: thus, the output is sorted.
83  std::vector<base::FilePath::StringType> expectedTypes;
84  expectedTypes.push_back(ToStringType("crx"));
85  expectedTypes.push_back(ToStringType("jso"));
86  CheckExtensions(expectedTypes, file_type_info.extensions[0]);
87
88  // Test that not satisfying the extension will force all types.
89  file_type_info = ui::SelectFileDialog::FileTypeInfo();
90  options.clear();
91  options.push_back(linked_ptr<AcceptOption>(
92      BuildAcceptOption(std::string(), std::string(), "unrelated")));
93  acceptsAllTypes = false;
94  FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
95      ToStringType(".jso"), &options, &acceptsAllTypes);
96  EXPECT_TRUE(file_type_info.include_all_files);
97
98  // Test multiple list entries, all containing their own types.
99  file_type_info = ui::SelectFileDialog::FileTypeInfo();
100  options.clear();
101  options.push_back(linked_ptr<AcceptOption>(
102      BuildAcceptOption(std::string(), std::string(), "jso,js")));
103  options.push_back(linked_ptr<AcceptOption>(
104      BuildAcceptOption(std::string(), std::string(), "cpp,cc")));
105  acceptsAllTypes = false;
106  FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
107      base::FilePath::StringType(), &options, &acceptsAllTypes);
108  ASSERT_EQ(file_type_info.extensions.size(), options.size());
109
110  expectedTypes.clear();
111  expectedTypes.push_back(ToStringType("js"));
112  expectedTypes.push_back(ToStringType("jso"));
113  CheckExtensions(expectedTypes, file_type_info.extensions[0]);
114
115  expectedTypes.clear();
116  expectedTypes.push_back(ToStringType("cc"));
117  expectedTypes.push_back(ToStringType("cpp"));
118  CheckExtensions(expectedTypes, file_type_info.extensions[1]);
119
120  // Test accept type that causes description override.
121  file_type_info = ui::SelectFileDialog::FileTypeInfo();
122  options.clear();
123  options.push_back(linked_ptr<AcceptOption>(
124      BuildAcceptOption(std::string(), "image/*", "html")));
125  acceptsAllTypes = false;
126  FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
127      base::FilePath::StringType(), &options, &acceptsAllTypes);
128  ASSERT_EQ(file_type_info.extension_description_overrides.size(), (size_t) 1);
129  EXPECT_FALSE(file_type_info.extension_description_overrides[0].empty()) <<
130      "Accept type \"image/*\" must generate description override";
131
132  // Test multiple accept types that cause description override causes us to
133  // still present the default.
134  file_type_info = ui::SelectFileDialog::FileTypeInfo();
135  options.clear();
136  options.push_back(linked_ptr<AcceptOption>(BuildAcceptOption(
137      std::string(), "image/*,audio/*,video/*", std::string())));
138  acceptsAllTypes = false;
139  FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
140      base::FilePath::StringType(), &options, &acceptsAllTypes);
141  ASSERT_EQ(file_type_info.extension_description_overrides.size(), (size_t) 1);
142  EXPECT_TRUE(file_type_info.extension_description_overrides[0].empty());
143
144  // Test explicit description override.
145  file_type_info = ui::SelectFileDialog::FileTypeInfo();
146  options.clear();
147  options.push_back(linked_ptr<AcceptOption>(
148      BuildAcceptOption("File Types 101", "image/jpeg", std::string())));
149  acceptsAllTypes = false;
150  FileSystemChooseEntryFunction::BuildFileTypeInfo(&file_type_info,
151      base::FilePath::StringType(), &options, &acceptsAllTypes);
152  EXPECT_EQ(file_type_info.extension_description_overrides[0],
153      UTF8ToUTF16("File Types 101"));
154}
155
156TEST_F(FileSystemApiUnitTest, FileSystemChooseEntryFunctionSuggestionTest) {
157  std::string opt_name;
158  base::FilePath suggested_name;
159  base::FilePath::StringType suggested_extension;
160
161  opt_name = std::string("normal_path.txt");
162  FileSystemChooseEntryFunction::BuildSuggestion(&opt_name, &suggested_name,
163      &suggested_extension);
164  EXPECT_FALSE(suggested_name.IsAbsolute());
165  EXPECT_EQ(suggested_name.MaybeAsASCII(), "normal_path.txt");
166  EXPECT_EQ(suggested_extension, ToStringType("txt"));
167
168  // We should provide just the basename, i.e., "path".
169  opt_name = std::string("/a/bad/path");
170  FileSystemChooseEntryFunction::BuildSuggestion(&opt_name, &suggested_name,
171      &suggested_extension);
172  EXPECT_FALSE(suggested_name.IsAbsolute());
173  EXPECT_EQ(suggested_name.MaybeAsASCII(), "path");
174  EXPECT_TRUE(suggested_extension.empty());
175
176#if !defined(OS_WIN)
177  // TODO(thorogood): Fix this test on Windows.
178  // Filter out absolute paths with no basename.
179  opt_name = std::string("/");
180  FileSystemChooseEntryFunction::BuildSuggestion(&opt_name, &suggested_name,
181      &suggested_extension);
182  EXPECT_FALSE(suggested_name.IsAbsolute());
183  EXPECT_TRUE(suggested_name.MaybeAsASCII().empty());
184  EXPECT_TRUE(suggested_extension.empty());
185#endif
186}
187