toolkit/components/osfile/tests/mochi/main_test_osfile_async.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 "use strict";
     3 Components.utils.import("resource://gre/modules/osfile.jsm");
     4 Components.utils.import("resource://gre/modules/Promise.jsm");
     5 Components.utils.import("resource://gre/modules/Task.jsm");
     6 Components.utils.import("resource://gre/modules/AsyncShutdown.jsm");
     8 // The following are used to compare against a well-tested reference
     9 // implementation of file I/O.
    10 Components.utils.import("resource://gre/modules/NetUtil.jsm");
    11 Components.utils.import("resource://gre/modules/FileUtils.jsm");
    12 Components.utils.import("resource://gre/modules/Services.jsm");
    14 let myok = ok;
    15 let myis = is;
    16 let myinfo = info;
    17 let myisnot = isnot;
    19 let isPromise = function ispromise(value) {
    20   return value != null && typeof value == "object" && "then" in value;
    21 };
    23 let maketest = function(prefix, test) {
    24   let utils = {
    25     ok: function ok(t, m) {
    26       myok(t, prefix + ": " + m);
    27     },
    28     is: function is(l, r, m) {
    29       myis(l, r, prefix + ": " + m);
    30     },
    31     isnot: function isnot(l, r, m) {
    32       myisnot(l, r, prefix + ": " + m);
    33     },
    34     info: function info(m) {
    35       myinfo(prefix + ": " + m);
    36     },
    37     fail: function fail(m) {
    38       utils.ok(false, m);
    39     },
    40     okpromise: function okpromise(t, m) {
    41       return t.then(
    42         function onSuccess() {
    43           util.ok(true, m);
    44         },
    45         function onFailure() {
    46           util.ok(false, m);
    47         }
    48       );
    49     }
    50   };
    51   return function runtest() {
    52     utils.info("Entering");
    53     try {
    54       let result = test.call(this, utils);
    55       if (!isPromise(result)) {
    56         throw new TypeError("The test did not return a promise");
    57       }
    58       utils.info("This was a promise");
    59       // The test returns a promise
    60       result = result.then(function test_complete() {
    61         utils.info("Complete");
    62       }, function catch_uncaught_errors(err) {
    63         utils.fail("Uncaught error " + err);
    64         if (err && typeof err == "object" && "message" in err) {
    65           utils.fail("(" + err.message + ")");
    66         }
    67         if (err && typeof err == "object" && "stack" in err) {
    68           utils.fail("at " + err.stack);
    69         }
    70       });
    71       return result;
    72     } catch (x) {
    73       utils.fail("Error " + x + " at " + x.stack);
    74       return null;
    75     }
    76   };
    77 };
    79 /**
    80  * Fetch asynchronously the contents of a file using xpcom.
    81  *
    82  * Used for comparing xpcom-based results to os.file-based results.
    83  *
    84  * @param {string} path The _absolute_ path to the file.
    85  * @return {promise}
    86  * @resolves {string} The contents of the file.
    87  */
    88 let reference_fetch_file = function reference_fetch_file(path, test) {
    89   test.info("Fetching file " + path);
    90   let promise = Promise.defer();
    91   let file = new FileUtils.File(path);
    92   NetUtil.asyncFetch(file,
    93     function(stream, status) {
    94       if (!Components.isSuccessCode(status)) {
    95         promise.reject(status);
    96         return;
    97       }
    98       let result, reject;
    99       try {
   100         result = NetUtil.readInputStreamToString(stream, stream.available());
   101       } catch (x) {
   102         reject = x;
   103       }
   104       stream.close();
   105       if (reject) {
   106         promise.reject(reject);
   107       } else {
   108         promise.resolve(result);
   109       }
   110   });
   111   return promise.promise;
   112 };
   114 /**
   115  * Compare asynchronously the contents two files using xpcom.
   116  *
   117  * Used for comparing xpcom-based results to os.file-based results.
   118  *
   119  * @param {string} a The _absolute_ path to the first file.
   120  * @param {string} b The _absolute_ path to the second file.
   121  *
   122  * @resolves {null}
   123  */
   124 let reference_compare_files = function reference_compare_files(a, b, test) {
   125   test.info("Comparing files " + a + " and " + b);
   126   let a_contents = yield reference_fetch_file(a, test);
   127   let b_contents = yield reference_fetch_file(b, test);
   128   is(a_contents, b_contents, "Contents of files " + a + " and " + b + " match");
   129 };
   131 let reference_dir_contents = function reference_dir_contents(path) {
   132   let result = [];
   133   let entries = new FileUtils.File(path).directoryEntries;
   134   while (entries.hasMoreElements()) {
   135     let entry = entries.getNext().QueryInterface(Components.interfaces.nsILocalFile);
   136     result.push(entry.path);
   137   }
   138   return result;
   139 };
   141 // Set/Unset OS.Shared.DEBUG, OS.Shared.TEST and a console listener.
   142 function toggleDebugTest (pref, consoleListener) {
   143   Services.prefs.setBoolPref("toolkit.osfile.log", pref);
   144   Services.prefs.setBoolPref("toolkit.osfile.log.redirect", pref);
   145   Services.console[pref ? "registerListener" : "unregisterListener"](
   146     consoleListener);
   147 }
   149 let test = maketest("Main", function main(test) {
   150   return Task.spawn(function() {
   151     SimpleTest.waitForExplicitFinish();
   152     yield test_constants();
   153     yield test_path();
   154     yield test_stat();
   155     yield test_debug();
   156     yield test_info_features_detect();
   157     yield test_read_write();
   158     yield test_position();
   159     yield test_iter();
   160     yield test_exists();
   161     yield test_debug_test();
   162     info("Test is over");
   163     SimpleTest.finish();
   164   });
   165 });
   167 /**
   168  * A file that we know exists and that can be used for reading.
   169  */
   170 let EXISTING_FILE = OS.Path.join("chrome", "toolkit", "components",
   171   "osfile", "tests", "mochi", "main_test_osfile_async.js");
   173 /**
   174  * Test that OS.Constants is defined correctly.
   175  */
   176 let test_constants = maketest("constants", function constants(test) {
   177   return Task.spawn(function() {
   178     test.isnot(OS.Constants, null, "OS.Constants exists");
   179     test.ok(OS.Constants.Win || OS.Constants.libc, "OS.Constants.Win exists or OS.Constants.Unix exists");
   180     test.isnot(OS.Constants.Path, null, "OS.Constants.Path exists");
   181     test.isnot(OS.Constants.Sys, null, "OS.Constants.Sys exists");
   182   });
   183 });
   185 /**
   186  * Test that OS.Constants.Path paths are consistent.
   187  */
   188 let test_path = maketest("path",  function path(test) {
   189   return Task.spawn(function() {
   190     test.ok(OS.Path, "OS.Path exists");
   191     test.ok(OS.Constants.Path, "OS.Constants.Path exists");
   192     test.is(OS.Constants.Path.tmpDir, Services.dirsvc.get("TmpD", Components.interfaces.nsIFile).path, "OS.Constants.Path.tmpDir is correct");
   193     test.is(OS.Constants.Path.profileDir, Services.dirsvc.get("ProfD", Components.interfaces.nsIFile).path, "OS.Constants.Path.profileDir is correct");
   194     test.is(OS.Constants.Path.localProfileDir, Services.dirsvc.get("ProfLD", Components.interfaces.nsIFile).path, "OS.Constants.Path.localProfileDir is correct");
   195   });
   196 });
   198 /**
   199  * Test OS.File.stat and OS.File.prototype.stat
   200  */
   201 let test_stat = maketest("stat", function stat(test) {
   202   return Task.spawn(function() {
   203     // Open a file and stat it
   204     let file = yield OS.File.open(EXISTING_FILE);
   205     let stat1;
   207     try {
   208       test.info("Stating file");
   209       stat1 = yield file.stat();
   210       test.ok(true, "stat has worked " + stat1);
   211       test.ok(stat1, "stat is not empty");
   212     } finally {
   213       yield file.close();
   214     }
   216     // Stat the same file without opening it
   217     test.info("Stating a file without opening it");
   218     let stat2 = yield OS.File.stat(EXISTING_FILE);
   219     test.ok(true, "stat 2 has worked " + stat2);
   220     test.ok(stat2, "stat 2 is not empty");
   221     for (let key in stat2) {
   222       test.is("" + stat1[key], "" + stat2[key], "Stat field " + key + "is the same");
   223     }
   224   });
   225 });
   227 /**
   228  * Test feature detection using OS.File.Info.prototype on main thread
   229  */
   230 let test_info_features_detect = maketest("features_detect", function features_detect(test) {
   231   return Task.spawn(function() {
   232     if (OS.Constants.Win) {
   233       // see if winBirthDate is defined
   234       if ("winBirthDate" in OS.File.Info.prototype) {
   235         test.ok(true, "winBirthDate is defined");
   236       } else {
   237         test.fail("winBirthDate not defined though we are under Windows");
   238       }
   239     } else if (OS.Constants.libc) {
   240       // see if unixGroup is defined
   241       if ("unixGroup" in OS.File.Info.prototype) {
   242         test.ok(true, "unixGroup is defined");
   243       } else {
   244         test.fail("unixGroup is not defined though we are under Unix");
   245       }
   246     }
   247   });
   248 });
   250 /**
   251  * Test OS.File.prototype.{read, readTo, write}
   252  */
   253 let test_read_write = maketest("read_write", function read_write(test) {
   254   return Task.spawn(function() {
   255     // Test readTo/write
   256     let currentDir = yield OS.File.getCurrentDirectory();
   257     let pathSource = OS.Path.join(currentDir, EXISTING_FILE);
   258     let pathDest = OS.Path.join(OS.Constants.Path.tmpDir,
   259       "osfile async test.tmp");
   261     let fileSource = yield OS.File.open(pathSource);
   262     test.info("Input file opened");
   263     let fileDest = yield OS.File.open(pathDest,
   264       { truncate: true, read: true, write: true});
   265     test.info("Output file opened");
   267     let stat = yield fileSource.stat();
   268     test.info("Input stat worked");
   269     let size = stat.size;
   270     let array = new Uint8Array(size);
   272     try {
   273       test.info("Now calling readTo");
   274       let readLength = yield fileSource.readTo(array);
   275       test.info("ReadTo worked");
   276       test.is(readLength, size, "ReadTo got all bytes");
   277       let writeLength = yield fileDest.write(array);
   278       test.info("Write worked");
   279       test.is(writeLength, size, "Write wrote all bytes");
   281       // Test read
   282       yield fileSource.setPosition(0);
   283       let readAllResult = yield fileSource.read();
   284       test.info("ReadAll worked");
   285       test.is(readAllResult.length, size, "ReadAll read all bytes");
   286       test.is(Array.prototype.join.call(readAllResult),
   287               Array.prototype.join.call(array),
   288               "ReadAll result is correct");
   289     } finally {
   290       // Close stuff
   291       yield fileSource.close();
   292       yield fileDest.close();
   293       test.info("Files are closed");
   294     }
   296     stat = yield OS.File.stat(pathDest);
   297     test.is(stat.size, size, "Both files have the same size");
   298     yield reference_compare_files(pathSource, pathDest, test);
   300     // Cleanup.
   301     OS.File.remove(pathDest);
   302   });
   303 });
   306 /**
   307  * Test file.{getPosition, setPosition}
   308  */
   309 let test_position = maketest("position", function position(test) {
   310   return Task.spawn(function() {
   311     let file = yield OS.File.open(EXISTING_FILE);
   313     try {
   314       let stat = yield file.stat();
   315       test.info("Obtained file length");
   317       let view = new Uint8Array(stat.size);
   318       yield file.readTo(view);
   319       test.info("First batch of content read");
   321       let CHUNK_SIZE = 178;// An arbitrary number of bytes to read from the file
   322       let pos = yield file.getPosition();
   323       test.info("Obtained position");
   324       test.is(pos, view.byteLength, "getPosition returned the end of the file");
   325       pos = yield file.setPosition(-CHUNK_SIZE, OS.File.POS_END);
   326       test.info("Changed position");
   327       test.is(pos, view.byteLength - CHUNK_SIZE, "setPosition returned the correct position");
   329       let view2 = new Uint8Array(CHUNK_SIZE);
   330       yield file.readTo(view2);
   331       test.info("Read the end of the file");
   332       for (let i = 0; i < CHUNK_SIZE; ++i) {
   333         if (view2[i] != view[i + view.byteLength - CHUNK_SIZE]) {
   334           test.is(view2[i], view[i], "setPosition put us in the right position");
   335         }
   336       }
   337     } finally {
   338       yield file.close();
   339     }
   340   });
   341 });
   343 /**
   344  * Test OS.File.prototype.{DirectoryIterator}
   345  */
   346 let test_iter = maketest("iter", function iter(test) {
   347   return Task.spawn(function() {
   348     let currentDir = yield OS.File.getCurrentDirectory();
   350     // Trivial walks through the directory
   351     test.info("Preparing iteration");
   352     let iterator = new OS.File.DirectoryIterator(currentDir);
   353     let temporary_file_name = OS.Path.join(currentDir, "empty-temporary-file.tmp");
   354     try {
   355       yield OS.File.remove(temporary_file_name);
   356     } catch (err) {
   357       // Ignore errors removing file
   358     }
   359     let allFiles1 = yield iterator.nextBatch();
   360     test.info("Obtained all files through nextBatch");
   361     test.isnot(allFiles1.length, 0, "There is at least one file");
   362     test.isnot(allFiles1[0].path, null, "Files have a path");
   364     // Ensure that we have the same entries with |reference_dir_contents|
   365     let referenceEntries = new Set();
   366     for (let entry of reference_dir_contents(currentDir)) {
   367       referenceEntries.add(entry);
   368     }
   369     test.is(referenceEntries.size, allFiles1.length, "All the entries in the directory have been listed");
   370     for (let entry of allFiles1) {
   371       test.ok(referenceEntries.has(entry.path), "File " + entry.path + " effectively exists");
   372       // Ensure that we have correct isDir and isSymLink
   373       // Current directory is {objdir}/_tests/testing/mochitest/, assume it has some dirs and symlinks.
   374       var f = new FileUtils.File(entry.path);
   375       test.is(entry.isDir, f.isDirectory(), "Get file " + entry.path + " isDir correctly");
   376       test.is(entry.isSymLink, f.isSymlink(), "Get file " + entry.path + " isSymLink correctly");
   377     }
   379     yield iterator.close();
   380     test.info("Closed iterator");
   382     test.info("Double closing DirectoryIterator");
   383     iterator = new OS.File.DirectoryIterator(currentDir);
   384     yield iterator.close();
   385     yield iterator.close(); //double closing |DirectoryIterator|
   386     test.ok(true, "|DirectoryIterator| was closed twice successfully");
   388     let allFiles2 = [];
   389     let i = 0;
   390     iterator = new OS.File.DirectoryIterator(currentDir);
   391     yield iterator.forEach(function(entry, index) {
   392       test.is(i++, index, "Getting the correct index");
   393       allFiles2.push(entry);
   394     });
   395     test.info("Obtained all files through forEach");
   396     is(allFiles1.length, allFiles2.length, "Both runs returned the same number of files");
   397     for (let i = 0; i < allFiles1.length; ++i) {
   398       if (allFiles1[i].path != allFiles2[i].path) {
   399         test.is(allFiles1[i].path, allFiles2[i].path, "Both runs return the same files");
   400         break;
   401       }
   402     }
   404     // Testing batch iteration + whether an iteration can be stopped early
   405     let BATCH_LENGTH = 10;
   406     test.info("Getting some files through nextBatch");
   407     yield iterator.close();
   409     iterator = new OS.File.DirectoryIterator(currentDir);
   410     let someFiles1 = yield iterator.nextBatch(BATCH_LENGTH);
   411     let someFiles2 = yield iterator.nextBatch(BATCH_LENGTH);
   412     yield iterator.close();
   414     iterator = new OS.File.DirectoryIterator(currentDir);
   415     yield iterator.forEach(function cb(entry, index, iterator) {
   416       if (index < BATCH_LENGTH) {
   417         test.is(entry.path, someFiles1[index].path, "Both runs return the same files (part 1)");
   418       } else if (index < 2*BATCH_LENGTH) {
   419         test.is(entry.path, someFiles2[index - BATCH_LENGTH].path, "Both runs return the same files (part 2)");
   420       } else if (index == 2 * BATCH_LENGTH) {
   421         test.info("Attempting to stop asynchronous forEach");
   422         return iterator.close();
   423       } else {
   424         test.fail("Can we stop an asynchronous forEach? " + index);
   425       }
   426       return null;
   427     });
   428     yield iterator.close();
   430     // Ensuring that we find new files if they appear
   431     let file = yield OS.File.open(temporary_file_name, { write: true } );
   432     file.close();
   433     iterator = new OS.File.DirectoryIterator(currentDir);
   434     try {
   435       let files = yield iterator.nextBatch();
   436       is(files.length, allFiles1.length + 1, "The directory iterator has noticed the new file");
   437       let exists = yield iterator.exists();
   438       test.ok(exists, "After nextBatch, iterator detects that the directory exists");
   439     } finally {
   440       yield iterator.close();
   441     }
   443     // Ensuring that opening a non-existing directory fails consistently
   444     // once iteration starts.
   445     try {
   446       iterator = null;
   447       iterator = new OS.File.DirectoryIterator("/I do not exist");
   448       let exists = yield iterator.exists();
   449       test.ok(!exists, "Before any iteration, iterator detects that the directory doesn't exist");
   450       let exn = null;
   451       try {
   452         yield iterator.next();
   453       } catch (ex if ex instanceof OS.File.Error && ex.becauseNoSuchFile) {
   454         exn = ex;
   455         let exists = yield iterator.exists();
   456         test.ok(!exists, "After one iteration, iterator detects that the directory doesn't exist");
   457       }
   458       test.ok(exn, "Iterating through a directory that does not exist has failed with becauseNoSuchFile");
   459     } finally {
   460       if (iterator) {
   461         iterator.close();
   462       }
   463     }
   464     test.ok(!!iterator, "The directory iterator for a non-existing directory was correctly created");
   465   });
   466 });
   468 /**
   469  * Test OS.File.prototype.{exists}
   470  */
   471 let test_exists = maketest("exists", function exists(test) {
   472   return Task.spawn(function() {
   473     let fileExists = yield OS.File.exists(EXISTING_FILE);
   474     test.ok(fileExists, "file exists");
   475     fileExists = yield OS.File.exists(EXISTING_FILE + ".tmp");
   476     test.ok(!fileExists, "file does not exists");
   477   });
   478 });
   480 /**
   481  * Test changes to OS.Shared.DEBUG flag.
   482  */
   483 let test_debug = maketest("debug", function debug(test) {
   484   return Task.spawn(function() {
   485     function testSetDebugPref (pref) {
   486       try {
   487         Services.prefs.setBoolPref("toolkit.osfile.log", pref);
   488       } catch (x) {
   489         test.fail("Setting OS.Shared.DEBUG to " + pref +
   490           " should not cause error.");
   491       } finally {
   492         test.is(OS.Shared.DEBUG, pref, "OS.Shared.DEBUG is set correctly.");
   493       }
   494     }
   495     testSetDebugPref(true);
   496     let workerDEBUG = yield OS.File.GET_DEBUG();
   497     test.is(workerDEBUG, true, "Worker's DEBUG is set.");
   498     testSetDebugPref(false);
   499     workerDEBUG = yield OS.File.GET_DEBUG();
   500     test.is(workerDEBUG, false, "Worker's DEBUG is unset.");
   501   });
   502 });
   504 /**
   505  * Test logging in the main thread with set OS.Shared.DEBUG and
   506  * OS.Shared.TEST flags.
   507  */
   508 let test_debug_test = maketest("debug_test", function debug_test(test) {
   509   return Task.spawn(function () {
   510     // Create a console listener.
   511     let consoleListener = {
   512       observe: function (aMessage) {
   513         // Ignore unexpected messages.
   514         if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
   515           return;
   516         }
   517         if (aMessage.message.indexOf("TEST OS") < 0) {
   518           return;
   519         }
   520         test.ok(true, "DEBUG TEST messages are logged correctly.");
   521       }
   522     };
   523     toggleDebugTest(true, consoleListener);
   524     // Execution of OS.File.exist method will trigger OS.File.LOG several times.
   525     let fileExists = yield OS.File.exists(EXISTING_FILE);
   526     toggleDebugTest(false, consoleListener);
   527   });
   528 });

mercurial