Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,43 @@
/* global assert */
'use strict';
suite('apply-manifest', function() {
var exec = require('child_process').exec;
var ROOT = __dirname + '/../../';
var BIN = ROOT + '/bin/apply-manifest';
test('manifest-blacklist.json', function(done) {
var argv = [
BIN,
__dirname + '/fixtures/manifest-blacklist.json',
'test/bin/fixtures/manifest-test-skip.js',
'test/bin/fixtures/manifest-test-use.js'
];
exec(argv.join(' '), { cwd: ROOT }, function(err, stdout) {
if (err) return done(err);
assert.equal(
stdout.trim(),
__dirname + '/fixtures/manifest-test-use.js'
);
done();
});
});
test('manifest-whitelist.json', function(done) {
var argv = [
BIN,
__dirname + '/fixtures/manifest-whitelist.json',
'test/bin/fixtures/manifest-test-skip.js',
'test/bin/fixtures/manifest-test-use.js'
];
exec(argv.join(' '), { cwd: ROOT }, function(err, stdout) {
if (err) return done(err);
assert.equal(
stdout.trim(),
__dirname + '/fixtures/manifest-test-use.js'
);
done();
});
});
});

View file

@ -0,0 +1,18 @@
/* global spawnMarionette */
'use strict';
test('console.log --verbose', function(done) {
this.timeout('10s');
// Usual process spawning stuff the important bit here is this fixture will
// fail and never complete unless we wait for 100s or kill the process.
var proc = spawnMarionette([
'--verbose',
__dirname + '/fixtures/consolelog.js'
]);
proc.stdout.on('data', function(buffer) {
if (buffer.toString().indexOf('the fox say') !== -1) {
proc.once('exit', done.bind(null, null));
return;
}
});
});

View file

@ -0,0 +1,25 @@
/* global assert, spawnMarionette */
'use strict';
suite('Crash handling', function() {
this.timeout('20s');
var proc;
test('handle crash', function(done) {
// Usual process spawning stuff the important bit here is this fixture will
// fail and never complete unless we wait for 100s or kill the process.
proc = spawnMarionette([__dirname + '/fixtures/crash.js']);
var hasCrash;
proc.stderr.on('data', function(buffer) {
if (buffer.toString().indexOf('Crash detected ') !== -1) {
hasCrash = true;
}
});
proc.once('exit', function(code) {
assert(hasCrash, 'has crash message');
assert(code !== 0, 'must exit in error');
done();
});
});
});

View file

@ -0,0 +1,17 @@
'use strict';
// Test here is only to test SIGINT functionality.
marionette('stay blocked', function() {
var client = marionette.client();
test('block', function() {
// Ensure we don't die on any startup errors...
try {
client.executeScript(function() {});
} catch (e) {}
client.setScriptTimeout(1000000);
client.executeAsyncScript(function() {
});
});
});

View file

@ -0,0 +1,10 @@
'use strict';
function Builder() {}
Builder.prototype = {
build: function(callback) {
// magic process exit number to indicate that this file was loaded.
process.exit(66);
}
};
module.exports = Builder;

View file

@ -0,0 +1,3 @@
{
"desiredCapability": true
}

View file

@ -0,0 +1,18 @@
'use strict';
/**
* @fileoverview A marionette test that simply logs something in gecko.
*/
suite('console proxy', function() {
this.timeout('10s');
var client = marionette.client();
test('log in host', function(done) {
// logging is asynchronous so wait a bit longer then just the execution
// so we actually get some logs...
client.executeScript(function() {
console.log('What does the fox say');
});
setTimeout(done, 2000);
});
});

View file

@ -0,0 +1,22 @@
/* global Components, ctypes */
'use strict';
suite('crash', function() {
var client = marionette.client();
test('crash me', function() {
client.scope({ context: 'chrome' }).executeScript(function() {
const Cu = Components.utils;
Cu.import('resource://gre/modules/ctypes.jsm');
var dies = function() {
var zero = new ctypes.intptr_t(8);
var badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
badptr.contents;
};
dump('Et tu, Brute?');
dies();
});
});
});

View file

@ -0,0 +1,5 @@
{
"phone": "test on phone",
"tv": "test on tv",
"common": "test on both"
}

View file

@ -0,0 +1,32 @@
'use strict';
var Promise = require('promise');
var assert = require('assert');
exports.help = {
group: {
title: 'CUSTOM XFOO',
description: 'CUSTOM DESC'
},
arguments: {
'--code': {
type: 'int',
help: 'exit code'
}
}
};
var expectedHost = {
destroy: function() {
return Promise.resolve();
}
};
module.exports.createHost = function() {
return Promise.resolve(expectedHost);
};
module.exports.createSession = function(host, profile, opts) {
assert.equal(host, expectedHost);
process.exit(opts.code);
return Promise.resolve();
};

View file

@ -0,0 +1,5 @@
{
"blacklist": {
"test/bin/fixtures/manifest-test-skip.js": "Bug 12345 - whatever bug I want."
}
}

View file

@ -0,0 +1,4 @@
'use strict';
marionette('test', {}, function() {
process.exit(13);
});

View file

@ -0,0 +1,4 @@
'use strict';
marionette('test', {}, function() {
process.exit(13);
});

View file

@ -0,0 +1,4 @@
'use strict';
marionette('test', {}, function() {
process.exit(23);
});

View file

@ -0,0 +1,3 @@
{
"whitelist": ["test/bin/fixtures/manifest-test-use.js"]
}

View file

@ -0,0 +1,15 @@
marionette('device test', function() {
var client = marionette.client();
test('test on phone', { devices: ['phone'] }, function() {
1 + 1;
});
test('test on tv', { devices: ['tv'] }, function() {
1 + 1;
});
test('test on both', { devices: ['phone', 'tv'] }, function() {
1 + 1;
});
});

View file

@ -0,0 +1,8 @@
'use strict';
marionette('test', function() {
var client = marionette.client();
test('1+1', function() {
1 + 1;
});
});

View file

