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 "extensions/browser/extension_creator_filter.h"
6 
7 #include <stddef.h>
8 
9 #include <memory>
10 
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/macros.h"
14 #include "base/stl_util.h"
15 #include "build/build_config.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "testing/platform_test.h"
18 
19 #if defined(OS_WIN)
20 #include <windows.h>
21 #endif
22 
23 namespace {
24 
25 class ExtensionCreatorFilterTest : public PlatformTest {
26  protected:
SetUp()27   void SetUp() override {
28     PlatformTest::SetUp();
29 
30     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
31     extension_dir_ = temp_dir_.GetPath();
32 
33     filter_ = base::MakeRefCounted<extensions::ExtensionCreatorFilter>(
34         extension_dir_);
35   }
36 
CreateTestFile(const base::FilePath & file_path)37   base::FilePath CreateTestFile(const base::FilePath& file_path) {
38     return CreateRelativeFilePath(file_path);
39   }
40 
41   // Creates an empty file with the given relative path. Creates parent
42   // directories if needed.
CreateRelativeFilePath(const base::FilePath & relative_file_path)43   base::FilePath CreateRelativeFilePath(
44       const base::FilePath& relative_file_path) {
45     base::FilePath path = extension_dir_.Append(relative_file_path);
46     EXPECT_TRUE(base::CreateDirectory(path.DirName()));
47 
48     std::string contents = "test";
49     EXPECT_EQ(static_cast<int>(contents.size()),
50               base::WriteFile(path, contents.c_str(), contents.size()));
51     return path;
52   }
53 
CreateTestFileInDir(const base::FilePath::StringType & file_name,const base::FilePath::StringType & dir)54   base::FilePath CreateTestFileInDir(
55       const base::FilePath::StringType& file_name,
56       const base::FilePath::StringType& dir) {
57     return CreateRelativeFilePath(base::FilePath(dir).Append(file_name));
58   }
59 
60   scoped_refptr<extensions::ExtensionCreatorFilter> filter_;
61 
62   base::ScopedTempDir temp_dir_;
63 
64   base::FilePath extension_dir_;
65 };
66 
67 struct UnaryBooleanTestData {
68   const base::FilePath::CharType* input;
69   bool expected;
70 };
71 
TEST_F(ExtensionCreatorFilterTest,NormalCases)72 TEST_F(ExtensionCreatorFilterTest, NormalCases) {
73   const struct UnaryBooleanTestData cases[] = {
74       {FILE_PATH_LITERAL("foo"), true},
75       {FILE_PATH_LITERAL(".foo"), false},
76       {FILE_PATH_LITERAL("~foo"), true},
77       {FILE_PATH_LITERAL("foo~"), false},
78       {FILE_PATH_LITERAL("#foo"), true},
79       {FILE_PATH_LITERAL("foo#"), true},
80       {FILE_PATH_LITERAL("#foo#"), false},
81       {FILE_PATH_LITERAL(".svn"), false},
82       {FILE_PATH_LITERAL("__MACOSX"), false},
83       {FILE_PATH_LITERAL(".DS_Store"), false},
84       {FILE_PATH_LITERAL("desktop.ini"), false},
85       {FILE_PATH_LITERAL("Thumbs.db"), false},
86   };
87 
88   for (size_t i = 0; i < base::size(cases); ++i) {
89     base::FilePath input(cases[i].input);
90     base::FilePath test_file(CreateTestFile(input));
91     bool observed = filter_->ShouldPackageFile(test_file);
92 
93     EXPECT_EQ(cases[i].expected, observed)
94         << "i: " << i << ", input: " << test_file.value();
95   }
96 }
97 
TEST_F(ExtensionCreatorFilterTest,MetadataFolderExcluded)98 TEST_F(ExtensionCreatorFilterTest, MetadataFolderExcluded) {
99   const struct UnaryBooleanTestData cases[] = {
100       {FILE_PATH_LITERAL("_metadata/foo"), false},
101       {FILE_PATH_LITERAL("_metadata/abc/foo"), false},
102       {FILE_PATH_LITERAL("_metadata/abc/xyz/foo"), false},
103       {FILE_PATH_LITERAL("abc/_metadata/xyz"), true},
104       {FILE_PATH_LITERAL("xyz/_metadata"), true},
105   };
106 
107   // Create and test the filepaths.
108   for (size_t i = 0; i < base::size(cases); ++i) {
109     base::FilePath test_file =
110         CreateRelativeFilePath(base::FilePath(cases[i].input));
111     bool observed = filter_->ShouldPackageFile(test_file);
112 
113     EXPECT_EQ(cases[i].expected, observed)
114         << "i: " << i << ", input: " << test_file.value();
115   }
116 
117   // Also test directories.
118   const struct UnaryBooleanTestData directory_cases[] = {
119       {FILE_PATH_LITERAL("_metadata"), false},
120       {FILE_PATH_LITERAL("_metadata/abc"), false},
121       {FILE_PATH_LITERAL("_metadata/abc/xyz"), false},
122       {FILE_PATH_LITERAL("abc"), true},
123       {FILE_PATH_LITERAL("abc/_metadata"), true},
124       {FILE_PATH_LITERAL("xyz"), true},
125   };
126   for (size_t i = 0; i < base::size(directory_cases); ++i) {
127     base::FilePath directory = extension_dir_.Append(directory_cases[i].input);
128     bool observed = filter_->ShouldPackageFile(directory);
129 
130     EXPECT_EQ(directory_cases[i].expected, observed)
131         << "i: " << i << ", input: " << directory.value();
132   }
133 }
134 
135 struct StringStringWithBooleanTestData {
136   const base::FilePath::StringType file_name;
137   const base::FilePath::StringType dir;
138   bool expected;
139 };
140 
141 // Ignore the files in special directories, including ".git", ".svn",
142 // "__MACOSX".
TEST_F(ExtensionCreatorFilterTest,IgnoreFilesInSpecialDir)143 TEST_F(ExtensionCreatorFilterTest, IgnoreFilesInSpecialDir) {
144   const struct StringStringWithBooleanTestData cases[] = {
145       {FILE_PATH_LITERAL("foo"), FILE_PATH_LITERAL(".git"), false},
146       {FILE_PATH_LITERAL("goo"), FILE_PATH_LITERAL(".svn"), false},
147       {FILE_PATH_LITERAL("foo"), FILE_PATH_LITERAL("__MACOSX"), false},
148       {FILE_PATH_LITERAL("foo"), FILE_PATH_LITERAL("foo"), true},
149       {FILE_PATH_LITERAL("index.js"), FILE_PATH_LITERAL("scripts"), true},
150   };
151 
152   for (size_t i = 0; i < base::size(cases); ++i) {
153     base::FilePath test_file(
154         CreateTestFileInDir(cases[i].file_name, cases[i].dir));
155     bool observed = filter_->ShouldPackageFile(test_file);
156     EXPECT_EQ(cases[i].expected, observed)
157         << "i: " << i << ", input: " << test_file.value();
158   }
159 }
160 
161 #if defined(OS_WIN)
162 struct StringBooleanWithBooleanTestData {
163   const base::FilePath::CharType* input_char;
164   bool input_bool;
165   bool expected;
166 };
167 
TEST_F(ExtensionCreatorFilterTest,WindowsHiddenFiles)168 TEST_F(ExtensionCreatorFilterTest, WindowsHiddenFiles) {
169   const struct StringBooleanWithBooleanTestData cases[] = {
170       {FILE_PATH_LITERAL("a-normal-file"), false, true},
171       {FILE_PATH_LITERAL(".a-dot-file"), false, false},
172       {FILE_PATH_LITERAL(".a-dot-file-that-we-have-set-to-hidden"), true,
173        false},
174       {FILE_PATH_LITERAL("a-file-that-we-have-set-to-hidden"), true, false},
175       {FILE_PATH_LITERAL("a-file-that-we-have-not-set-to-hidden"), false, true},
176   };
177 
178   for (size_t i = 0; i < base::size(cases); ++i) {
179     base::FilePath input(cases[i].input_char);
180     bool should_hide = cases[i].input_bool;
181     base::FilePath test_file(CreateTestFile(input));
182 
183     if (should_hide) {
184       SetFileAttributes(test_file.value().c_str(), FILE_ATTRIBUTE_HIDDEN);
185     }
186     bool observed = filter_->ShouldPackageFile(test_file);
187     EXPECT_EQ(cases[i].expected, observed)
188         << "i: " << i << ", input: " << test_file.value();
189   }
190 }
191 #endif
192 
193 }  // namespace
194