Save prekeys optimistically, track confirms, new clean behavior ()

* Re-enable libtextsecure unit tests, get passing, run in CI

* Save prekeys optimistically, track confirmed, new clean behavior

* Eliminate potential conflicts when rotating on startup

* Remove last symlink: get libtextsecure tests running on windows
This commit is contained in:
Scott Nonnenberg 2017-12-01 13:35:39 -08:00 committed by GitHub
parent 21325bc922
commit c195ba2630
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 433 additions and 147 deletions

View file

@ -335,71 +335,82 @@ module.exports = function(grunt) {
}); });
}); });
grunt.registerTask('unit-tests', 'Run unit tests inside Electron', function() { function runTests(environment, cb) {
var environment = grunt.option('env') || 'test'; var failure;
var done = this.async(); var Application = require('spectron').Application;
var failure; var electronBinary = process.platform === 'win32' ? 'electron.cmd' : 'electron';
var app = new Application({
var Application = require('spectron').Application; path: path.join(__dirname, 'node_modules', '.bin', electronBinary),
var electronBinary = process.platform === 'win32' ? 'electron.cmd' : 'electron'; args: [path.join(__dirname, 'main.js')],
var app = new Application({ env: {
path: path.join(__dirname, 'node_modules', '.bin', electronBinary), NODE_ENV: environment
args: [path.join(__dirname, 'main.js')],
env: {
NODE_ENV: environment
}
});
function getMochaResults() {
return window.mochaResults;
} }
});
app.start().then(function() { function getMochaResults() {
return app.client.waitUntil(function() { return window.mochaResults;
return app.client.execute(getMochaResults).then(function(data) { }
return Boolean(data.value);
}); app.start().then(function() {
}, 10000, 'Expected to find window.mochaResults set!'); return app.client.waitUntil(function() {
}).then(function() { return app.client.execute(getMochaResults).then(function(data) {
return app.client.execute(getMochaResults); return Boolean(data.value);
}).then(function(data) { });
var results = data.value; }, 10000, 'Expected to find window.mochaResults set!');
if (results.failures > 0) { }).then(function() {
console.error(results.reports); return app.client.execute(getMochaResults);
failure = function() { }).then(function(data) {
grunt.fail.fatal('Found ' + results.failures + ' failing unit tests.'); var results = data.value;
}; if (results.failures > 0) {
return app.client.log('browser'); console.error(results.reports);
} else {
grunt.log.ok(results.passes + ' tests passed.');
}
}).then(function(logs) {
if (logs) {
console.error();
console.error('Because tests failed, printing browser logs:');
console.error(logs);
}
}).catch(function (error) {
failure = function() { failure = function() {
grunt.fail.fatal('Something went wrong: ' + error.message + ' ' + error.stack); grunt.fail.fatal('Found ' + results.failures + ' failing unit tests.');
}; };
}).then(function () { return app.client.log('browser');
// We need to use the failure variable and this early stop to clean up before } else {
// shutting down. Grunt's fail methods are the only way to set the return value, grunt.log.ok(results.passes + ' tests passed.');
// but they shut the process down immediately! }
return app.stop(); }).then(function(logs) {
}).then(function() { if (logs) {
if (failure) { console.error();
failure(); console.error('Because tests failed, printing browser logs:');
} console.error(logs);
done(); }
}).catch(function (error) { }).catch(function (error) {
console.error('Second-level error:', error.message, error.stack); failure = function() {
if (failure) { grunt.fail.fatal('Something went wrong: ' + error.message + ' ' + error.stack);
failure(); };
} }).then(function () {
done(); // We need to use the failure variable and this early stop to clean up before
}); // shutting down. Grunt's fail methods are the only way to set the return value,
// but they shut the process down immediately!
return app.stop();
}).then(function() {
if (failure) {
failure();
}
cb();
}).catch(function (error) {
console.error('Second-level error:', error.message, error.stack);
if (failure) {
failure();
}
cb();
});
}
grunt.registerTask('unit-tests', 'Run unit tests w/Electron', function() {
var environment = grunt.option('env') || 'test';
var done = this.async();
runTests(environment, done);
});
grunt.registerTask('lib-unit-tests', 'Run libtextsecure unit tests w/Electron', function() {
var environment = grunt.option('env') || 'test-lib';
var done = this.async();
runTests(environment, done);
}); });
grunt.registerMultiTask('test-release', 'Test packaged releases', function() { grunt.registerMultiTask('test-release', 'Test packaged releases', function() {
@ -473,7 +484,7 @@ module.exports = function(grunt) {
grunt.registerTask('tx', ['exec:tx-pull', 'locale-patch']); grunt.registerTask('tx', ['exec:tx-pull', 'locale-patch']);
grunt.registerTask('dev', ['default', 'watch']); grunt.registerTask('dev', ['default', 'watch']);
grunt.registerTask('test', ['jshint', 'jscs', 'unit-tests']); grunt.registerTask('test', ['jshint', 'jscs', 'unit-tests', 'lib-unit-tests']);
grunt.registerTask('copy_dist', ['gitinfo', 'copy:res', 'copy:src']); grunt.registerTask('copy_dist', ['gitinfo', 'copy:res', 'copy:src']);
grunt.registerTask('date', ['gitinfo', 'getExpireTime']); grunt.registerTask('date', ['gitinfo', 'getExpireTime']);
grunt.registerTask('prep-release', ['gitinfo', 'clean-release', 'fetch-release']); grunt.registerTask('prep-release', ['gitinfo', 'clean-release', 'fetch-release']);

5
config/test-lib.json Normal file
View file

@ -0,0 +1,5 @@
{
"storageProfile": "test",
"disableAutoUpdate": true,
"openDevTools": false
}

View file

@ -79,11 +79,11 @@
function start() { function start() {
var currentVersion = window.config.version; var currentVersion = window.config.version;
var lastVersion = storage.get('version'); var lastVersion = storage.get('version');
var newVersion = !lastVersion || currentVersion !== lastVersion;
storage.put('version', currentVersion); storage.put('version', currentVersion);
if (!lastVersion || currentVersion !== lastVersion) { if (newVersion) {
console.log('New version detected:', currentVersion); console.log('New version detected:', currentVersion);
getAccountManager().rotateSignedPreKey();
} }
window.dispatchEvent(new Event('storage_ready')); window.dispatchEvent(new Event('storage_ready'));
@ -91,7 +91,7 @@
console.log('listening for registration events'); console.log('listening for registration events');
Whisper.events.on('registration_done', function() { Whisper.events.on('registration_done', function() {
console.log('handling registration event'); console.log('handling registration event');
Whisper.RotateSignedPreKeyListener.init(Whisper.events); Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion);
connect(true); connect(true);
}); });
@ -104,7 +104,7 @@
console.log('Import was interrupted, showing import error screen'); console.log('Import was interrupted, showing import error screen');
appView.openImporter(); appView.openImporter();
} else if (Whisper.Registration.everDone()) { } else if (Whisper.Registration.everDone()) {
Whisper.RotateSignedPreKeyListener.init(Whisper.events); Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion);
connect(); connect();
appView.openInbox({ appView.openInbox({
initialLoadComplete: initialLoadComplete initialLoadComplete: initialLoadComplete

View file

@ -37988,26 +37988,35 @@ var TextSecureServer = (function() {
rotateSignedPreKey: function() { rotateSignedPreKey: function() {
return this.queueTask(function() { return this.queueTask(function() {
var signedKeyId = textsecure.storage.get('signedKeyId', 1); var signedKeyId = textsecure.storage.get('signedKeyId', 1);
if (typeof signedKeyId != 'number') { if (typeof signedKeyId != 'number') {
throw new Error('Invalid signedKeyId'); throw new Error('Invalid signedKeyId');
} }
var store = textsecure.storage.protocol; var store = textsecure.storage.protocol;
var server = this.server; var server = this.server;
var cleanSignedPreKeys = this.cleanSignedPreKeys; var cleanSignedPreKeys = this.cleanSignedPreKeys;
return store.getIdentityKeyPair().then(function(identityKey) { return store.getIdentityKeyPair().then(function(identityKey) {
return libsignal.KeyHelper.generateSignedPreKey(identityKey, signedKeyId); return libsignal.KeyHelper.generateSignedPreKey(identityKey, signedKeyId);
}).then(function(res) { }).then(function(res) {
return server.setSignedPreKey({ console.log('Saving new signed prekey', res.keyId);
keyId : res.keyId, return Promise.all([
publicKey : res.keyPair.pubKey, textsecure.storage.put('signedKeyId', signedKeyId + 1),
signature : res.signature store.storeSignedPreKey(res.keyId, res.keyPair),
server.setSignedPreKey({
keyId : res.keyId,
publicKey : res.keyPair.pubKey,
signature : res.signature
}),
]).then(function() {
var confirmed = true;
console.log('Confirming new signed prekey', res.keyId);
return Promise.all([
textsecure.storage.remove('signedKeyRotationRejected'),
store.storeSignedPreKey(res.keyId, res.keyPair, confirmed),
]);
}).then(function() { }).then(function() {
textsecure.storage.put('signedKeyId', signedKeyId + 1); return cleanSignedPreKeys();
textsecure.storage.remove('signedKeyRotationRejected');
return store.storeSignedPreKey(res.keyId, res.keyPair).then(function() {
return cleanSignedPreKeys();
});
}); });
}).catch(function(e) { }).catch(function(e) {
console.log( console.log(
@ -38030,35 +38039,72 @@ var TextSecureServer = (function() {
return this.pending = this.pending.then(taskWithTimeout, taskWithTimeout); return this.pending = this.pending.then(taskWithTimeout, taskWithTimeout);
}, },
cleanSignedPreKeys: function() { cleanSignedPreKeys: function() {
var nextSignedKeyId = textsecure.storage.get('signedKeyId'); var MINIMUM_KEYS = 3;
if (typeof nextSignedKeyId != 'number') {
return Promise.resolve();
}
var activeSignedPreKeyId = nextSignedKeyId - 1;
var store = textsecure.storage.protocol; var store = textsecure.storage.protocol;
return store.loadSignedPreKeys().then(function(allRecords) { return store.loadSignedPreKeys().then(function(allKeys) {
var oldRecords = allRecords.filter(function(record) { allKeys.sort(function(a, b) {
return record.keyId !== activeSignedPreKeyId;
});
oldRecords.sort(function(a, b) {
return (a.created_at || 0) - (b.created_at || 0); return (a.created_at || 0) - (b.created_at || 0);
}); });
allKeys.reverse(); // we want the most recent first
var confirmed = allKeys.filter(function(key) {
return key.confirmed;
});
var unconfirmed = allKeys.filter(function(key) {
return !key.confirmed;
});
console.log("Active signed prekey: " + activeSignedPreKeyId); var recent = allKeys[0] ? allKeys[0].keyId : 'none';
console.log("Old signed prekey record count: " + oldRecords.length); var recentConfirmed = confirmed[0] ? confirmed[0].keyId : 'none';
console.log('Most recent signed key: ' + recent);
console.log('Most recent confirmed signed key: ' + recentConfirmed);
console.log(
'Total signed key count:',
allKeys.length,
'-',
confirmed.length,
'confirmed'
);
oldRecords.forEach(function(oldRecord) { var confirmedCount = confirmed.length;
if ( oldRecord.keyId >= activeSignedPreKeyId - 3 ) {
// keep at least the last 3 signed keys // Keep MINIMUM_KEYS confirmed keys, then drop if older than a week
confirmed = confirmed.forEach(function(key, index) {
if (index < MINIMUM_KEYS) {
return; return;
} }
var created_at = oldRecord.created_at || 0; var created_at = key.created_at || 0;
var archiveDuration = Date.now() - created_at; var age = Date.now() - created_at;
if (archiveDuration > ARCHIVE_AGE) { if (age > ARCHIVE_AGE) {
console.log("Removing signed prekey record:", console.log(
oldRecord.keyId, "with timestamp:", created_at); 'Removing confirmed signed prekey:',
store.removeSignedPreKey(oldRecord.keyId); key.keyId,
'with timestamp:',
created_at
);
store.removeSignedPreKey(key.keyId);
confirmedCount--;
}
});
var stillNeeded = MINIMUM_KEYS - confirmedCount;
// If we still don't have enough total keys, we keep as many unconfirmed
// keys as necessary. If not necessary, and over a week old, we drop.
unconfirmed.forEach(function(key, index) {
if (index < stillNeeded) {
return;
}
var created_at = key.created_at || 0;
var age = Date.now() - created_at;
if (age > ARCHIVE_AGE) {
console.log(
'Removing unconfirmed signed prekey:',
key.keyId,
'with timestamp:',
created_at
);
store.removeSignedPreKey(key.keyId);
} }
}); });
}); });

View file

@ -58,20 +58,19 @@
var initComplete; var initComplete;
Whisper.RotateSignedPreKeyListener = { Whisper.RotateSignedPreKeyListener = {
init: function(events) { init: function(events, newVersion) {
if (initComplete) { if (initComplete) {
console.log('Rotate signed prekey listener: Already initialized'); console.log('Rotate signed prekey listener: Already initialized');
return; return;
} }
initComplete = true; initComplete = true;
if (Whisper.Registration.isDone()) { if (newVersion) {
runWhenOnline();
} else {
setTimeoutForNextRun(); setTimeoutForNextRun();
} }
events.on('registration_done', function() {
scheduleNextRotation();
setTimeoutForNextRun();
});
events.on('timetravel', function() { events.on('timetravel', function() {
if (Whisper.Registration.isDone()) { if (Whisper.Registration.isDone()) {
setTimeoutForNextRun(); setTimeoutForNextRun();

View file

@ -240,7 +240,8 @@
pubKey : prekey.get('publicKey'), pubKey : prekey.get('publicKey'),
privKey : prekey.get('privateKey'), privKey : prekey.get('privateKey'),
created_at : prekey.get('created_at'), created_at : prekey.get('created_at'),
keyId : prekey.get('id') keyId : prekey.get('id'),
confirmed : prekey.get('confirmed'),
}); });
}).fail(function() { }).fail(function() {
console.log('Failed to load signed prekey:', keyId); console.log('Failed to load signed prekey:', keyId);
@ -260,18 +261,20 @@
pubKey : prekey.get('publicKey'), pubKey : prekey.get('publicKey'),
privKey : prekey.get('privateKey'), privKey : prekey.get('privateKey'),
created_at : prekey.get('created_at'), created_at : prekey.get('created_at'),
keyId : prekey.get('id') keyId : prekey.get('id'),
confirmed : prekey.get('confirmed'),
}; };
})); }));
}); });
}); });
}, },
storeSignedPreKey: function(keyId, keyPair) { storeSignedPreKey: function(keyId, keyPair, confirmed) {
var prekey = new SignedPreKey({ var prekey = new SignedPreKey({
id : keyId, id : keyId,
publicKey : keyPair.pubKey, publicKey : keyPair.pubKey,
privateKey : keyPair.privKey, privateKey : keyPair.privKey,
created_at : Date.now() created_at : Date.now(),
confirmed : Boolean(confirmed),
}); });
return new Promise(function(resolve) { return new Promise(function(resolve) {
prekey.save().always(function() { prekey.save().always(function() {

View file

@ -118,26 +118,35 @@
rotateSignedPreKey: function() { rotateSignedPreKey: function() {
return this.queueTask(function() { return this.queueTask(function() {
var signedKeyId = textsecure.storage.get('signedKeyId', 1); var signedKeyId = textsecure.storage.get('signedKeyId', 1);
if (typeof signedKeyId != 'number') { if (typeof signedKeyId != 'number') {
throw new Error('Invalid signedKeyId'); throw new Error('Invalid signedKeyId');
} }
var store = textsecure.storage.protocol; var store = textsecure.storage.protocol;
var server = this.server; var server = this.server;
var cleanSignedPreKeys = this.cleanSignedPreKeys; var cleanSignedPreKeys = this.cleanSignedPreKeys;
return store.getIdentityKeyPair().then(function(identityKey) { return store.getIdentityKeyPair().then(function(identityKey) {
return libsignal.KeyHelper.generateSignedPreKey(identityKey, signedKeyId); return libsignal.KeyHelper.generateSignedPreKey(identityKey, signedKeyId);
}).then(function(res) { }).then(function(res) {
return server.setSignedPreKey({ console.log('Saving new signed prekey', res.keyId);
keyId : res.keyId, return Promise.all([
publicKey : res.keyPair.pubKey, textsecure.storage.put('signedKeyId', signedKeyId + 1),
signature : res.signature store.storeSignedPreKey(res.keyId, res.keyPair),
server.setSignedPreKey({
keyId : res.keyId,
publicKey : res.keyPair.pubKey,
signature : res.signature
}),
]).then(function() {
var confirmed = true;
console.log('Confirming new signed prekey', res.keyId);
return Promise.all([
textsecure.storage.remove('signedKeyRotationRejected'),
store.storeSignedPreKey(res.keyId, res.keyPair, confirmed),
]);
}).then(function() { }).then(function() {
textsecure.storage.put('signedKeyId', signedKeyId + 1); return cleanSignedPreKeys();
textsecure.storage.remove('signedKeyRotationRejected');
return store.storeSignedPreKey(res.keyId, res.keyPair).then(function() {
return cleanSignedPreKeys();
});
}); });
}).catch(function(e) { }).catch(function(e) {
console.log( console.log(
@ -160,35 +169,72 @@
return this.pending = this.pending.then(taskWithTimeout, taskWithTimeout); return this.pending = this.pending.then(taskWithTimeout, taskWithTimeout);
}, },
cleanSignedPreKeys: function() { cleanSignedPreKeys: function() {
var nextSignedKeyId = textsecure.storage.get('signedKeyId'); var MINIMUM_KEYS = 3;
if (typeof nextSignedKeyId != 'number') {
return Promise.resolve();
}
var activeSignedPreKeyId = nextSignedKeyId - 1;
var store = textsecure.storage.protocol; var store = textsecure.storage.protocol;
return store.loadSignedPreKeys().then(function(allRecords) { return store.loadSignedPreKeys().then(function(allKeys) {
var oldRecords = allRecords.filter(function(record) { allKeys.sort(function(a, b) {
return record.keyId !== activeSignedPreKeyId;
});
oldRecords.sort(function(a, b) {
return (a.created_at || 0) - (b.created_at || 0); return (a.created_at || 0) - (b.created_at || 0);
}); });
allKeys.reverse(); // we want the most recent first
var confirmed = allKeys.filter(function(key) {
return key.confirmed;
});
var unconfirmed = allKeys.filter(function(key) {
return !key.confirmed;
});
console.log("Active signed prekey: " + activeSignedPreKeyId); var recent = allKeys[0] ? allKeys[0].keyId : 'none';
console.log("Old signed prekey record count: " + oldRecords.length); var recentConfirmed = confirmed[0] ? confirmed[0].keyId : 'none';
console.log('Most recent signed key: ' + recent);
console.log('Most recent confirmed signed key: ' + recentConfirmed);
console.log(
'Total signed key count:',
allKeys.length,
'-',
confirmed.length,
'confirmed'
);
oldRecords.forEach(function(oldRecord) { var confirmedCount = confirmed.length;
if ( oldRecord.keyId >= activeSignedPreKeyId - 3 ) {
// keep at least the last 3 signed keys // Keep MINIMUM_KEYS confirmed keys, then drop if older than a week
confirmed = confirmed.forEach(function(key, index) {
if (index < MINIMUM_KEYS) {
return; return;
} }
var created_at = oldRecord.created_at || 0; var created_at = key.created_at || 0;
var archiveDuration = Date.now() - created_at; var age = Date.now() - created_at;
if (archiveDuration > ARCHIVE_AGE) { if (age > ARCHIVE_AGE) {
console.log("Removing signed prekey record:", console.log(
oldRecord.keyId, "with timestamp:", created_at); 'Removing confirmed signed prekey:',
store.removeSignedPreKey(oldRecord.keyId); key.keyId,
'with timestamp:',
created_at
);
store.removeSignedPreKey(key.keyId);
confirmedCount--;
}
});
var stillNeeded = MINIMUM_KEYS - confirmedCount;
// If we still don't have enough total keys, we keep as many unconfirmed
// keys as necessary. If not necessary, and over a week old, we drop.
unconfirmed.forEach(function(key, index) {
if (index < stillNeeded) {
return;
}
var created_at = key.created_at || 0;
var age = Date.now() - created_at;
if (age > ARCHIVE_AGE) {
console.log(
'Removing unconfirmed signed prekey:',
key.keyId,
'with timestamp:',
created_at
);
store.removeSignedPreKey(key.keyId);
} }
}); });
}); });

View file

@ -1,5 +1,6 @@
mocha.setup("bdd"); mocha.setup("bdd");
window.assert = chai.assert; window.assert = chai.assert;
window.PROTO_ROOT = '../../protos';
(function() { (function() {
var OriginalReporter = mocha._reporter; var OriginalReporter = mocha._reporter;
@ -52,3 +53,5 @@ function hexToArrayBuffer(str) {
array[i] = parseInt(str.substr(i*2, 2), 16); array[i] = parseInt(str.substr(i*2, 2), 16);
return ret; return ret;
}; };
window.MockSocket.prototype.addEventListener = function() {};

View file

@ -0,0 +1,156 @@
'use strict';
describe("AccountManager", function() {
let accountManager;
let originalServer;
before(function() {
originalServer = window.TextSecureServer;
window.TextSecureServer = function() {};
});
after(function() {
window.TextSecureServer = originalServer;
});
beforeEach(function() {
accountManager = new window.textsecure.AccountManager();
});
describe('#cleanSignedPreKeys', function() {
let originalProtocolStorage;
let signedPreKeys;
const DAY = 1000 * 60 * 60 * 24;
beforeEach(function() {
originalProtocolStorage = window.textsecure.storage.protocol;
window.textsecure.storage.protocol = {
loadSignedPreKeys: function() {
return Promise.resolve(signedPreKeys);
},
};
});
afterEach(function() {
window.textsecure.storage.protocol = originalProtocolStorage;
});
it('keeps three confirmed keys even if over a week old', function() {
const now = Date.now();
signedPreKeys = [{
keyId: 1,
created_at: now - DAY * 21,
confirmed: true,
}, {
keyId: 2,
created_at: now - DAY * 14,
confirmed: true,
}, {
keyId: 3,
created_at: now - DAY * 18,
confirmed: true,
}];
// should be no calls to store.removeSignedPreKey, would cause crash
return accountManager.cleanSignedPreKeys();
});
it('eliminates confirmed keys over a week old, if more than three', function() {
const now = Date.now();
signedPreKeys = [{
keyId: 1,
created_at: now - DAY * 21,
confirmed: true,
}, {
keyId: 2,
created_at: now - DAY * 14,
confirmed: true,
}, {
keyId: 3,
created_at: now - DAY * 4,
confirmed: true,
}, {
keyId: 4,
created_at: now - DAY * 18,
confirmed: true,
}, {
keyId: 5,
created_at: now - DAY,
confirmed: true,
}];
let count = 0;
window.textsecure.storage.protocol.removeSignedPreKey = function(keyId) {
if (keyId !== 1 && keyId !== 4) {
throw new Error('Wrong keys were eliminated! ' + keyId);
}
count++;
};
return accountManager.cleanSignedPreKeys().then(function() {
assert.strictEqual(count, 2);
});
});
it('keeps at least three unconfirmed keys if no confirmed', function() {
const now = Date.now();
signedPreKeys = [{
keyId: 1,
created_at: now - DAY * 14,
}, {
keyId: 2,
created_at: now - DAY * 21,
}, {
keyId: 3,
created_at: now - DAY * 18,
}, {
keyId: 4,
created_at: now - DAY
}];
let count = 0;
window.textsecure.storage.protocol.removeSignedPreKey = function(keyId) {
if (keyId !== 2) {
throw new Error('Wrong keys were eliminated! ' + keyId);
}
count++;
};
return accountManager.cleanSignedPreKeys().then(function() {
assert.strictEqual(count, 1);
});
});
it('if some confirmed keys, keeps unconfirmed to addd up to three total', function() {
const now = Date.now();
signedPreKeys = [{
keyId: 1,
created_at: now - DAY * 21,
confirmed: true,
}, {
keyId: 2,
created_at: now - DAY * 14,
confirmed: true,
}, {
keyId: 3,
created_at: now - DAY * 12,
}, {
keyId: 4,
created_at: now - DAY * 8,
}];
let count = 0;
window.textsecure.storage.protocol.removeSignedPreKey = function(keyId) {
if (keyId !== 3) {
throw new Error('Wrong keys were eliminated! ' + keyId);
}
count++;
};
return accountManager.cleanSignedPreKeys().then(function() {
assert.strictEqual(count, 1);
});
});
});
});

View file

@ -1,6 +1,7 @@
<html> <html>
<head> <head>
<meta charset='utf-8'>
<title>libTextSecure test runner</title> <title>libTextSecure test runner</title>
<link rel="stylesheet" href="../../components/mocha/mocha.css" /> <link rel="stylesheet" href="../../components/mocha/mocha.css" />
</head> </head>
@ -12,7 +13,6 @@
</div> </div>
<script type="text/javascript" src="test.js"></script> <script type="text/javascript" src="test.js"></script>
<script type="text/javascript" src="blanket_mocha.js"></script>
<script type="text/javascript" src="in_memory_signal_protocol_store.js"></script> <script type="text/javascript" src="in_memory_signal_protocol_store.js"></script>
<script type="text/javascript" src="../components.js"></script> <script type="text/javascript" src="../components.js"></script>
@ -23,12 +23,12 @@
<script type="text/javascript" src="../storage.js" data-cover></script> <script type="text/javascript" src="../storage.js" data-cover></script>
<script type="text/javascript" src="../protocol_wrapper.js" data-cover></script> <script type="text/javascript" src="../protocol_wrapper.js" data-cover></script>
<script type="text/javascript" src="../event_target.js" data-cover></script>
<script type="text/javascript" src="../websocket-resources.js" data-cover></script> <script type="text/javascript" src="../websocket-resources.js" data-cover></script>
<script type="text/javascript" src="../helpers.js" data-cover></script> <script type="text/javascript" src="../helpers.js" data-cover></script>
<script type="text/javascript" src="../stringview.js" data-cover></script> <script type="text/javascript" src="../stringview.js" data-cover></script>
<script type="text/javascript" src="../api.js"></script> <script type="text/javascript" src="../api.js"></script>
<script type="text/javascript" src="../sendmessage.js" data-cover></script> <script type="text/javascript" src="../sendmessage.js" data-cover></script>
<script type="text/javascript" src="../event_target.js" data-cover></script>
<script type="text/javascript" src="../account_manager.js" data-cover></script> <script type="text/javascript" src="../account_manager.js" data-cover></script>
<script type="text/javascript" src="../contacts_parser.js" data-cover></script> <script type="text/javascript" src="../contacts_parser.js" data-cover></script>
<script type="text/javascript" src="../task_with_timeout.js" data-cover></script> <script type="text/javascript" src="../task_with_timeout.js" data-cover></script>
@ -42,5 +42,14 @@
<script type="text/javascript" src="generate_keys_test.js"></script> <script type="text/javascript" src="generate_keys_test.js"></script>
<script type="text/javascript" src="websocket-resources_test.js"></script> <script type="text/javascript" src="websocket-resources_test.js"></script>
<script type="text/javascript" src="task_with_timeout_test.js"></script> <script type="text/javascript" src="task_with_timeout_test.js"></script>
<script type="text/javascript" src="account_manager_test.js"></script>
<!-- Comment out to turn off code coverage. Useful for getting real callstacks. -->
<script type="text/javascript" src="blanket_mocha.js"></script>
<!-- Uncomment to start tests without code coverage enabled -->
<!-- <script type="text/javascript">
mocha.run();
</script> -->
</body> </body>
</html> </html>

View file

@ -1 +0,0 @@
../../protos/

View file

@ -22054,6 +22054,7 @@ Library.prototype.test = function(obj, type) {
}); });
mocha.setup("bdd"); mocha.setup("bdd");
window.assert = chai.assert; window.assert = chai.assert;
window.PROTO_ROOT = '../../protos';
(function() { (function() {
var OriginalReporter = mocha._reporter; var OriginalReporter = mocha._reporter;
@ -22106,3 +22107,5 @@ function hexToArrayBuffer(str) {
array[i] = parseInt(str.substr(i*2, 2), 16); array[i] = parseInt(str.substr(i*2, 2), 16);
return ret; return ret;
}; };
window.MockSocket.prototype.addEventListener = function() {};

View file

@ -18,7 +18,8 @@
assert.strictEqual(message.response.status, 200); assert.strictEqual(message.response.status, 200);
assert.strictEqual(message.response.id.toString(), request_id); assert.strictEqual(message.response.id.toString(), request_id);
done(); done();
} },
addEventListener: function() {},
}; };
// actual test // actual test
@ -58,7 +59,8 @@
assert.strictEqual(message.request.path, '/some/path'); assert.strictEqual(message.request.path, '/some/path');
assertEqualArrayBuffers(message.request.body.toArrayBuffer(), new Uint8Array([1,2,3]).buffer); assertEqualArrayBuffers(message.request.body.toArrayBuffer(), new Uint8Array([1,2,3]).buffer);
request_id = message.request.id; request_id = message.request.id;
} },
addEventListener: function() {},
}; };
// actual test // actual test

View file

@ -207,6 +207,8 @@ function createWindow () {
if (config.environment === 'test') { if (config.environment === 'test') {
mainWindow.loadURL(prepareURL([__dirname, 'test', 'index.html'])); mainWindow.loadURL(prepareURL([__dirname, 'test', 'index.html']));
} else if (config.environment === 'test-lib') {
mainWindow.loadURL(prepareURL([__dirname, 'libtextsecure', 'test', 'index.html']));
} else { } else {
mainWindow.loadURL(prepareURL([__dirname, 'background.html'])); mainWindow.loadURL(prepareURL([__dirname, 'background.html']));
} }
@ -225,7 +227,9 @@ function createWindow () {
// Emitted when the window is about to be closed. // Emitted when the window is about to be closed.
mainWindow.on('close', function (e) { mainWindow.on('close', function (e) {
if (process.platform === 'darwin' && !windowState.shouldQuit() && config.environment !== 'test') { if (process.platform === 'darwin' && !windowState.shouldQuit()
&& config.environment !== 'test' && config.environment !== 'test-lib') {
e.preventDefault(); e.preventDefault();
mainWindow.hide(); mainWindow.hide();
} }
@ -356,7 +360,7 @@ app.on('before-quit', function() {
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar // On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin' || config.environment === 'test') { if (process.platform !== 'darwin' || config.environment === 'test' || config.environment === 'test-lib') {
app.quit() app.quit()
} }
}) })