Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: test/filterStorage_readwrite.js

Issue 29408742: Issue 5059 - Simplify I/O API and FilterStorage implementation (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore
Patch Set: Created April 10, 2017, 2:44 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2017 eyeo GmbH 3 * Copyright (C) 2006-2017 eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 "use strict"; 18 "use strict";
19 19
20 const {createSandbox, unexpectedError} = require("./_common"); 20 const {createSandbox, unexpectedError} = require("./_common");
21 21
22 let Filter = null; 22 let Filter = null;
23 let FilterNotifier = null;
24 let FilterStorage = null; 23 let FilterStorage = null;
25 let IO = null; 24 let IO = null;
26 let Prefs = null; 25 let Prefs = null;
27 let ExternalSubscription = null; 26 let ExternalSubscription = null;
28 let dataFile = null;
29 27
30 exports.setUp = function(callback) 28 exports.setUp = function(callback)
31 { 29 {
32 let sandboxedRequire = createSandbox(); 30 let sandboxedRequire = createSandbox();
33 ( 31 (
34 {Filter} = sandboxedRequire("../lib/filterClasses"), 32 {Filter} = sandboxedRequire("../lib/filterClasses"),
35 {FilterNotifier} = sandboxedRequire("../lib/filterNotifier"),
36 {FilterStorage} = sandboxedRequire("../lib/filterStorage"), 33 {FilterStorage} = sandboxedRequire("../lib/filterStorage"),
37 {IO} = sandboxedRequire("./stub-modules/io"), 34 {IO} = sandboxedRequire("./stub-modules/io"),
38 {Prefs} = sandboxedRequire("./stub-modules/prefs"), 35 {Prefs} = sandboxedRequire("./stub-modules/prefs"),
39 {ExternalSubscription} = sandboxedRequire("../lib/subscriptionClasses") 36 {ExternalSubscription} = sandboxedRequire("../lib/subscriptionClasses")
40 ); 37 );
41 38
42 Prefs.patternsfile = "patterns.ini";
43 dataFile = IO.resolveFilePath(Prefs.patternsfile);
44
45 FilterStorage.addFilter(Filter.fromText("foobar")); 39 FilterStorage.addFilter(Filter.fromText("foobar"));
46 callback(); 40 callback();
47 }; 41 };
48 42
49 let testData = new Promise((resolve, reject) => 43 let testData = new Promise((resolve, reject) =>
50 { 44 {
51 const fs = require("fs"); 45 const fs = require("fs");
52 const path = require("path"); 46 const path = require("path");
53 let datapath = path.resolve(__dirname, "data", "patterns.ini"); 47 let datapath = path.resolve(__dirname, "data", "patterns.ini");
54 48
55 fs.readFile(datapath, "utf-8", (error, data) => 49 fs.readFile(datapath, "utf-8", (error, data) =>
56 { 50 {
57 if (error) 51 if (error)
58 reject(error); 52 reject(error);
59 else 53 else
60 resolve(data); 54 resolve(data.split(/[\r\n]+/));
61 }); 55 });
62 }); 56 });
63 57
64 function loadFilters()
65 {
66 FilterStorage.loadFromDisk();
67 return FilterNotifier.once("load");
68 }
69
70 function saveFilters()
71 {
72 FilterStorage.saveToDisk();
73 return FilterNotifier.once("save");
74 }
75
76 function canonize(data) 58 function canonize(data)
77 { 59 {
78 let curSection = null; 60 let curSection = null;
79 let sections = []; 61 let sections = [];
80 for (let line of (data + "\n[end]").split(/[\r\n]+/)) 62 for (let line of data)
81 { 63 {
82 if (/^\[.*\]$/.test(line)) 64 if (/^\[.*\]$/.test(line))
83 { 65 {
84 if (curSection) 66 if (curSection)
85 sections.push(curSection); 67 sections.push(curSection);
86 68
87 curSection = {header: line, data: []}; 69 curSection = {header: line, data: []};
88 } 70 }
89 else if (curSection && /\S/.test(line)) 71 else if (curSection && /\S/.test(line))
90 curSection.data.push(line); 72 curSection.data.push(line);
91 } 73 }
74 if (curSection)
75 sections.push(curSection);
76
92 for (let section of sections) 77 for (let section of sections)
93 { 78 {
94 section.key = section.header + " " + section.data[0]; 79 section.key = section.header + " " + section.data[0];
95 section.data.sort(); 80 section.data.sort();
96 } 81 }
97 sections.sort((a, b) => 82 sections.sort((a, b) =>
98 { 83 {
99 if (a.key < b.key) 84 if (a.key < b.key)
100 return -1; 85 return -1;
101 else if (a.key > b.key) 86 else if (a.key > b.key)
102 return 1; 87 return 1;
103 return 0; 88 return 0;
104 }); 89 });
105 return sections.map( 90 return sections;
106 section => [section.header].concat(section.data).join("\n")
107 ).join("\n");
108 } 91 }
109 92
110 function testReadWrite(test, withExternal) 93 function testReadWrite(test, withExternal)
111 { 94 {
112 test.ok(!FilterStorage.initialized, "Uninitialized before the first load"); 95 test.ok(!FilterStorage.initialized, "Uninitialized before the first load");
113 96
114 return testData.then(data => 97 return testData.then(data =>
115 { 98 {
116 dataFile.contents = data; 99 IO._setFileContents(FilterStorage.sourceFile, data);
117 return loadFilters(); 100 return FilterStorage.loadFromDisk();
118 }).then(() => 101 }).then(() =>
119 { 102 {
120 test.ok(FilterStorage.initialized, "Initialize after the first load"); 103 test.ok(FilterStorage.initialized, "Initialize after the first load");
121 test.equal(FilterStorage.fileProperties.version, FilterStorage.formatVersion , "File format version"); 104 test.equal(FilterStorage.fileProperties.version, FilterStorage.formatVersion , "File format version");
122 105
123 if (withExternal) 106 if (withExternal)
124 { 107 {
125 { 108 {
126 let subscription = new ExternalSubscription("~external~external subscrip tion ID", "External subscription"); 109 let subscription = new ExternalSubscription("~external~external subscrip tion ID", "External subscription");
127 subscription.filters = [Filter.fromText("foo"), Filter.fromText("bar")]; 110 subscription.filters = [Filter.fromText("foo"), Filter.fromText("bar")];
128 FilterStorage.addSubscription(subscription); 111 FilterStorage.addSubscription(subscription);
129 } 112 }
130 113
131 let externalSubscriptions = FilterStorage.subscriptions.filter(subscriptio n => subscription instanceof ExternalSubscription); 114 let externalSubscriptions = FilterStorage.subscriptions.filter(subscriptio n => subscription instanceof ExternalSubscription);
132 test.equal(externalSubscriptions.length, 1, "Number of external subscripti ons after updateExternalSubscription"); 115 test.equal(externalSubscriptions.length, 1, "Number of external subscripti ons after updateExternalSubscription");
133 116
134 test.equal(externalSubscriptions[0].url, "~external~external subscription ID", "ID of external subscription"); 117 test.equal(externalSubscriptions[0].url, "~external~external subscription ID", "ID of external subscription");
135 test.equal(externalSubscriptions[0].filters.length, 2, "Number of filters in external subscription"); 118 test.equal(externalSubscriptions[0].filters.length, 2, "Number of filters in external subscription");
136 } 119 }
137 120
138 return saveFilters(); 121 return FilterStorage.saveToDisk();
139 }).then(() => testData).then(expected => 122 }).then(() => testData).then(expected =>
140 { 123 {
141 test.equal(canonize(dataFile.contents), canonize(expected), "Read/write resu lt"); 124 test.deepEqual(canonize(IO._getFileContents(FilterStorage.sourceFile)),
125 canonize(expected), "Read/write result");
142 }).catch(unexpectedError.bind(test)).then(() => test.done()); 126 }).catch(unexpectedError.bind(test)).then(() => test.done());
143 } 127 }
144 128
145 exports.testReadAndSaveToFile = function(test) 129 exports.testReadAndSaveToFile = function(test)
146 { 130 {
147 testReadWrite(test, false); 131 testReadWrite(test, false);
148 }; 132 };
149 133
150 exports.testReadAndSaveToFileWithExternalSubscription = function(test) 134 exports.testReadAndSaveToFileWithExternalSubscription = function(test)
151 { 135 {
152 testReadWrite(test, true); 136 testReadWrite(test, true);
153 }; 137 };
154 138
155 exports.testLegacyGroups = {}; 139 exports.testLegacyGroups = {};
156 140
157 for (let url of ["~wl~", "~fl~", "~eh~"]) 141 for (let url of ["~wl~", "~fl~", "~eh~"])
158 { 142 {
159 exports.testLegacyGroups["read empty " + url] = function(test) 143 exports.testLegacyGroups["read empty " + url] = function(test)
160 { 144 {
161 dataFile.contents = "[Subscription]\nurl=" + url; 145 IO._setFileContents(FilterStorage.sourceFile, [
146 "[Subscription]", "url=" + url
147 ]);
162 148
163 loadFilters(() => 149 FilterStorage.loadFromDisk().then(() =>
164 { 150 {
165 test.equal(FilterStorage.subscriptions.length, 0, "Number of filter subscr iptions"); 151 test.equal(FilterStorage.subscriptions.length, 0, "Number of filter subscr iptions");
166 }).catch(unexpectedError.bind(test)).then(() => test.done()); 152 }).catch(unexpectedError.bind(test)).then(() => test.done());
167 }; 153 };
168 154
169 exports.testLegacyGroups["read non-empty " + url] = function(test) 155 exports.testLegacyGroups["read non-empty " + url] = function(test)
170 { 156 {
171 dataFile.contents = "[Subscription]\nurl=" + url + "\n[Subscription filters] \nfoo"; 157 IO._setFileContents(FilterStorage.sourceFile, [
158 "[Subscription]", "url=" + url,
159 "[Subscription filters]", "foo"
160 ]);
172 161
173 loadFilters().then(() => 162 FilterStorage.loadFromDisk().then(() =>
174 { 163 {
175 test.equal(FilterStorage.subscriptions.length, 1, "Number of filter subscr iptions"); 164 test.equal(FilterStorage.subscriptions.length, 1, "Number of filter subscr iptions");
176 if (FilterStorage.subscriptions.length == 1) 165 if (FilterStorage.subscriptions.length == 1)
177 { 166 {
178 let subscription = FilterStorage.subscriptions[0]; 167 let subscription = FilterStorage.subscriptions[0];
179 test.equal(subscription.url, url, "Subscription ID"); 168 test.equal(subscription.url, url, "Subscription ID");
180 test.equal(subscription.title, null, "Subscription title"); 169 test.equal(subscription.title, null, "Subscription title");
181 test.deepEqual(subscription.defaults, null, "Default types"); 170 test.deepEqual(subscription.defaults, null, "Default types");
182 test.equal(subscription.filters.length, 1, "Number of subscription filte rs"); 171 test.equal(subscription.filters.length, 1, "Number of subscription filte rs");
183 if (subscription.filters.length == 1) 172 if (subscription.filters.length == 1)
184 test.equal(subscription.filters[0].text, "foo", "First filter"); 173 test.equal(subscription.filters[0].text, "foo", "First filter");
185 } 174 }
186 }).catch(unexpectedError.bind(test)).then(() => test.done()); 175 }).catch(unexpectedError.bind(test)).then(() => test.done());
187 }; 176 };
188 } 177 }
189 178
190 exports.testReadLegacyFilters = function(test) 179 exports.testReadLegacyFilters = function(test)
191 { 180 {
192 dataFile.contents = "[Subscription]\nurl=~user~1234\ntitle=Foo\n[Subscription filters]\n[User patterns]\nfoo\n\\[bar]\nfoo#bar"; 181 IO._setFileContents(FilterStorage.sourceFile, [
182 "[Subscription]", "url=~user~1234", "title=Foo",
183 "[Subscription filters]",
184 "[User patterns]", "foo", "\\[bar]", "foo#bar"
185 ]);
193 186
194 loadFilters().then(() => 187 FilterStorage.loadFromDisk().then(() =>
195 { 188 {
196 test.equal(FilterStorage.subscriptions.length, 1, "Number of filter subscrip tions"); 189 test.equal(FilterStorage.subscriptions.length, 1, "Number of filter subscrip tions");
197 if (FilterStorage.subscriptions.length == 1) 190 if (FilterStorage.subscriptions.length == 1)
198 { 191 {
199 let subscription = FilterStorage.subscriptions[0]; 192 let subscription = FilterStorage.subscriptions[0];
200 test.equal(subscription.filters.length, 3, "Number of subscription filters "); 193 test.equal(subscription.filters.length, 3, "Number of subscription filters ");
201 if (subscription.filters.length == 3) 194 if (subscription.filters.length == 3)
202 { 195 {
203 test.equal(subscription.filters[0].text, "foo", "First filter"); 196 test.equal(subscription.filters[0].text, "foo", "First filter");
204 test.equal(subscription.filters[1].text, "[bar]", "Second filter"); 197 test.equal(subscription.filters[1].text, "[bar]", "Second filter");
205 test.equal(subscription.filters[2].text, "foo#bar", "Third filter"); 198 test.equal(subscription.filters[2].text, "foo#bar", "Third filter");
206 } 199 }
207 } 200 }
208 }).catch(unexpectedError.bind(test)).then(() => test.done()); 201 }).catch(unexpectedError.bind(test)).then(() => test.done());
209 }; 202 };
210 203
211 exports.testImportExport = function(test) 204 exports.testImportExport = function(test)
212 { 205 {
213 testData.then(data => 206 testData.then(lines =>
214 { 207 {
215 let lines = data.split("\n");
216 if (lines.length && lines[lines.length - 1] == "") 208 if (lines.length && lines[lines.length - 1] == "")
217 lines.pop(); 209 lines.pop();
218 210
219 let importer = FilterStorage.importData(); 211 let importer = FilterStorage.importData();
220 for (let line of lines) 212 for (let line of lines)
221 importer(line); 213 importer(line);
222 importer(null); 214 importer(null);
223 215
224 test.equal(FilterStorage.fileProperties.version, FilterStorage.formatVersion , "File format version"); 216 test.equal(FilterStorage.fileProperties.version, FilterStorage.formatVersion , "File format version");
225 217
226 let exported = ""; 218 let exported = Array.from(FilterStorage.exportData());
227 for (let line of FilterStorage.exportData()) 219 test.deepEqual(canonize(exported), canonize(lines), "Import/export result");
228 exported += line + "\n";
229 test.equal(canonize(exported), canonize(data), "Import/export result");
230 }).catch(unexpectedError.bind(test)).then(() => test.done()); 220 }).catch(unexpectedError.bind(test)).then(() => test.done());
231 }; 221 };
232 222
233 exports.testSavingWithoutBackups = function(test) 223 exports.testSavingWithoutBackups = function(test)
234 { 224 {
235 Prefs.patternsbackups = 0; 225 Prefs.patternsbackups = 0;
236 Prefs.patternsbackupinterval = 24; 226 Prefs.patternsbackupinterval = 24;
237 227
238 saveFilters().then(() => 228 FilterStorage.saveToDisk().then(() =>
239 { 229 {
240 return saveFilters(); 230 return FilterStorage.saveToDisk();
241 }).then(() => 231 }).then(() =>
242 { 232 {
243 let backupFile = dataFile.clone(); 233 test.ok(!IO._getFileContents(FilterStorage.getBackupName(1)),
244 backupFile.leafName = backupFile.leafName.replace(/\.ini$/, "-backup1.ini"); 234 "Backup shouldn't be created");
245 test.ok(!backupFile.exists(), "Backup shouldn't be created");
246 }).catch(unexpectedError.bind(test)).then(() => test.done()); 235 }).catch(unexpectedError.bind(test)).then(() => test.done());
247 }; 236 };
248 237
249 exports.testSavingWithBackups = function(test) 238 exports.testSavingWithBackups = function(test)
250 { 239 {
251 Prefs.patternsbackups = 2; 240 Prefs.patternsbackups = 2;
252 Prefs.patternsbackupinterval = 24; 241 Prefs.patternsbackupinterval = 24;
253 242
254 let backupFile = dataFile.clone(); 243 let backupFile = FilterStorage.getBackupName(1);
255 backupFile.leafName = backupFile.leafName.replace(/\.ini$/, "-backup1.ini"); 244 let backupFile2 = FilterStorage.getBackupName(2);
256 245 let backupFile3 = FilterStorage.getBackupName(3);
257 let backupFile2 = dataFile.clone();
258 backupFile2.leafName = backupFile2.leafName.replace(/\.ini$/, "-backup2.ini");
259
260 let backupFile3 = dataFile.clone();
261 backupFile3.leafName = backupFile3.leafName.replace(/\.ini$/, "-backup3.ini");
262 246
263 let oldModifiedTime; 247 let oldModifiedTime;
264 248
265 saveFilters().then(() => 249 FilterStorage.saveToDisk().then(() =>
266 { 250 {
267 // Save again immediately 251 // Save again immediately
268 return saveFilters(); 252 return FilterStorage.saveToDisk();
269 }).then(() => 253 }).then(() =>
270 { 254 {
271 test.ok(backupFile.exists(), "First backup created"); 255 test.ok(IO._getFileContents(backupFile), "First backup created");
272 256
273 backupFile.lastModifiedTime -= 10000; 257 oldModifiedTime = IO._getModifiedTime(backupFile) - 10000;
274 oldModifiedTime = backupFile.lastModifiedTime; 258 IO._setModifiedTime(backupFile, oldModifiedTime);
275 return saveFilters(); 259 return FilterStorage.saveToDisk();
276 }).then(() => 260 }).then(() =>
277 { 261 {
278 test.equal(backupFile.lastModifiedTime, oldModifiedTime, "Backup not overwri tten if it is only 10 seconds old"); 262 test.equal(IO._getModifiedTime(backupFile), oldModifiedTime, "Backup not ove rwritten if it is only 10 seconds old");
279 263
280 backupFile.lastModifiedTime -= 40 * 60 * 60 * 1000; 264 oldModifiedTime -= 40 * 60 * 60 * 1000;
281 oldModifiedTime = backupFile.lastModifiedTime; 265 IO._setModifiedTime(backupFile, oldModifiedTime);
282 return saveFilters(); 266 return FilterStorage.saveToDisk();
283 }).then(() => 267 }).then(() =>
284 { 268 {
285 test.notEqual(backupFile.lastModifiedTime, oldModifiedTime, "Backup overwrit ten if it is 40 hours old"); 269 test.notEqual(IO._getModifiedTime(backupFile), oldModifiedTime, "Backup over written if it is 40 hours old");
286 270
287 test.ok(backupFile2.exists(), "Second backup created when first backup is ov erwritten"); 271 test.ok(IO._getFileContents(backupFile2), "Second backup created when first backup is overwritten");
288 272
289 backupFile.lastModifiedTime -= 20000; 273 IO._setModifiedTime(backupFile, IO._getModifiedTime(backupFile) - 20000);
290 oldModifiedTime = backupFile2.lastModifiedTime; 274 oldModifiedTime = IO._getModifiedTime(backupFile2);
291 return saveFilters(); 275 return FilterStorage.saveToDisk();
292 }).then(() => 276 }).then(() =>
293 { 277 {
294 test.equal(backupFile2.lastModifiedTime, oldModifiedTime, "Second backup not overwritten if first one is only 20 seconds old"); 278 test.equal(IO._getModifiedTime(backupFile2), oldModifiedTime, "Second backup not overwritten if first one is only 20 seconds old");
295 279
296 backupFile.lastModifiedTime -= 25 * 60 * 60 * 1000; 280 IO._setModifiedTime(backupFile, IO._getModifiedTime(backupFile) - 25 * 60 * 60 * 1000);
297 oldModifiedTime = backupFile2.lastModifiedTime; 281 oldModifiedTime = IO._getModifiedTime(backupFile2);
298 return saveFilters(); 282 return FilterStorage.saveToDisk();
299 }).then(() => 283 }).then(() =>
300 { 284 {
301 test.notEqual(backupFile2.lastModifiedTime, oldModifiedTime, "Second backup overwritten if first one is 25 hours old"); 285 test.notEqual(IO._getModifiedTime(backupFile2), oldModifiedTime, "Second bac kup overwritten if first one is 25 hours old");
302 286
303 test.ok(!backupFile3.exists(), "Third backup not created with patternsbackup s = 2"); 287 test.ok(!IO._getFileContents(backupFile3), "Third backup not created with pa tternsbackups = 2");
304 }).catch(unexpectedError.bind(test)).then(() => test.done()); 288 }).catch(unexpectedError.bind(test)).then(() => test.done());
305 }; 289 };
306 290
307 exports.testRestoringBackup = function(test) 291 exports.testRestoringBackup = function(test)
308 { 292 {
309 Prefs.patternsbackups = 2; 293 Prefs.patternsbackups = 2;
310 Prefs.patternsbackupinterval = 24; 294 Prefs.patternsbackupinterval = 24;
311 295
312 saveFilters().then(() => 296 FilterStorage.saveToDisk().then(() =>
313 { 297 {
314 test.equal(FilterStorage.subscriptions.length, 1, "Initial subscription coun t"); 298 test.equal(FilterStorage.subscriptions[0].filters.length, 1, "Initial filter count");
315 FilterStorage.removeSubscription(FilterStorage.subscriptions[0]); 299 FilterStorage.addFilter(Filter.fromText("barfoo"));
Wladimir Palant 2017/04/10 14:59:15 Removing subscription here doesn't work because Fi
316 return saveFilters(); 300 test.equal(FilterStorage.subscriptions[0].filters.length, 2, "Filter count a fter adding a filter");
301 return FilterStorage.saveToDisk();
317 }).then(() => 302 }).then(() =>
318 { 303 {
319 return loadFilters(); 304 return FilterStorage.loadFromDisk();
320 }).then(() => 305 }).then(() =>
321 { 306 {
322 test.equal(FilterStorage.subscriptions.length, 0, "Subscription count after removing subscriptions and reloading"); 307 test.equal(FilterStorage.subscriptions[0].filters.length, 2, "Filter count a fter adding filter and reloading");
323 return FilterStorage.restoreBackup(1); 308 return FilterStorage.restoreBackup(1);
324 }).then(() => 309 }).then(() =>
325 { 310 {
326 test.equal(FilterStorage.subscriptions.length, 1, "Subscription count after restoring backup"); 311 test.equal(FilterStorage.subscriptions[0].filters.length, 1, "Filter count a fter restoring backup");
327 return loadFilters(); 312 return FilterStorage.loadFromDisk();
328 }).then(() => 313 }).then(() =>
329 { 314 {
330 test.equal(FilterStorage.subscriptions.length, 1, "Subscription count after reloading"); 315 test.equal(FilterStorage.subscriptions[0].filters.length, 1, "Filter count a fter reloading");
331 }).catch(unexpectedError.bind(test)).then(() => test.done()); 316 }).catch(unexpectedError.bind(test)).then(() => test.done());
332 }; 317 };
OLDNEW
« lib/filterStorage.js ('K') | « lib/filterStorage.js ('k') | test/stub-modules/io.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld