test_metadatajob_errorcases.js
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 
30 var gTestFileLocation = "testharness/metadatamanager/errorcases/";
31 
32 // the number of errors we expect
34 // the number of retries we expect
36 
42 function retries(aHandlers) {
43  var contractId = "@songbirdnest.com/Songbird/MetadataHandler/";
44  const MAP = {
45  "taglib": "Taglib;1",
46  "gstreamer": "GStreamer;1",
47  "wma": "WMA;1"
48  };
49  var count = 0;
50  for each (var handler in aHandlers) {
51  var available = (contractId + MAP[String(handler).toLowerCase()]) in Cc;
52  if (available)
53  ++count;
54  }
55  return count - 1;
56 }
57 
62 function runTest() {
63  var files = [];
64  var filesToRemove = [];
65 
66  var isWindows = getPlatform() == "Windows_NT";
67 
69  // Set up test files //
71  // A file that doesnt exist
72 
73  var file = newAppRelativeFile("testharness/metadatamanager/errorcases/file_that_doesnt_exist.mp3");
74  assertEqual(file.exists(), false, "file_that_doesn't_exist shouldn't exist!");
75  files.push(file);
77  gRetriesExpected += retries(["taglib", "gstreamer"]);
78 
79  // Bogus files
80  var fakeFile = newAppRelativeFile("testharness/metadatamanager/errorcases/fake-file.mp3");
81  fakeFile = getCopyOfFile(fakeFile, "fake-file-temp.mp3");
82  files.push(fakeFile);
83  filesToRemove.push(fakeFile);
84  // fake file is not seen as an error
85  var corruptFile = newAppRelativeFile("testharness/metadatamanager/errorcases/corrupt.mp3");
86  corruptFile = getCopyOfFile(corruptFile, "corrupt-file-temp.mp3");
87  files.push(corruptFile);
88  filesToRemove.push(corruptFile);
89  // corrupt file is not seen as an error
90 
91  // Media files with the wrong extensions
92  files.push(newAppRelativeFile("testharness/metadatamanager/errorcases/mp3-disguised-as.flac"));
94  gRetriesExpected += retries(["taglib", "gstreamer"]);
95  files.push(newAppRelativeFile("testharness/metadatamanager/errorcases/mp3-disguised-as.ogg"));
97  gRetriesExpected += retries(["taglib", "gstreamer"]);
98  files.push(newAppRelativeFile("testharness/metadatamanager/errorcases/ogg-disguised-as.m4a"));
100  gRetriesExpected += retries(["taglib", "gstreamer"]);
101 
102  // Misc file permissions
103  file = newAppRelativeFile("testharness/metadatamanager/errorcases/access-tests.mp3");
104  var readonly = getCopyOfFile(file, "readonly.mp3");
105  readonly.permissions = 0400;
106  files.push(readonly);
107  filesToRemove.push(readonly);
108 
109  var writeonly = getCopyOfFile(file, "writeonly.mp3");
110  writeonly.permissions = 0200;
111  // If we aren't able to set write only, don't bother with this test (e.g. on windows)
112  if ((writeonly.permissions & 0777) == 0200) {
113  files.push(writeonly);
114  gErrorExpected++;
115  gRetriesExpected += retries(["taglib", "gstreamer"]);
116  } else {
117  log("MetadataJob_ErrorCases: platform does not support write-only. Perms=" + (writeonly.permissions & 0777));
118  }
119  filesToRemove.push(writeonly);
120 
121  var noaccess = getCopyOfFile(file, "noaccess.mp3");
122  noaccess.permissions = 0000;
123  files.push(noaccess);
124  filesToRemove.push(noaccess);
125  if (!isWindows) {
126  // only seen as an error on non-Windows (Windows doesn't support permissions correctly)
128  gRetriesExpected += retries(["taglib", "gstreamer"]);
129  }
130 
131  // A remote file that doesn't exist
132  // XXX: temporarily, we use a non-default port. Unfortunately, we don't
133  // fail properly if we get a 404 from the server! This is just to fix the
134  // unit tests for now; once the other issues are fixed this should go back
135  // to port 80.
136  files.push(newURI("http://localhost:12345/remote/file/that/doesnt/exist.mp3"));
137  gErrorExpected++;
138  gRetriesExpected += retries(["taglib", "gstreamer"]);
139 
140 
142  // Load the files into two libraries //
144  log("Creating libraries");
145  var library1 = createNewLibrary( "test_metadatajob_errorcases_library1" );
146  var library2 = createNewLibrary( "test_metadatajob_errorcases_library2" );
147  var items1 = importFilesToLibrary(files, library1);
148  var items2 = importFilesToLibrary(files, library2);
149  // We need to make the items in the two libraries copies of each other
150  // So the synchronization logic is happy
151  log("Populating library2");
152  for (index = 0; index < items1.length; ++index) {
153  // Set item 2 to be a copy of item 1
154  let item1 = library1.getItemByIndex(index);
155  let item2 = library2.getItemByIndex(index);
156  // hopefully these should all be in the same order on reimport
157  assertEqual(item1.contentSrc.spec,
158  item2.contentSrc.spec,
159  "expected item " + index + " of libraries to match");
160  item2.setProperty(SBProperties.originItemGuid, item1.guid);
161  item2.setProperty(SBProperties.originLibraryGuid, item2.library.guid);
162 
163  }
164 
165  assertEqual(items1.length,
166  files.length,
167  "expecting number of added items in library1 to equal total number of files");
168  assertEqual(items2.length,
169  files.length,
170  "expecting number of added items in library2 to equal total number of files");
171 
172  var job = startMetadataJob(items1, "read");
173 
174 
176  // Write new metadata to the files //
178 
179  // Called when the first scan into library1 completes
180  function onLib1ReadComplete(job) {
181  try {
182  reportJobProgress(job, "onLib1ReadComplete");
183 
184  if (job.status == Ci.sbIJobProgress.STATUS_RUNNING) {
185  return;
186  }
187  job.removeJobProgressListener(onLib1ReadComplete);
188 
189  // Verify job progress reporting.
190 
191  assertEqual(files.length + gRetriesExpected,
192  job.total,
193  "expected files plus retries to equal job total");
194  assertEqual(files.length + gRetriesExpected,
195  job.progress,
196  "expected files plus retries to equal job progress");
197  assertEqual(job.status,
198  Ci.sbIJobProgress.STATUS_FAILED,
199  "expected job to be failed");
200 
201  // Ok great, lets try writing back new metadata for all the files via library 2
202  var propertiesToWrite = [ SBProperties.artistName,
203  SBProperties.albumName,
204  SBProperties.trackName
205  ];
206 
207  for each (var item in items2) {
208  for each (var prop in propertiesToWrite) {
209  item.setProperty(prop, prop);
210  }
211  }
212 
213  job = startMetadataJob(items2, "write", propertiesToWrite);
214 
215  // Wait for reading to complete before continuing
216  job.addJobProgressListener(onWriteComplete);
217 
218  // print errors, since otherwise they will be eaten by the observe call
219  } catch (e) {
220  doFail(e);
221  }
222  }
223 
224 
226  // Read the metadata into library 2 //
228 
229  // Called when the write out from library2 completes
230  function onWriteComplete(job) {
231  try {
232  reportJobProgress(job, "onWriteComplete");
233 
234  if (job.status == Ci.sbIJobProgress.STATUS_RUNNING) {
235  return;
236  }
237  job.removeJobProgressListener(onWriteComplete);
238 
239  // Nothing should have been written.
240  // Make sure by reimporting library2 and comparing it with library1
241  library2.clear();
242  items2 = importFilesToLibrary(files, library2);
243  for (index = 0; index < items1.length; ++index) {
244  let item1 = library1.getItemByIndex(index);
245  let item2 = library2.getItemByIndex(index);
246  // hopefully these should all be in the same order on reimport
247  assertEqual(item1.contentSrc.spec,
248  item2.contentSrc.spec,
249  "expected item " + index + " of libraries to match");
250  item2.setProperty(SBProperties.originItemGuid, item1.guid);
251  item2.setProperty(SBProperties.originLibraryGuid, item2.library.guid);
252  }
253  assertEqual(items2.length,
254  files.length,
255  "expected number of items added to library to be all files");
256 
257  // Verify job progress reporting.
258  assertEqual(job.total - 2,
259  job.errorCount,
260  "expected all but 2 items to fail");
261  assertEqual(job.total,
262  files.length,
263  "expected the total to be the number of files");
264  assertEqual(job.progress,
265  files.length,
266  "expected the process to be the number of files");
267  assertEqual(job.status,
268  Ci.sbIJobProgress.STATUS_FAILED,
269  "expected the job to have failed");
270 
271  job = startMetadataJob(items2, "read");
272 
273  // Wait for reading to complete before continuing
274  job.addJobProgressListener(onLib2ReadComplete);
275 
276  // print errors, since otherwise they will be eaten by the observe call
277  } catch (e) {
278  doFail(e);
279  }
280  }
281 
282 
284  // Compare library 1 with library 2 //
286 
287  // Called when reading metadata back into library2 completes
288  function onLib2ReadComplete(job) {
289  try {
290  reportJobProgress(job, "onLib2ReadComplete");
291 
292  if (job.status == Ci.sbIJobProgress.STATUS_RUNNING) {
293  return;
294  }
295  job.removeJobProgressListener(onLib2ReadComplete);
296 
297  // Make sure writing didnt break anything by
298  // comparing library1 with library2
299  var diffingService = Cc["@songbirdnest.com/Songbird/Library/DiffingService;1"]
300  .getService(Ci.sbILibraryDiffingService);
301  var libraryChangeset = diffingService.createChangeset(library2,
302  library1);
303  var changes = libraryChangeset.changes;
304  log("\n\n\nMetadataJob_ErrorCases: There are " + changes.length +
305  " differences between library1 and library2.\n\n");
306  var changesEnum = changes.enumerate();
307 
308  // Only the bogus files should have changed,
309  // since taglib doesn't know to leave them alone.
310  var fakeFileURL = newFileURI(fakeFile).spec;
311  var corruptFileURL = newFileURI(corruptFile).spec;
312  while(changesEnum.hasMoreElements()) {
313  var change = changesEnum.getNext().QueryInterface(Ci.sbILibraryChange);
314  var propEnum = change.properties.enumerate();
315  var url = change.sourceItem.contentSrc.spec;
316  log("MetadataJob_ErrorCases: changes in " +
317  url + "\n");
318  while(propEnum.hasMoreElements()) {
319  var prop = propEnum.getNext().QueryInterface(Ci.sbIPropertyChange);
320  log("\t\t[" + prop.id + "] " + prop.oldValue + " -> " + prop.newValue + "\n");
321  }
322  assertTrue(url == fakeFileURL || url == corruptFileURL,
323  "expected url to be either fakeFileURL or curruptFileURL");
324  }
325  assertEqual(changes.length,
326  2,
327  "expected 2 changes");
328 
329  // Verify job progress reporting. Do this last since the info above is
330  // useful for debugging.
331 
332  assertEqual(job.errorCount,
334  "error count unexpected");
335  assertEqual(files.length + gRetriesExpected,
336  job.total,
337  "expected files plus retries to equal total");
338  assertEqual(files.length + gRetriesExpected,
339  job.progress,
340  "expected files plus retries to equal progress");
341  assertEqual(job.status,
342  Ci.sbIJobProgress.STATUS_FAILED,
343  "expected job to have failed");
344 
345  // print errors, since otherwise they will be eaten by the observe call
346  } catch (e) {
347  doFail(e);
348  }
349  finish();
350  }
351 
353  // Get rid of temp files and finish the test //
355  function finish() {
356  try {
357  // Clean up temp files
358  for each (file in filesToRemove) {
359  // Restore file perms so that windows can remove the file
360  file.permissions = 0600;
361 
362  file.remove(true);
363  }
364  job = null;
365  } catch (e) {
366  doFail(e);
367  }
368  testFinished();
369  }
370 
371  // Wait for reading to complete before continuing
372  job.addJobProgressListener(onLib1ReadComplete);
373  testPending();
374 }
375 
376 
377 
381 function importFilesToLibrary(files, library) {
382  var items = [];
383  for each (var file in files) {
384  if (!(file instanceof Ci.nsIURI)) {
385  file = newFileURI(file);
386  }
387  items.push(library.createMediaItem(file, null, true));
388  }
389  return items;
390 }
391 
392 
396 function startMetadataJob(items, type, writeProperties) {
397  var prefSvc = Cc["@mozilla.org/preferences-service;1"]
398  .getService(Ci.nsIPrefBranch);
399  var oldWritingEnabledPref = prefSvc.getBoolPref("songbird.metadata.enableWriting");
400  prefSvc.setBoolPref("songbird.metadata.enableWriting", true);
401  var array = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
402  .createInstance(Ci.nsIMutableArray);
403  for each (var item in items) {
404  array.appendElement(item, false);
405  }
406  manager = Cc["@songbirdnest.com/Songbird/FileMetadataService;1"]
407  .getService(Ci.sbIFileMetadataService);
408  var job;
409  if (type == "write") {
410  job = manager.write(array, ArrayConverter.stringEnumerator(writeProperties));
411  } else {
412  job = manager.read(array);
413  }
414  prefSvc.setBoolPref("songbird.metadata.enableWriting", oldWritingEnabledPref);
415 
416  return job;
417 }
function startMetadataJob(items, type, writeProperties)
let prefSvc
const Cc
function importFilesToLibrary(files, library)
function newAppRelativeFile(path)
function getPlatform()
inArray array
function log(s)
function testFinished()
function createNewLibrary(databaseGuid, databaseLocation)
function assertTrue(aTest, aMessage)
function assertEqual(aExpected, aActual, aMessage)
var count
Definition: test_bug7406.js:32
function reportJobProgress(job, jobName)
function retries(aHandlers)
function getCopyOfFile(file, tempName, optionalLocation)
function newFileURI(file)
return null
Definition: FeedWriter.js:1143
function newURI(aURLString)
function url(spec)
const Ci
function doFail(text)
function runTest()
Advanced DataRemote unit tests.
function onWriteComplete(job)
GstMessage gpointer data sbGStreamerMessageHandler * handler
var gTestFileLocation
Test error handling in metadata jobs.
var file
function testPending()