• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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