@ -0,0 +1,257 @@
/* global assert, spawnMarionette, spawnMocha */
'use strict';
suite('mocha integration', function() {
this.timeout('30s');
var fs = require('fs');
var fsPath = require('path');
function aggregateOutput(childProcess) {
var result = {
stderr: '',
stdout: ''
};
childProcess.stderr.on('data', function(out) {
result.stderr += out;
});
childProcess.stdout.on('data', function(out) {
result.stdout += out;
});
return result;
}
var MS_REGEXP = /(([0-9]+) ?ms)/;
var NEW_LINES = /(\n|(\s{2,}))/g;
function waitForProcess(child, done) {
var result = aggregateOutput(child);
child.on('exit', function(code) {
// there are very small newline/whitespace differences between
// mocha and our marionette reporter... these probably are not
// bugs but prevent us from verifying real content so they are stripped.
['stderr', 'stdout'].forEach(function(field) {
[MS_REGEXP, NEW_LINES].forEach(function(regex) {
result[field + 'Raw'] = result[field];
result[field] = result[field].replace(regex, '').trim();
});
});
// exit status is _really_ important
result.code = code;
done();
});
return result;
}
var tests = [
// this also tests picking up mocha.opts
['test', ['--reporter', 'spec']],
['test', [
'--reporter',
fsPath.resolve(
__dirname,
'/../../node_modules/mocha/lib/reporters/spec'
)
]],
['pending', ['--reporter', 'spec']],
['with-helper', [
'--require', fsPath.resolve(__dirname + '/../fixtures/helper.js'),
'--reporter', 'spec'
]
]
];
tests.forEach(function(pair) {
var file = pair[0];
var path = fsPath.resolve(__dirname + '/../fixtures/' + file);
var argv = [path].concat(pair[1]);
// run same test with same options on both mocha & our marionette proxy
// runner.
suite(file, function() {
var mochaOut;
var marionetteOut;
setup(function(done) {
var proc = spawnMocha(argv);
mochaOut = waitForProcess(proc, done);
});
setup(function(done) {
var proc = spawnMarionette(argv);
marionetteOut = waitForProcess(proc, done);
});
test('code', function() {
assert.equal(
JSON.stringify(mochaOut.code), JSON.stringify(marionetteOut.code)
);
});
test('stdout', function() {
assert.equal(mochaOut.stdout, marionetteOut.stdout);
});
test.skip('stderr', function() {
assert.equal(mochaOut.stderr, marionetteOut.stderr);
});
});
});
suite('--host', function() {
var result;
var argv = ['--host', __dirname + '/fixtures/host'];
var Host = require('./fixtures/host');
suite('when passing --help', function() {
setup(function(done) {
var proc = spawnMarionette(argv.concat(['--help']));
result = waitForProcess(proc, done);
});
test('custom help is shown', function() {
assert.ok(result.stdoutRaw.indexOf(Host.help.group.title) !== -1);
assert.ok(result.stdoutRaw.indexOf(Host.help.group.description) !== -1);
assert.ok(result.stdoutRaw.indexOf('--code') !== -1);
});
});
suite('run a test', function() {
setup(function(done) {
var proc = spawnMarionette(argv.concat([
__dirname + '/fixtures/marionettetest',
'--code',
'55'
]));
result = waitForProcess(proc, done);
});
test('exits with magic code', function() {
assert.equal(result.code, 55, JSON.stringify(result));
});
});
});
suite('--profile-builder', function() {
var result,
argv = [
'--profile-builder', __dirname + '/fixtures/builder',
__dirname + '/fixtures/marionettetest'
];
setup(function(done) {
var proc = spawnMarionette(argv);
result = waitForProcess(proc, done);
});
test('exits with magic code', function() {
assert.equal(result.code, 66, JSON.stringify(result));
});
});
suite('--marionette-capabilities', function() {
var result,
capabilitiesPath = __dirname + '/fixtures/capabilities.json',
expected = JSON.stringify({ capabilities: require(capabilitiesPath) }),
argv = [
'--marionette-capabilities', capabilitiesPath,
'--host-log', 'stdout',
__dirname + '/fixtures/marionettetest'
];
setup(function(done) {
var proc = spawnMarionette(argv);
result = waitForProcess(proc, done);
});
test('desired capabilities are set', function() {
assert.ok(result.stdoutRaw.indexOf(expected) !== -1);
});
});
suite('--device-type', function() {
var result,
devicesPath = __dirname + '/fixtures/devices.json',
tests = require(devicesPath),
argv = [
'--device-type', 'phone',
'--host-log', 'stdout',
__dirname + '/fixtures/marionettedevicetest'
];
setup(function(done) {
var proc = spawnMarionette(argv);
result = waitForProcess(proc, done);
});
test('desired devices is being tested', function() {
assert.ok(result.stdoutRaw.indexOf(tests.phone) !== -1);
assert.ok(result.stdoutRaw.indexOf(tests.common) !== -1);
assert.ok(result.stdoutRaw.indexOf(tests.tv) === -1);
});
});
suite('--host-log=stdout', function() {
var result,
argv = [
'--host-log', 'stdout',
__dirname + '/fixtures/marionettetest'
];
setup(function(done) {
var proc = spawnMarionette(argv);
result = waitForProcess(proc, done);
});
test('has marionette output', function() {
assert.ok(result.stdout.indexOf('newSession') !== -1);
assert.ok(result.stderr.indexOf('newSession') === -1);
});
});
suite('--host-log=stderr', function() {
var result,
argv = [
'--host-log', 'stderr',
__dirname + '/fixtures/marionettetest'
];
setup(function(done) {
var proc = spawnMarionette(argv);
result = waitForProcess(proc, done);
});
test('has marionette output', function() {
assert.ok(result.stderr.indexOf('newSession') !== -1);
assert.ok(result.stdout.indexOf('newSession') !== 0);
});
});
suite('--host-log=<file>', function() {
var file = __dirname + '/log.txt';
var result,
argv = [
'--host-log', file,
__dirname + '/fixtures/marionettetest'
];
setup(function(done) {
var proc = spawnMarionette(argv);
result = waitForProcess(proc, done);
});
teardown(function() {
fs.unlinkSync(file);
});
test('has marionette output', function() {
assert.ok(fs.existsSync(file));
assert.ok(fs.readFileSync(file, 'utf8').indexOf('newSession') !== -1);
});
});
});

