1 // Copyright 2014 The Chromium Authors
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/android/content_uri_utils.h"
6
7 #include <vector>
8
9 #include "base/containers/fixed_flat_map.h"
10 #include "base/files/file.h"
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/path_service.h"
14 #include "base/test/android/content_uri_test_utils.h"
15 #include "base/test/test_file_util.h"
16 #include "base/time/time.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 namespace base {
20
TEST(ContentUriUtilsTest,Test)21 TEST(ContentUriUtilsTest, Test) {
22 // Get the test image path.
23 FilePath data_dir;
24 ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir));
25 data_dir = data_dir.AppendASCII("file_util");
26 ASSERT_TRUE(PathExists(data_dir));
27 FilePath image_file = data_dir.Append(FILE_PATH_LITERAL("red.png"));
28 File::Info info;
29 ASSERT_TRUE(GetFileInfo(image_file, &info));
30 int image_size = info.size;
31
32 // Insert the image into MediaStore. MediaStore will do some conversions, and
33 // return the content URI.
34 FilePath path = InsertImageIntoMediaStore(image_file);
35 EXPECT_TRUE(path.IsContentUri());
36 EXPECT_TRUE(PathExists(path));
37
38 // Validate GetContentUriMimeType().
39 std::string mime = GetContentUriMimeType(path);
40 EXPECT_EQ(mime, std::string("image/png"));
41
42 // Validate GetFileInfo() for content-URI.
43 EXPECT_TRUE(GetFileInfo(path, &info));
44 EXPECT_EQ(info.size, image_size);
45
46 FilePath invalid_path("content://foo.bar");
47 mime = GetContentUriMimeType(invalid_path);
48 EXPECT_TRUE(mime.empty());
49 EXPECT_FALSE(GetFileInfo(invalid_path, &info));
50 }
51
TEST(ContentUriUtilsTest,TranslateOpenFlagsToJavaMode)52 TEST(ContentUriUtilsTest, TranslateOpenFlagsToJavaMode) {
53 constexpr auto kTranslations = MakeFixedFlatMap<uint32_t, std::string>({
54 {File::FLAG_OPEN | File::FLAG_READ, "r"},
55 {File::FLAG_OPEN_ALWAYS | File::FLAG_READ | File::FLAG_WRITE, "rw"},
56 {File::FLAG_OPEN_ALWAYS | File::FLAG_APPEND, "wa"},
57 {File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE, "rwt"},
58 {File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE, "wt"},
59 });
60
61 for (const auto open_or_create : std::vector<uint32_t>(
62 {0u, File::FLAG_OPEN, File::FLAG_CREATE, File::FLAG_OPEN_ALWAYS,
63 File::FLAG_CREATE_ALWAYS, File::FLAG_OPEN_TRUNCATED})) {
64 for (const auto read_write_append : std::vector<uint32_t>(
65 {0u, File::FLAG_READ, File::FLAG_WRITE, File::FLAG_APPEND,
66 File::FLAG_READ | File::FLAG_WRITE})) {
67 for (const auto other : std::vector<uint32_t>(
68 {0u, File::FLAG_DELETE_ON_CLOSE, File::FLAG_TERMINAL_DEVICE})) {
69 uint32_t open_flags = open_or_create | read_write_append | other;
70 auto mode = internal::TranslateOpenFlagsToJavaMode(open_flags);
71 auto it = kTranslations.find(open_flags);
72 if (it != kTranslations.end()) {
73 EXPECT_TRUE(mode.has_value()) << "flag=0x" << std::hex << open_flags;
74 EXPECT_EQ(mode.value(), it->second)
75 << "flag=0x" << std::hex << open_flags;
76 } else {
77 EXPECT_FALSE(mode.has_value()) << "flag=0x" << std::hex << open_flags;
78 }
79 }
80 }
81 }
82 }
83
TEST(ContentUriUtilsTest,GetFileInfo)84 TEST(ContentUriUtilsTest, GetFileInfo) {
85 ScopedTempDir temp_dir;
86 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
87 FilePath file = temp_dir.GetPath().Append("testfile");
88 FilePath dir = temp_dir.GetPath().Append("testdir");
89 FilePath not_exists = temp_dir.GetPath().Append("not-exists");
90 ASSERT_TRUE(WriteFile(file, "123"));
91 ASSERT_TRUE(CreateDirectory(dir));
92
93 FilePath content_uri_file =
94 *test::android::GetContentUriFromCacheDirFilePath(file);
95 FilePath content_uri_dir =
96 *test::android::GetContentUriFromCacheDirFilePath(dir);
97 FilePath content_uri_not_exists =
98 *test::android::GetContentUriFromCacheDirFilePath(not_exists);
99
100 EXPECT_TRUE(PathExists(content_uri_file));
101 EXPECT_TRUE(PathExists(content_uri_dir));
102 EXPECT_FALSE(PathExists(content_uri_not_exists));
103
104 File::Info info;
105 EXPECT_TRUE(GetFileInfo(file, &info));
106 File::Info content_uri_info;
107 EXPECT_TRUE(GetFileInfo(content_uri_file, &content_uri_info));
108 EXPECT_EQ(content_uri_info.size, 3);
109 EXPECT_FALSE(content_uri_info.is_directory);
110 EXPECT_EQ(content_uri_info.last_modified, info.last_modified);
111
112 EXPECT_TRUE(GetFileInfo(dir, &info));
113 EXPECT_TRUE(GetFileInfo(content_uri_dir, &content_uri_info));
114 EXPECT_TRUE(content_uri_info.is_directory);
115 EXPECT_EQ(content_uri_info.last_modified, info.last_modified);
116
117 EXPECT_FALSE(GetFileInfo(not_exists, &info));
118 EXPECT_FALSE(GetFileInfo(content_uri_not_exists, &info));
119 }
120
TEST(ContentUriUtilsTest,ContentUriBuildDocumentUriUsingTree)121 TEST(ContentUriUtilsTest, ContentUriBuildDocumentUriUsingTree) {
122 base::FilePath tree_uri("content://authority/tree/foo");
123 // The encoded_document_id will be encoded if it has any special chars.
124 EXPECT_EQ(ContentUriBuildDocumentUriUsingTree(tree_uri, "doc:bar").value(),
125 "content://authority/tree/foo/document/doc%3Abar");
126
127 // `%` should not get encoded again to `%25` when it is a valid encoding, but
128 // chars are upper-cased.
129 EXPECT_EQ(ContentUriBuildDocumentUriUsingTree(tree_uri, "doc%3Abar").value(),
130 "content://authority/tree/foo/document/doc%3Abar");
131 EXPECT_EQ(ContentUriBuildDocumentUriUsingTree(tree_uri, "doc%3abar").value(),
132 "content://authority/tree/foo/document/doc%3Abar");
133
134 // Strange stuff happens if the encoding is invalid.
135 EXPECT_EQ(ContentUriBuildDocumentUriUsingTree(tree_uri, "doc%").value(),
136 "content://authority/tree/foo/document/doc%EF%BF%BD");
137 EXPECT_EQ(ContentUriBuildDocumentUriUsingTree(tree_uri, "doc%3").value(),
138 "content://authority/tree/foo/document/doc%EF%BF%BD");
139 EXPECT_EQ(ContentUriBuildDocumentUriUsingTree(tree_uri, "doc%xy").value(),
140 "content://authority/tree/foo/document/doc%EF%BF%BD%00y");
141 }
142
TEST(ContentUriUtilsTest,GetOrCreateByDisplayName)143 TEST(ContentUriUtilsTest, GetOrCreateByDisplayName) {
144 ScopedTempDir temp_dir;
145 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
146 FilePath dir = temp_dir.GetPath().Append("dir");
147 ASSERT_TRUE(base::CreateDirectory(dir));
148 FilePath child1 = dir.Append("child1.txt");
149 FilePath child2 = dir.Append("child2.txt");
150 ASSERT_TRUE(WriteFile(child1, "1"));
151 FilePath parent =
152 *test::android::GetInMemoryContentTreeUriFromCacheDirDirectory(dir);
153
154 // If there is a match, the result should be the valid tree URI.
155 bool is_directory = false;
156 bool create = false;
157 FilePath child = ContentUriGetChildDocumentOrQuery(
158 parent, "child1.txt", "text/plain", is_directory, create);
159 EXPECT_EQ(child.value(),
160 "content://org.chromium.native_test.docprov/tree/" +
161 temp_dir.GetPath().BaseName().value() + "%2Fdir/document/" +
162 temp_dir.GetPath().BaseName().value() + "%2Fdir%2Fchild1.txt");
163 EXPECT_FALSE(ContentUriIsCreateChildDocumentQuery(child));
164 EXPECT_TRUE(internal::ContentUriExists(child));
165
166 // If there is not a match, and create is not set, the result should be empty.
167 child = ContentUriGetChildDocumentOrQuery(parent, "child2.txt", "text/plain",
168 is_directory, create);
169 EXPECT_TRUE(child.empty());
170 EXPECT_FALSE(ContentUriIsCreateChildDocumentQuery(child));
171 EXPECT_FALSE(internal::ContentUriExists(child));
172
173 // If create is set the result should be a create-child-document query.
174 create = true;
175 FilePath query = ContentUriGetChildDocumentOrQuery(
176 parent, "child2.txt", "text/plain", is_directory, create);
177 EXPECT_EQ(query.value(),
178 "content://org.chromium.native_test.docprov/create-child-document/"
179 "tree/" +
180 temp_dir.GetPath().BaseName().value() + "%2Fdir/document/" +
181 temp_dir.GetPath().BaseName().value() +
182 "%2Fdir/mime-type/text%2Fplain/display-name/child2.txt");
183 EXPECT_TRUE(ContentUriIsCreateChildDocumentQuery(query));
184 EXPECT_FALSE(internal::ContentUriExists(query));
185
186 // Lookup should fail when create is false if doc does not exist.
187 create = false;
188 child = ContentUriGetDocumentFromQuery(query, create);
189 EXPECT_TRUE(child.empty());
190 EXPECT_FALSE(base::PathExists(child2));
191
192 // Lookup should create the document, and return the valid URI when create is
193 // set.
194 create = true;
195 child = ContentUriGetDocumentFromQuery(query, create);
196 EXPECT_EQ(child.value(),
197 "content://org.chromium.native_test.docprov/tree/" +
198 temp_dir.GetPath().BaseName().value() + "%2Fdir/document/" +
199 temp_dir.GetPath().BaseName().value() + "%2Fdir%2Fchild2.txt");
200 EXPECT_FALSE(ContentUriIsCreateChildDocumentQuery(child));
201 EXPECT_TRUE(internal::ContentUriExists(child));
202 EXPECT_TRUE(base::PathExists(child2));
203 }
204
205 } // namespace base
206