2010-11-29 16:41:17 -08:00
|
|
|
var btoa;
|
|
|
|
|
2008-06-23 16:23:57 -07:00
|
|
|
// initialize nss
|
|
|
|
let ch = Cc["@mozilla.org/security/hash;1"].
|
|
|
|
createInstance(Ci.nsICryptoHash);
|
|
|
|
|
2008-04-30 13:01:17 -07:00
|
|
|
let ds = Cc["@mozilla.org/file/directory_service;1"]
|
|
|
|
.getService(Ci.nsIProperties);
|
|
|
|
|
|
|
|
let provider = {
|
|
|
|
getFile: function(prop, persistent) {
|
|
|
|
persistent.value = true;
|
2010-08-09 09:38:18 -07:00
|
|
|
switch (prop) {
|
|
|
|
case "ExtPrefDL":
|
|
|
|
return [ds.get("CurProcD", Ci.nsIFile)];
|
|
|
|
case "ProfD":
|
|
|
|
return ds.get("CurProcD", Ci.nsIFile);
|
|
|
|
case "UHist":
|
|
|
|
let histFile = ds.get("CurProcD", Ci.nsIFile);
|
|
|
|
histFile.append("history.dat");
|
|
|
|
return histFile;
|
|
|
|
default:
|
|
|
|
throw Cr.NS_ERROR_FAILURE;
|
|
|
|
}
|
2008-04-30 13:01:17 -07:00
|
|
|
},
|
|
|
|
QueryInterface: function(iid) {
|
|
|
|
if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
|
|
|
|
iid.equals(Ci.nsISupports)) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
throw Cr.NS_ERROR_NO_INTERFACE;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
ds.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
|
|
|
|
|
2008-06-06 21:40:30 -07:00
|
|
|
function loadInSandbox(aUri) {
|
|
|
|
var sandbox = Components.utils.Sandbox(this);
|
|
|
|
var request = Components.
|
|
|
|
classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
|
|
|
createInstance();
|
|
|
|
|
|
|
|
request.open("GET", aUri, false);
|
2009-03-20 00:14:21 -07:00
|
|
|
request.overrideMimeType("application/javascript");
|
2008-06-06 21:40:30 -07:00
|
|
|
request.send(null);
|
2009-03-20 00:14:21 -07:00
|
|
|
Components.utils.evalInSandbox(request.responseText, sandbox, "1.8");
|
2008-06-06 21:40:30 -07:00
|
|
|
|
|
|
|
return sandbox;
|
|
|
|
}
|
2008-06-09 20:51:23 -07:00
|
|
|
|
2008-06-16 16:42:32 -07:00
|
|
|
function FakeTimerService() {
|
2010-06-16 14:30:08 -07:00
|
|
|
Cu.import("resource://services-sync/util.js");
|
2008-06-16 16:42:32 -07:00
|
|
|
|
|
|
|
this.callbackQueue = [];
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
this.__proto__ = {
|
|
|
|
makeTimerForCall: function FTS_makeTimerForCall(cb) {
|
|
|
|
// Just add the callback to our queue and we'll call it later, so
|
|
|
|
// as to simulate a real nsITimer.
|
|
|
|
self.callbackQueue.push(cb);
|
|
|
|
return "fake nsITimer";
|
|
|
|
},
|
|
|
|
processCallback: function FTS_processCallbacks() {
|
|
|
|
var cb = self.callbackQueue.pop();
|
|
|
|
if (cb) {
|
|
|
|
cb();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Utils.makeTimerForCall = self.makeTimerForCall;
|
|
|
|
};
|
|
|
|
|
2010-11-29 16:41:17 -08:00
|
|
|
btoa = Cu.import("resource://services-sync/log4moz.js").btoa;
|
2008-06-19 17:04:04 -07:00
|
|
|
function getTestLogger(component) {
|
2008-11-03 14:41:39 -08:00
|
|
|
return Log4Moz.repository.getLogger("Testing");
|
2008-06-19 17:04:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
function initTestLogging(level) {
|
2008-06-16 16:22:00 -07:00
|
|
|
function LogStats() {
|
|
|
|
this.errorsLogged = 0;
|
|
|
|
}
|
|
|
|
LogStats.prototype = {
|
2008-06-09 20:51:23 -07:00
|
|
|
format: function BF_format(message) {
|
|
|
|
if (message.level == Log4Moz.Level.Error)
|
2008-06-16 16:22:00 -07:00
|
|
|
this.errorsLogged += 1;
|
2008-06-09 20:51:23 -07:00
|
|
|
return message.loggerName + "\t" + message.levelDesc + "\t" +
|
|
|
|
message.message + "\n";
|
|
|
|
}
|
|
|
|
};
|
2008-06-16 16:22:00 -07:00
|
|
|
LogStats.prototype.__proto__ = new Log4Moz.Formatter();
|
2008-06-09 20:51:23 -07:00
|
|
|
|
2008-11-03 14:41:39 -08:00
|
|
|
var log = Log4Moz.repository.rootLogger;
|
2008-06-16 16:22:00 -07:00
|
|
|
var logStats = new LogStats();
|
|
|
|
var appender = new Log4Moz.DumpAppender(logStats);
|
2008-06-19 17:04:04 -07:00
|
|
|
|
|
|
|
if (typeof(level) == "undefined")
|
|
|
|
level = "Debug";
|
|
|
|
getTestLogger().level = Log4Moz.Level[level];
|
|
|
|
|
|
|
|
log.level = Log4Moz.Level.Trace;
|
|
|
|
appender.level = Log4Moz.Level.Trace;
|
2010-06-01 15:07:50 -07:00
|
|
|
// Overwrite any other appenders (e.g. from previous incarnations)
|
|
|
|
log._appenders = [appender];
|
2008-06-09 20:51:23 -07:00
|
|
|
|
2008-06-16 16:22:00 -07:00
|
|
|
return logStats;
|
|
|
|
}
|
|
|
|
|
2008-06-18 16:51:54 -07:00
|
|
|
function FakePrefService(contents) {
|
2010-06-16 14:30:08 -07:00
|
|
|
Cu.import("resource://services-sync/util.js");
|
2008-06-18 16:51:54 -07:00
|
|
|
this.fakeContents = contents;
|
|
|
|
Utils.__prefs = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
FakePrefService.prototype = {
|
|
|
|
_getPref: function fake__getPref(pref) {
|
2008-06-19 17:04:04 -07:00
|
|
|
getTestLogger().trace("Getting pref: " + pref);
|
2008-06-18 16:51:54 -07:00
|
|
|
return this.fakeContents[pref];
|
|
|
|
},
|
|
|
|
getCharPref: function fake_getCharPref(pref) {
|
|
|
|
return this._getPref(pref);
|
|
|
|
},
|
|
|
|
getBoolPref: function fake_getBoolPref(pref) {
|
|
|
|
return this._getPref(pref);
|
|
|
|
},
|
|
|
|
getIntPref: function fake_getIntPref(pref) {
|
|
|
|
return this._getPref(pref);
|
|
|
|
},
|
|
|
|
addObserver: function fake_addObserver() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
function FakePasswordService(contents) {
|
2010-06-16 14:30:08 -07:00
|
|
|
Cu.import("resource://services-sync/util.js");
|
2008-06-18 16:51:54 -07:00
|
|
|
|
|
|
|
this.fakeContents = contents;
|
|
|
|
let self = this;
|
|
|
|
|
|
|
|
Utils.findPassword = function fake_findPassword(realm, username) {
|
2008-06-19 17:04:04 -07:00
|
|
|
getTestLogger().trace("Password requested for " +
|
|
|
|
realm + ":" + username);
|
2008-06-18 17:28:28 -07:00
|
|
|
if (realm in self.fakeContents && username in self.fakeContents[realm])
|
|
|
|
return self.fakeContents[realm][username];
|
|
|
|
else
|
|
|
|
return null;
|
2008-06-18 16:51:54 -07:00
|
|
|
};
|
|
|
|
};
|
2008-06-20 17:47:32 -07:00
|
|
|
|
|
|
|
function FakeFilesystemService(contents) {
|
|
|
|
this.fakeContents = contents;
|
|
|
|
|
|
|
|
let self = this;
|
|
|
|
|
|
|
|
Utils.getProfileFile = function fake_getProfileFile(arg) {
|
|
|
|
let fakeNsILocalFile = {
|
|
|
|
exists: function() {
|
|
|
|
return this._fakeFilename in self.fakeContents;
|
|
|
|
},
|
|
|
|
_fakeFilename: (typeof(arg) == "object") ? arg.path : arg
|
|
|
|
};
|
|
|
|
return fakeNsILocalFile;
|
|
|
|
};
|
|
|
|
|
|
|
|
Utils.readStream = function fake_readStream(stream) {
|
|
|
|
getTestLogger().info("Reading from stream.");
|
|
|
|
return stream._fakeData;
|
|
|
|
};
|
|
|
|
|
|
|
|
Utils.open = function fake_open(file, mode) {
|
|
|
|
switch (mode) {
|
|
|
|
case "<":
|
|
|
|
mode = "reading";
|
|
|
|
break;
|
|
|
|
case ">":
|
|
|
|
mode = "writing";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Error("Unexpected mode: " + mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
getTestLogger().info("Opening '" + file._fakeFilename + "' for " +
|
|
|
|
mode + ".");
|
|
|
|
var contents = "";
|
|
|
|
if (file._fakeFilename in self.fakeContents && mode == "reading")
|
|
|
|
contents = self.fakeContents[file._fakeFilename];
|
|
|
|
let fakeStream = {
|
|
|
|
writeString: function(data) {
|
|
|
|
contents += data;
|
|
|
|
getTestLogger().info("Writing data to local file '" +
|
|
|
|
file._fakeFilename +"': " + data);
|
|
|
|
},
|
|
|
|
close: function() {
|
|
|
|
self.fakeContents[file._fakeFilename] = contents;
|
|
|
|
},
|
|
|
|
get _fakeData() { return contents; }
|
|
|
|
};
|
|
|
|
return [fakeStream];
|
|
|
|
};
|
|
|
|
};
|
2008-06-23 14:13:46 -07:00
|
|
|
|
|
|
|
function FakeGUIDService() {
|
|
|
|
let latestGUID = 0;
|
|
|
|
|
|
|
|
Utils.makeGUID = function fake_makeGUID() {
|
|
|
|
return "fake-guid-" + latestGUID++;
|
|
|
|
};
|
|
|
|
}
|
2008-06-23 20:57:10 -07:00
|
|
|
|
2010-06-01 15:07:50 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mock implementation of IWeaveCrypto. It does not encrypt or
|
|
|
|
* decrypt, just returns the input verbatimly.
|
|
|
|
*/
|
|
|
|
function FakeCryptoService() {
|
|
|
|
this.counter = 0;
|
|
|
|
|
|
|
|
delete Svc.Crypto; // get rid of the getter first
|
|
|
|
Svc.Crypto = this;
|
|
|
|
Utils.sha256HMAC = this.sha256HMAC;
|
|
|
|
}
|
|
|
|
FakeCryptoService.prototype = {
|
|
|
|
|
|
|
|
sha256HMAC: function(message, key) {
|
|
|
|
message = message.substr(0, 64);
|
|
|
|
while (message.length < 64) {
|
|
|
|
message += " ";
|
|
|
|
}
|
|
|
|
return message;
|
|
|
|
},
|
|
|
|
|
|
|
|
encrypt: function(aClearText, aSymmetricKey, aIV) {
|
|
|
|
return aClearText;
|
|
|
|
},
|
|
|
|
|
|
|
|
decrypt: function(aCipherText, aSymmetricKey, aIV) {
|
|
|
|
return aCipherText;
|
|
|
|
},
|
|
|
|
|
|
|
|
generateRandomKey: function() {
|
2010-11-29 16:41:17 -08:00
|
|
|
return btoa("fake-symmetric-key-" + this.counter++);
|
2010-06-01 15:07:50 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
generateRandomIV: function() {
|
2010-06-23 03:36:48 -07:00
|
|
|
// A base64-encoded IV is 24 characters long
|
2010-11-29 16:41:17 -08:00
|
|
|
return btoa("fake-fake-fake-random-iv");
|
2010-06-01 15:07:50 -07:00
|
|
|
},
|
|
|
|
|
2010-11-29 16:41:17 -08:00
|
|
|
expandData : function expandData(data, len) {
|
|
|
|
return data;
|
2010-06-01 15:07:50 -07:00
|
|
|
},
|
|
|
|
|
2010-11-29 16:41:17 -08:00
|
|
|
deriveKeyFromPassphrase : function (passphrase, salt, keyLength) {
|
|
|
|
return "some derived key string composed of bytes";
|
2010-06-01 15:07:50 -07:00
|
|
|
},
|
|
|
|
|
2010-11-29 16:41:17 -08:00
|
|
|
generateRandomBytes: function(aByteCount) {
|
|
|
|
return "not-so-random-now-are-we-HA-HA-HA! >:)".slice(aByteCount);
|
2010-06-01 15:07:50 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-06-26 15:01:34 -07:00
|
|
|
function SyncTestingInfrastructure(engineFactory) {
|
2008-06-23 20:57:10 -07:00
|
|
|
let __fakePasswords = {
|
|
|
|
'Mozilla Services Password': {foo: "bar"},
|
2010-11-29 16:41:17 -08:00
|
|
|
'Mozilla Services Encryption Passphrase': {foo: "a-abcde-abcde-abcde-abcde-abcde"}
|
2008-06-23 20:57:10 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
let __fakePrefs = {
|
|
|
|
"encryption" : "none",
|
|
|
|
"log.logger.service.crypto" : "Debug",
|
|
|
|
"log.logger.service.engine" : "Debug",
|
2008-06-24 16:55:56 -07:00
|
|
|
"log.logger.async" : "Debug",
|
|
|
|
"xmpp.enabled" : false
|
2008-06-23 20:57:10 -07:00
|
|
|
};
|
|
|
|
|
2010-06-16 14:30:08 -07:00
|
|
|
Cu.import("resource://services-sync/identity.js");
|
|
|
|
Cu.import("resource://services-sync/util.js");
|
2008-06-23 20:57:10 -07:00
|
|
|
|
|
|
|
ID.set('WeaveID',
|
|
|
|
new Identity('Mozilla Services Encryption Passphrase', 'foo'));
|
2008-06-30 11:54:10 -07:00
|
|
|
ID.set('WeaveCryptoID',
|
|
|
|
new Identity('Mozilla Services Encryption Passphrase', 'foo'));
|
2008-06-23 20:57:10 -07:00
|
|
|
|
|
|
|
this.fakePasswordService = new FakePasswordService(__fakePasswords);
|
|
|
|
this.fakePrefService = new FakePrefService(__fakePrefs);
|
|
|
|
this.fakeTimerService = new FakeTimerService();
|
|
|
|
this.logStats = initTestLogging();
|
|
|
|
this.fakeFilesystem = new FakeFilesystemService({});
|
|
|
|
this.fakeGUIDService = new FakeGUIDService();
|
2010-06-01 15:07:50 -07:00
|
|
|
this.fakeCryptoService = new FakeCryptoService();
|
2008-06-23 20:57:10 -07:00
|
|
|
|
2008-06-26 11:07:13 -07:00
|
|
|
this._logger = getTestLogger();
|
2008-06-26 15:01:34 -07:00
|
|
|
this._engineFactory = engineFactory;
|
2008-06-26 11:07:13 -07:00
|
|
|
this._clientStates = [];
|
|
|
|
|
|
|
|
this.saveClientState = function pushClientState(label) {
|
|
|
|
let state = Utils.deepCopy(this.fakeFilesystem.fakeContents);
|
2008-06-26 15:01:34 -07:00
|
|
|
let currContents = this.fakeFilesystem.fakeContents;
|
|
|
|
this.fakeFilesystem.fakeContents = [];
|
|
|
|
let engine = this._engineFactory();
|
|
|
|
let snapshot = Utils.deepCopy(engine._store.wrap());
|
|
|
|
this._clientStates[label] = {state: state, snapshot: snapshot};
|
|
|
|
this.fakeFilesystem.fakeContents = currContents;
|
2008-06-26 11:07:13 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
this.restoreClientState = function restoreClientState(label) {
|
2008-06-26 15:01:34 -07:00
|
|
|
let state = this._clientStates[label].state;
|
|
|
|
let snapshot = this._clientStates[label].snapshot;
|
2008-06-26 11:07:13 -07:00
|
|
|
|
|
|
|
function _restoreState() {
|
|
|
|
let self = yield;
|
|
|
|
|
2008-06-26 15:01:34 -07:00
|
|
|
this.fakeFilesystem.fakeContents = [];
|
|
|
|
let engine = this._engineFactory();
|
2008-06-26 11:07:13 -07:00
|
|
|
engine._store.wipe();
|
|
|
|
let originalSnapshot = Utils.deepCopy(engine._store.wrap());
|
|
|
|
|
|
|
|
engine._core.detectUpdates(self.cb, originalSnapshot, snapshot);
|
|
|
|
let commands = yield;
|
|
|
|
|
|
|
|
engine._store.applyCommands.async(engine._store, self.cb, commands);
|
|
|
|
yield;
|
2008-06-26 15:01:34 -07:00
|
|
|
|
|
|
|
this.fakeFilesystem.fakeContents = Utils.deepCopy(state);
|
2008-06-26 11:07:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
let self = this;
|
|
|
|
|
|
|
|
function restoreState(cb) {
|
|
|
|
_restoreState.async(self, cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.runAsyncFunc("restore client state of " + label,
|
|
|
|
restoreState);
|
|
|
|
};
|
|
|
|
|
2008-06-23 20:57:10 -07:00
|
|
|
this.__makeCallback = function __makeCallback() {
|
|
|
|
this.__callbackCalled = false;
|
|
|
|
let self = this;
|
|
|
|
return function callback() {
|
|
|
|
self.__callbackCalled = true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2008-06-26 15:01:34 -07:00
|
|
|
this.doSync = function doSync(name) {
|
|
|
|
let self = this;
|
|
|
|
|
|
|
|
function freshEngineSync(cb) {
|
|
|
|
let engine = self._engineFactory();
|
|
|
|
engine.sync(cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.runAsyncFunc(name, freshEngineSync);
|
|
|
|
};
|
|
|
|
|
2008-06-23 20:57:10 -07:00
|
|
|
this.runAsyncFunc = function runAsyncFunc(name, func) {
|
2008-06-26 11:07:13 -07:00
|
|
|
let logger = this._logger;
|
2008-06-23 20:57:10 -07:00
|
|
|
|
|
|
|
logger.info("-----------------------------------------");
|
|
|
|
logger.info("Step '" + name + "' starting.");
|
|
|
|
logger.info("-----------------------------------------");
|
|
|
|
func(this.__makeCallback());
|
|
|
|
while (this.fakeTimerService.processCallback()) {}
|
|
|
|
do_check_true(this.__callbackCalled);
|
|
|
|
for (name in Async.outstandingGenerators)
|
|
|
|
logger.warn("Outstanding generator exists: " + name);
|
|
|
|
do_check_eq(this.logStats.errorsLogged, 0);
|
|
|
|
do_check_eq(Async.outstandingGenerators.length, 0);
|
|
|
|
logger.info("Step '" + name + "' succeeded.");
|
|
|
|
};
|
2008-06-26 11:07:13 -07:00
|
|
|
|
|
|
|
this.resetClientState = function resetClientState() {
|
|
|
|
this.fakeFilesystem.fakeContents = {};
|
2008-06-26 15:01:34 -07:00
|
|
|
let engine = this._engineFactory();
|
2008-06-26 11:07:13 -07:00
|
|
|
engine._store.wipe();
|
|
|
|
};
|
2008-06-23 20:57:10 -07:00
|
|
|
}
|
2009-08-19 18:01:06 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Print some debug message to the console. All arguments will be printed,
|
|
|
|
* separated by spaces.
|
|
|
|
*
|
|
|
|
* @param [arg0, arg1, arg2, ...]
|
|
|
|
* Any number of arguments to print out
|
|
|
|
* @usage _("Hello World") -> prints "Hello World"
|
|
|
|
* @usage _(1, 2, 3) -> prints "1 2 3"
|
|
|
|
*/
|
|
|
|
let _ = function(some, debug, text, to) print(Array.slice(arguments).join(" "));
|
2010-03-25 19:23:44 -07:00
|
|
|
|
|
|
|
_("Setting the identity for passphrase");
|
2010-06-16 14:30:08 -07:00
|
|
|
Cu.import("resource://services-sync/identity.js");
|
2010-03-25 19:23:44 -07:00
|
|
|
|
2010-12-07 13:20:16 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Test setup helpers.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Turn WBO cleartext into "encrypted" payload as it goes over the wire
|
|
|
|
function encryptPayload(cleartext) {
|
|
|
|
if (typeof cleartext == "object") {
|
|
|
|
cleartext = JSON.stringify(cleartext);
|
|
|
|
}
|
|
|
|
|
|
|
|
return {ciphertext: cleartext, // ciphertext == cleartext with fake crypto
|
|
|
|
IV: "irrelevant",
|
|
|
|
hmac: Utils.sha256HMAC(cleartext, Utils.makeHMACKey(""))};
|
|
|
|
}
|
|
|
|
|