1 | /* |
2 | * Copyright (C) 2019 Igalia S.L. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2,1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public License |
15 | * along with this library; see the file COPYING.LIB. If not, write to |
16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | * Boston, MA 02110-1301, USA. |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include "TestMain.h" |
23 | #include <wtf/glib/GRefPtr.h> |
24 | #include <wtf/glib/GUniquePtr.h> |
25 | |
26 | static const char* kInvalidJSONSources[] = { |
27 | // Empty input. |
28 | "" , |
29 | // Incorrect JSON input. |
30 | "[" , |
31 | "]" , |
32 | "[{]" , |
33 | "[{ \"trigger: {} }]" , |
34 | "[{ 'trigger': { 'url-filter': '.*' }, 'action': { 'type': 'block' } }]" , |
35 | // Oddball JSON objects. |
36 | "{}" , |
37 | "[]" , |
38 | "[{}]" , |
39 | // Empty trigger/action. |
40 | "[{ \"trigger\": {} }]" , |
41 | "[{ \"action\": {} }]" , |
42 | "[{ \"trigger\": {}, \"action\": {} }]" , |
43 | "[{ \"trigger\": { \"url-filter\": \".*\" } }]" , |
44 | "[{ \"trigger\": { \"url-filter\": \".*\" }, \"action\": {} }]" , |
45 | // Empty action type. |
46 | "[{ \"trigger\": { \"url-filter\": \".*\" }, \"action\": { \"type\": \"\" } }]" , |
47 | // Empty URL filter. |
48 | "[{ \"trigger\": { \"url-filter\": \"\" }, \"action\": { \"type\": \"block\" } }]" , |
49 | }; |
50 | |
51 | static const char* kSimpleJSONSource = |
52 | "[{\n" |
53 | " \"trigger\": {\n" |
54 | " \"url-filter\": \".*\"\n" |
55 | " },\n" |
56 | " \"action\": {\n" |
57 | " \"type\": \"block\"\n" |
58 | " }\n" |
59 | "}]\n" ; |
60 | |
61 | namespace WTF { |
62 | |
63 | template <> WebKitUserContentFilter* refGPtr(WebKitUserContentFilter* ptr) |
64 | { |
65 | if (ptr) |
66 | webkit_user_content_filter_ref(ptr); |
67 | return ptr; |
68 | } |
69 | |
70 | template <> void derefGPtr(WebKitUserContentFilter* ptr) |
71 | { |
72 | if (ptr) |
73 | webkit_user_content_filter_unref(ptr); |
74 | } |
75 | |
76 | } |
77 | |
78 | class UserContentFilterStoreTest: public Test { |
79 | public: |
80 | MAKE_GLIB_TEST_FIXTURE(UserContentFilterStoreTest); |
81 | |
82 | GError* lastError() const |
83 | { |
84 | return m_error.get(); |
85 | } |
86 | |
87 | void resetStore() |
88 | { |
89 | auto* oldStore = m_filterStore.get(); |
90 | m_filterStore = newStore(); |
91 | g_assert_true(oldStore != m_filterStore.get()); |
92 | } |
93 | |
94 | char** fetchIdentifiers() |
95 | { |
96 | webkit_user_content_filter_store_fetch_identifiers(m_filterStore.get(), nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) { |
97 | auto* test = static_cast<UserContentFilterStoreTest*>(userData); |
98 | test->m_filterIds.reset(webkit_user_content_filter_store_fetch_identifiers_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result)); |
99 | g_main_loop_quit(test->m_mainLoop.get()); |
100 | }, this); |
101 | g_main_loop_run(m_mainLoop.get()); |
102 | g_assert_nonnull(m_filterIds.get()); |
103 | return m_filterIds.get(); |
104 | } |
105 | |
106 | WebKitUserContentFilter* saveFilter(const char* filterId, const char* source) |
107 | { |
108 | GRefPtr<GBytes> sourceBytes = adoptGRef(g_bytes_new_static(source, strlen(source))); |
109 | webkit_user_content_filter_store_save(m_filterStore.get(), filterId, sourceBytes.get(), nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) { |
110 | auto* test = static_cast<UserContentFilterStoreTest*>(userData); |
111 | test->m_filter = adoptGRef(webkit_user_content_filter_store_save_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result, &test->m_error.outPtr())); |
112 | g_main_loop_quit(test->m_mainLoop.get()); |
113 | }, this); |
114 | g_main_loop_run(m_mainLoop.get()); |
115 | return m_filter.get(); |
116 | } |
117 | |
118 | WebKitUserContentFilter* saveFilterFromFile(const char* filterId, GFile* file) |
119 | { |
120 | webkit_user_content_filter_store_save_from_file(m_filterStore.get(), filterId, file, nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) { |
121 | auto* test = static_cast<UserContentFilterStoreTest*>(userData); |
122 | test->m_filter = adoptGRef(webkit_user_content_filter_store_save_from_file_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result, &test->m_error.outPtr())); |
123 | g_main_loop_quit(test->m_mainLoop.get()); |
124 | }, this); |
125 | g_main_loop_run(m_mainLoop.get()); |
126 | return m_filter.get(); |
127 | } |
128 | |
129 | WebKitUserContentFilter* loadFilter(const char* filterId) |
130 | { |
131 | webkit_user_content_filter_store_load(m_filterStore.get(), filterId, nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) { |
132 | auto* test = static_cast<UserContentFilterStoreTest*>(userData); |
133 | test->m_filter = adoptGRef(webkit_user_content_filter_store_load_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result, &test->m_error.outPtr())); |
134 | g_main_loop_quit(test->m_mainLoop.get()); |
135 | }, this); |
136 | g_main_loop_run(m_mainLoop.get()); |
137 | return m_filter.get(); |
138 | } |
139 | |
140 | bool removeFilter(const char* filterId) |
141 | { |
142 | webkit_user_content_filter_store_remove(m_filterStore.get(), filterId, nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) { |
143 | auto* test = static_cast<UserContentFilterStoreTest*>(userData); |
144 | test->m_completedOk = !!webkit_user_content_filter_store_remove_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result, &test->m_error.outPtr()); |
145 | g_main_loop_quit(test->m_mainLoop.get()); |
146 | }, this); |
147 | g_main_loop_run(m_mainLoop.get()); |
148 | return m_completedOk; |
149 | } |
150 | |
151 | private: |
152 | GRefPtr<WebKitUserContentFilterStore> newStore() |
153 | { |
154 | GUniquePtr<char> storagePath(g_build_filename(dataDirectory(), "content-filters" , nullptr)); |
155 | auto store = adoptGRef(webkit_user_content_filter_store_new(storagePath.get())); |
156 | g_assert_nonnull(store.get()); |
157 | assertObjectIsDeletedWhenTestFinishes(G_OBJECT(store.get())); |
158 | g_assert_cmpstr(storagePath.get(), ==, webkit_user_content_filter_store_get_path(store.get())); |
159 | return store; |
160 | } |
161 | |
162 | GRefPtr<WebKitUserContentFilterStore> m_filterStore { newStore() }; |
163 | GRefPtr<GMainLoop> m_mainLoop { adoptGRef(g_main_loop_new(nullptr, FALSE)) }; |
164 | GRefPtr<WebKitUserContentFilter> m_filter; |
165 | GUniquePtr<char*> m_filterIds; |
166 | GUniqueOutPtr<GError> m_error; |
167 | bool m_completedOk; |
168 | }; |
169 | |
170 | static void testEmptyStore(UserContentFilterStoreTest* test, gconstpointer) |
171 | { |
172 | g_assert_cmpuint(0, ==, g_strv_length(test->fetchIdentifiers())); |
173 | } |
174 | |
175 | static void testSaveInvalidFilter(UserContentFilterStoreTest* test, gconstpointer) |
176 | { |
177 | for (unsigned i = 0; i < G_N_ELEMENTS(kInvalidJSONSources); i++) { |
178 | GUniquePtr<char> filterId(g_strdup_printf("Filter-%u" , i)); |
179 | g_assert_null(test->saveFilter(filterId.get(), kInvalidJSONSources[i])); |
180 | g_assert_error(test->lastError(), WEBKIT_USER_CONTENT_FILTER_ERROR, WEBKIT_USER_CONTENT_FILTER_ERROR_INVALID_SOURCE); |
181 | } |
182 | } |
183 | |
184 | static void testSaveLoadFilter(UserContentFilterStoreTest* test, gconstpointer) |
185 | { |
186 | static const char* kFilterId = "SimpleFilter" ; |
187 | |
188 | // Trying to load a filter before saving must fail. |
189 | g_assert_null(test->loadFilter(kFilterId)); |
190 | g_assert_error(test->lastError(), WEBKIT_USER_CONTENT_FILTER_ERROR, WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND); |
191 | |
192 | // Then save a filter. Which must succed. |
193 | g_assert_nonnull(test->saveFilter(kFilterId, kSimpleJSONSource)); |
194 | g_assert_no_error(test->lastError()); |
195 | |
196 | // Now it should be possible to actually load the filter. |
197 | g_assert_nonnull(test->loadFilter(kFilterId)); |
198 | g_assert_no_error(test->lastError()); |
199 | } |
200 | |
201 | static void testSavedFilterIdentifierMatch(UserContentFilterStoreTest* test, gconstpointer) |
202 | { |
203 | static const char* kFilterId = "SimpleFilter" ; |
204 | |
205 | auto* filter = test->saveFilter(kFilterId, kSimpleJSONSource); |
206 | g_assert_nonnull(filter); |
207 | g_assert_no_error(test->lastError()); |
208 | g_assert_cmpstr(kFilterId, ==, webkit_user_content_filter_get_identifier(filter)); |
209 | } |
210 | |
211 | static void testRemoveFilter(UserContentFilterStoreTest* test, gconstpointer) |
212 | { |
213 | static const char* kFilterId = "SimpleFilter" ; |
214 | |
215 | // Save a filter. |
216 | g_assert_nonnull(test->saveFilter(kFilterId, kSimpleJSONSource)); |
217 | g_assert_no_error(test->lastError()); |
218 | |
219 | // Now remove it. |
220 | g_assert_true(test->removeFilter(kFilterId)); |
221 | g_assert_no_error(test->lastError()); |
222 | |
223 | // Load must fail, and should not be listed. |
224 | g_assert_null(test->loadFilter(kFilterId)); |
225 | g_assert_error(test->lastError(), WEBKIT_USER_CONTENT_FILTER_ERROR, WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND); |
226 | g_assert_cmpuint(0, ==, g_strv_length(test->fetchIdentifiers())); |
227 | } |
228 | |
229 | static void testSaveMultipleFilters(UserContentFilterStoreTest* test, gconstpointer) |
230 | { |
231 | static const char* kFilterIdSpam = "Filter-Spam" ; |
232 | static const char* kFilterIdEggs = "Filter-Eggs" ; |
233 | |
234 | g_assert_nonnull(test->saveFilter(kFilterIdSpam, kSimpleJSONSource)); |
235 | g_assert_no_error(test->lastError()); |
236 | |
237 | g_assert_nonnull(test->saveFilter(kFilterIdEggs, kSimpleJSONSource)); |
238 | g_assert_no_error(test->lastError()); |
239 | |
240 | auto filterIds = test->fetchIdentifiers(); |
241 | g_assert_true(g_strv_contains(filterIds, kFilterIdSpam)); |
242 | g_assert_true(g_strv_contains(filterIds, kFilterIdEggs)); |
243 | g_assert_cmpuint(2, ==, g_strv_length(filterIds)); |
244 | |
245 | g_assert_nonnull(test->saveFilter(kFilterIdEggs, kSimpleJSONSource)); |
246 | g_assert_no_error(test->lastError()); |
247 | |
248 | filterIds = test->fetchIdentifiers(); |
249 | g_assert_true(g_strv_contains(filterIds, kFilterIdSpam)); |
250 | g_assert_true(g_strv_contains(filterIds, kFilterIdEggs)); |
251 | g_assert_cmpuint(2, ==, g_strv_length(filterIds)); |
252 | } |
253 | |
254 | static void testSaveFilterFromFile(UserContentFilterStoreTest* test, gconstpointer) |
255 | { |
256 | static const char* kFilterId = "SimpleFilter" ; |
257 | |
258 | GUniqueOutPtr<GError> error; |
259 | GUniquePtr<char> filePath(g_build_filename(test->dataDirectory(), "SimpleFilter.json" , nullptr)); |
260 | g_assert_true(g_file_set_contents(filePath.get(), kSimpleJSONSource, strlen(kSimpleJSONSource), &error.outPtr())); |
261 | g_assert_no_error(error.get()); |
262 | |
263 | GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(filePath.get())); |
264 | g_assert_nonnull(test->saveFilterFromFile(kFilterId, file.get())); |
265 | g_assert_no_error(test->lastError()); |
266 | |
267 | g_assert_true(g_strv_contains(test->fetchIdentifiers(), kFilterId)); |
268 | } |
269 | |
270 | static void testFilterPersistence(UserContentFilterStoreTest* test, gconstpointer) |
271 | { |
272 | static const char* kFilterId = "PersistedFilter" ; |
273 | |
274 | g_assert_nonnull(test->saveFilter(kFilterId, kSimpleJSONSource)); |
275 | g_assert_no_error(test->lastError()); |
276 | |
277 | test->resetStore(); |
278 | |
279 | g_assert_nonnull(test->loadFilter(kFilterId)); |
280 | g_assert_no_error(test->lastError()); |
281 | } |
282 | |
283 | void beforeAll() |
284 | { |
285 | UserContentFilterStoreTest::add("WebKitUserContentFilterStore" , "empty-store" , testEmptyStore); |
286 | UserContentFilterStoreTest::add("WebKitUserContentFilterStore" , "invalid-filter-source" , testSaveInvalidFilter); |
287 | UserContentFilterStoreTest::add("WebKitUserContentFilterStore" , "filter-save-load" , testSaveLoadFilter); |
288 | UserContentFilterStoreTest::add("WebKitUserContentFilterStore" , "saved-filter-identifier-match" , testSavedFilterIdentifierMatch); |
289 | UserContentFilterStoreTest::add("WebKitUserContentFilterStore" , "remove-filter" , testRemoveFilter); |
290 | UserContentFilterStoreTest::add("WebKitUserContentFilterStore" , "save-multiple-filters" , testSaveMultipleFilters); |
291 | UserContentFilterStoreTest::add("WebKitUserContentFilterStore" , "save-from-file" , testSaveFilterFromFile); |
292 | UserContentFilterStoreTest::add("WebKitUserContentFilterStore" , "filter-persistence" , testFilterPersistence); |
293 | } |
294 | |
295 | void afterAll() |
296 | { |
297 | } |
298 | |