View file

@ -0,0 +1,122 @@
/* global spawnMarionette */
'use strict';
suite('SIGINT', function() {
var exec = require('child_process').exec;
var assert = require('assert');
// Cleanup pid line (works for columns and value lines)
function splitPidLine(line) {
return line.trim().replace(/\s+/g, ' ').split(' ');
}
// Pretty much what you would expect out of a ps parser.
function ps(callback) {
// ppid is important here for the intent of this test but aside from the
// flags this code is actually could be general purpose.
exec('ps -o comm -o pid -o ppid', function(err, stdout) {
if (err) return callback(err);
var lines = stdout.replace('\r', '').split('\n');
var columns = splitPidLine(lines.shift());
var result = [];
lines.forEach(function(line) {
var lineParts = splitPidLine(line);
if (lineParts.length < columns.length) return;
var obj = {};
lineParts.forEach(function(part, idx) {
// lower case to better fit node conventions...
if (!columns[idx]) {
return;
}
obj[columns[idx].toLowerCase()] = part;
});
result.push(obj);
});
callback(null, result);
});
}
// Find all processes which share a starting process parent.
function recursivePpidSearch(pid, callback) {
ps(function(err, list) {
if (err) return callback(err);
var result = [];
// Yes this can stack overflow be very careful if this is used elsewhere.
function findPpid(searchPid) {
list.forEach(function(item) {
if (item.ppid == searchPid) {
findPpid(item.pid);
result.push(item);
}
});
}
findPpid(pid);
return callback(null, result);
});
}
function ensureKill(testFile, waitTime) {
suite('SIGKILL ' + testFile + ' after ' + waitTime + ' ms', function() {
var proc, processList, pid;
setup(function(done) {
// Usual process spawning stuff the important bit here is this fixture
// will fail and never complete unless we wait for 100s or kill the
// process.
proc = spawnMarionette([testFile]);
proc.stdout.pipe(process.stdout);
proc.stderr.pipe(process.stderr);
pid = proc.pid;
// Wait for all the created processes to finish cleanly
// - b2g process
// - child _mocha process
// - proxy reporter process
var processes = 3;
function search() {
recursivePpidSearch(process.pid, function(err, list) {
if (err) return done(err);
if (list.length < processes) return setTimeout(search);
processList = list;
setTimeout(done, waitTime);
});
}
search();
});
test('cleanly sigkill with no leftovers', function(done) {
proc.kill('SIGINT');
proc.once('exit', function(code) {
ps(function(err, currentProcesses) {
if (err) return done(err);
var remaining = processList.filter(function(startProc) {
for (var i = 0; i < currentProcesses.length; i++) {
if (currentProcesses[i].pid == startProc.pid) return true;
}
return false;
});
assert(
!remaining.length,
'Process still running: ' + JSON.stringify(remaining)
);
done();
});
});
});
});
}
// Basically instantly.
ensureKill(__dirname + '/../integration/multiclient_test', 10);
// Very early in the startup.
ensureKill(__dirname + '/../integration/multiclient_test', 100);
// Sessions are probably being created.
ensureKill(__dirname + '/../integration/multiclient_test', 500);
// Marionette is blocked.
ensureKill(__dirname + '/fixtures/blocked', 500);
});