mirror of https://github.com/espruino/BangleApps
Removed duplication in node.js apps with 2 new libraries, added an 'app test' program - not finished yet
parent
cf59aac032
commit
b89a368bd9
|
@ -14,7 +14,6 @@ for Noble.
|
||||||
var SETTINGS = {
|
var SETTINGS = {
|
||||||
pretokenise : true
|
pretokenise : true
|
||||||
};
|
};
|
||||||
var APPSDIR = __dirname+"/../apps/";
|
|
||||||
var noble;
|
var noble;
|
||||||
["@abandonware/noble", "noble"].forEach(module => {
|
["@abandonware/noble", "noble"].forEach(module => {
|
||||||
if (!noble) try {
|
if (!noble) try {
|
||||||
|
@ -37,36 +36,18 @@ function ERROR(msg) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//eval(require("fs").readFileSync(__dirname+"../core/js/utils.js"));
|
|
||||||
var AppInfo = require("../core/js/appinfo.js");
|
|
||||||
global.Const = {
|
|
||||||
/* Are we only putting a single app on a device? If so
|
|
||||||
apps should all be saved as .bootcde and we write info
|
|
||||||
about the current app into app.info */
|
|
||||||
SINGLE_APP_ONLY : false,
|
|
||||||
};
|
|
||||||
var deviceId = "BANGLEJS2";
|
var deviceId = "BANGLEJS2";
|
||||||
var apps = [];
|
|
||||||
var dirs = require("fs").readdirSync(APPSDIR, {withFileTypes: true});
|
|
||||||
dirs.forEach(dir => {
|
|
||||||
var appsFile;
|
|
||||||
if (dir.name.startsWith("_example") || !dir.isDirectory())
|
|
||||||
return;
|
|
||||||
try {
|
|
||||||
appsFile = require("fs").readFileSync(APPSDIR+dir.name+"/metadata.json").toString();
|
|
||||||
} catch (e) {
|
|
||||||
ERROR(dir.name+"/metadata.json does not exist");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
apps.push(JSON.parse(appsFile));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
var apploader = require("./lib/apploader.js");
|
||||||
var args = process.argv;
|
var args = process.argv;
|
||||||
|
|
||||||
var bangleParam = args.findIndex(arg => /-b\d/.test(arg));
|
var bangleParam = args.findIndex(arg => /-b\d/.test(arg));
|
||||||
if (bangleParam!==-1) {
|
if (bangleParam!==-1) {
|
||||||
deviceId = "BANGLEJS"+args.splice(bangleParam, 1)[0][2];
|
deviceId = "BANGLEJS"+args.splice(bangleParam, 1)[0][2];
|
||||||
}
|
}
|
||||||
|
apploader.init({
|
||||||
|
DEVICEID : deviceId
|
||||||
|
});
|
||||||
if (args.length==3 && args[2]=="list") cmdListApps();
|
if (args.length==3 && args[2]=="list") cmdListApps();
|
||||||
else if (args.length==3 && args[2]=="devices") cmdListDevices();
|
else if (args.length==3 && args[2]=="devices") cmdListDevices();
|
||||||
else if (args.length==4 && args[2]=="install") cmdInstallApp(args[3]);
|
else if (args.length==4 && args[2]=="install") cmdInstallApp(args[3]);
|
||||||
|
@ -90,7 +71,7 @@ process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cmdListApps() {
|
function cmdListApps() {
|
||||||
console.log(apps.map(a=>a.id).join("\n"));
|
console.log(apploader.apps.map(a=>a.id).join("\n"));
|
||||||
}
|
}
|
||||||
function cmdListDevices() {
|
function cmdListDevices() {
|
||||||
var foundDevices = [];
|
var foundDevices = [];
|
||||||
|
@ -113,19 +94,10 @@ function cmdListDevices() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cmdInstallApp(appId, deviceAddress) {
|
function cmdInstallApp(appId, deviceAddress) {
|
||||||
var app = apps.find(a=>a.id==appId);
|
var app = apploader.apps.find(a=>a.id==appId);
|
||||||
if (!app) ERROR(`App ${JSON.stringify(appId)} not found`);
|
if (!app) ERROR(`App ${JSON.stringify(appId)} not found`);
|
||||||
if (app.custom) ERROR(`App ${JSON.stringify(appId)} requires HTML customisation`);
|
if (app.custom) ERROR(`App ${JSON.stringify(appId)} requires HTML customisation`);
|
||||||
return AppInfo.getFiles(app, {
|
return apploader.getAppFilesString(app).then(command => {
|
||||||
fileGetter:function(url) {
|
|
||||||
console.log(__dirname+"/"+url);
|
|
||||||
return Promise.resolve(require("fs").readFileSync(__dirname+"/../"+url).toString("binary"));
|
|
||||||
},
|
|
||||||
settings : SETTINGS,
|
|
||||||
device : { id : deviceId }
|
|
||||||
}).then(files => {
|
|
||||||
//console.log(files);
|
|
||||||
var command = files.map(f=>f.cmd).join("\n")+"\n";
|
|
||||||
bangleSend(command, deviceAddress).then(() => process.exit(0));
|
bangleSend(command, deviceAddress).then(() => process.exit(0));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
#!/usr/bin/env nodejs
|
#!/usr/bin/env node
|
||||||
/*
|
/*
|
||||||
Mashes together a bunch of different apps to make
|
Mashes together a bunch of different apps to make
|
||||||
a single firmware JS file which can be uploaded.
|
a single firmware JS file which can be uploaded.
|
||||||
*/
|
*/
|
||||||
var SETTINGS = {
|
|
||||||
pretokenise : true
|
|
||||||
};
|
|
||||||
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var ROOTDIR = path.join(__dirname, '..');
|
var ROOTDIR = path.join(__dirname, '..');
|
||||||
var APPDIR = ROOTDIR+'/apps';
|
|
||||||
var OUTFILE = ROOTDIR+'/firmware.js';
|
var OUTFILE = ROOTDIR+'/firmware.js';
|
||||||
var DEVICE = "BANGLEJS";
|
var DEVICEID = "BANGLEJS";
|
||||||
var APPS = [ // IDs of apps to install
|
var APPS = [ // IDs of apps to install
|
||||||
"boot","launch","mclock","setting",
|
"boot","launch","mclock","setting",
|
||||||
"about","alarm","widbat","widbt","welcome"
|
"about","alarm","widbat","widbt","welcome"
|
||||||
|
@ -19,53 +14,17 @@ var APPS = [ // IDs of apps to install
|
||||||
var MINIFY = true;
|
var MINIFY = true;
|
||||||
|
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
global.Const = {
|
var apploader = require("./lib/apploader.js");
|
||||||
/* Are we only putting a single app on a device? If so
|
apploader.init({
|
||||||
apps should all be saved as .bootcde and we write info
|
DEVICEID : DEVICEID
|
||||||
about the current app into app.info */
|
});
|
||||||
SINGLE_APP_ONLY : false,
|
|
||||||
};
|
|
||||||
|
|
||||||
var AppInfo = require(ROOTDIR+"/core/js/appinfo.js");
|
|
||||||
var appfiles = [];
|
var appfiles = [];
|
||||||
|
|
||||||
function fileGetter(url) {
|
|
||||||
console.log("Loading "+url)
|
|
||||||
if (MINIFY) {
|
|
||||||
/*if (url.endsWith(".js")) {
|
|
||||||
var f = url.slice(0,-3);
|
|
||||||
console.log("MINIFYING "+f);
|
|
||||||
const execSync = require('child_process').execSync;
|
|
||||||
// --config PRETOKENISE=true
|
|
||||||
// --minify
|
|
||||||
code = execSync(`espruino --config SET_TIME_ON_WRITE=false --minify --board BANGLEJS ${f}.js -o ${f}.min.js`);
|
|
||||||
console.log(code.toString());
|
|
||||||
url = f+".min.js";
|
|
||||||
}*/
|
|
||||||
if (url.endsWith(".json")) {
|
|
||||||
var f = url.slice(0,-5);
|
|
||||||
console.log("MINIFYING JSON "+f);
|
|
||||||
var j = eval("("+fs.readFileSync(url).toString("binary")+")");
|
|
||||||
var code = JSON.stringify(j);
|
|
||||||
//console.log(code);
|
|
||||||
url = f+".min.json";
|
|
||||||
fs.writeFileSync(url, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Promise.resolve(fs.readFileSync(url).toString("binary"));
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all(APPS.map(appid => {
|
Promise.all(APPS.map(appid => {
|
||||||
try {
|
var app = apploader.apps.find(a => a.id==appid);
|
||||||
var app = JSON.parse(fs.readFileSync(APPDIR + "/" + appid + "/metadata.json").toString());
|
if (!app) throw new Error(`App ${appid} not found`);
|
||||||
} catch (e) {
|
return apploader.getAppFiles(app).then(files => {
|
||||||
throw new Error(`App ${appid} not found`);
|
|
||||||
}
|
|
||||||
return AppInfo.getFiles(app, {
|
|
||||||
fileGetter : fileGetter,
|
|
||||||
settings : SETTINGS,
|
|
||||||
device : { id : DEVICE }
|
|
||||||
}).then(files => {
|
|
||||||
appfiles = appfiles.concat(files);
|
appfiles = appfiles.concat(files);
|
||||||
});
|
});
|
||||||
})).then(() => {
|
})).then(() => {
|
||||||
|
|
|
@ -7,25 +7,20 @@ to populate Storage initially.
|
||||||
Bangle.js 1 doesn't really have anough flash space for this,
|
Bangle.js 1 doesn't really have anough flash space for this,
|
||||||
but we have enough on v2.
|
but we have enough on v2.
|
||||||
*/
|
*/
|
||||||
var SETTINGS = {
|
var DEVICEID = process.argv[2];
|
||||||
pretokenise : true
|
|
||||||
};
|
|
||||||
|
|
||||||
var DEVICE = process.argv[2];
|
|
||||||
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
var fs = require("fs");
|
||||||
var ROOTDIR = path.join(__dirname, '..');
|
var ROOTDIR = path.join(__dirname, '..');
|
||||||
var APPDIR = ROOTDIR+'/apps';
|
|
||||||
var MINIFY = true;
|
|
||||||
var OUTFILE, APPS;
|
var OUTFILE, APPS;
|
||||||
|
|
||||||
if (DEVICE=="BANGLEJS") {
|
if (DEVICEID=="BANGLEJS") {
|
||||||
var OUTFILE = path.join(ROOTDIR, '../Espruino/libs/banglejs/banglejs1_storage_default.c');
|
var OUTFILE = path.join(ROOTDIR, '../Espruino/libs/banglejs/banglejs1_storage_default.c');
|
||||||
var APPS = [ // IDs of apps to install
|
var APPS = [ // IDs of apps to install
|
||||||
"boot","launch","mclock","setting",
|
"boot","launch","mclock","setting",
|
||||||
"about","alarm","sched","widbat","widbt","welcome"
|
"about","alarm","sched","widbat","widbt","welcome"
|
||||||
];
|
];
|
||||||
} else if (DEVICE=="BANGLEJS2") {
|
} else if (DEVICEID=="BANGLEJS2") {
|
||||||
var OUTFILE = path.join(ROOTDIR, '../Espruino/libs/banglejs/banglejs2_storage_default.c');
|
var OUTFILE = path.join(ROOTDIR, '../Espruino/libs/banglejs/banglejs2_storage_default.c');
|
||||||
var APPS = [ // IDs of apps to install
|
var APPS = [ // IDs of apps to install
|
||||||
"boot","launch","antonclk","setting",
|
"boot","launch","antonclk","setting",
|
||||||
|
@ -37,16 +32,12 @@ if (DEVICE=="BANGLEJS") {
|
||||||
console.log(" bin/firmwaremaker_c.js BANGLEJS2");
|
console.log(" bin/firmwaremaker_c.js BANGLEJS2");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log("Device = ",DEVICE);
|
console.log("Device = ",DEVICEID);
|
||||||
|
|
||||||
|
var apploader = require("./lib/apploader.js");
|
||||||
var fs = require("fs");
|
apploader.init({
|
||||||
global.Const = {
|
DEVICEID : DEVICEID
|
||||||
/* Are we only putting a single app on a device? If so
|
});
|
||||||
apps should all be saved as .bootcde and we write info
|
|
||||||
about the current app into app.info */
|
|
||||||
SINGLE_APP_ONLY : false,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function atob(input) {
|
function atob(input) {
|
||||||
|
@ -84,31 +75,8 @@ function atob(input) {
|
||||||
return new Uint8Array(output);
|
return new Uint8Array(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
var AppInfo = require(ROOTDIR+"/core/js/appinfo.js");
|
|
||||||
var appfiles = [];
|
var appfiles = [];
|
||||||
|
|
||||||
function fileGetter(url) {
|
|
||||||
console.log("Loading "+url)
|
|
||||||
if (MINIFY) {
|
|
||||||
if (url.endsWith(".json")) {
|
|
||||||
var f = url.slice(0,-5);
|
|
||||||
console.log("MINIFYING JSON "+f);
|
|
||||||
var j = eval("("+fs.readFileSync(url).toString("binary")+")");
|
|
||||||
var code = JSON.stringify(j);
|
|
||||||
//console.log(code);
|
|
||||||
url = f+".min.json";
|
|
||||||
fs.writeFileSync(url, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var blob = fs.readFileSync(url);
|
|
||||||
var data;
|
|
||||||
if (url.endsWith(".js") || url.endsWith(".json"))
|
|
||||||
data = blob.toString(); // allow JS/etc to be written in UTF-8
|
|
||||||
else
|
|
||||||
data = blob.toString("binary")
|
|
||||||
return Promise.resolve(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If file should be evaluated, try and do it...
|
// If file should be evaluated, try and do it...
|
||||||
function evaluateFile(file) {
|
function evaluateFile(file) {
|
||||||
var hsStart = 'require("heatshrink").decompress(atob("';
|
var hsStart = 'require("heatshrink").decompress(atob("';
|
||||||
|
@ -132,16 +100,9 @@ function evaluateFile(file) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(APPS.map(appid => {
|
Promise.all(APPS.map(appid => {
|
||||||
try {
|
var app = apploader.apps.find(a => a.id==appid);
|
||||||
var app = JSON.parse(fs.readFileSync(APPDIR + "/" + appid + "/metadata.json").toString());
|
if (!app) throw new Error(`App ${appid} not found`);
|
||||||
} catch (e) {
|
return apploader.getAppFiles(app).then(files => {
|
||||||
throw new Error(`App ${appid} not found`);
|
|
||||||
}
|
|
||||||
return AppInfo.getFiles(app, {
|
|
||||||
fileGetter : fileGetter,
|
|
||||||
settings : SETTINGS,
|
|
||||||
device : { id : DEVICE }
|
|
||||||
}).then(files => {
|
|
||||||
appfiles = appfiles.concat(files);
|
appfiles = appfiles.concat(files);
|
||||||
});
|
});
|
||||||
})).then(() => {
|
})).then(() => {
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
/* Node.js library with utilities to handle using the app loader from node.js */
|
||||||
|
|
||||||
|
var DEVICEID = "BANGLEJS2";
|
||||||
|
var MINIFY = true; // minify JSON?
|
||||||
|
var BASE_DIR = __dirname + "/../..";
|
||||||
|
var APPSDIR = BASE_DIR+"/apps/";
|
||||||
|
|
||||||
|
//eval(require("fs").readFileSync(__dirname+"../core/js/utils.js"));
|
||||||
|
var Espruino = require(__dirname + "/../../core/lib/espruinotools.js");
|
||||||
|
//eval(require("fs").readFileSync(__dirname + "/../../core/lib/espruinotools.js").toString());
|
||||||
|
//eval(require("fs").readFileSync(__dirname + "/../../core/js/utils.js").toString());
|
||||||
|
var AppInfo = require(__dirname+"/../../core/js/appinfo.js");
|
||||||
|
|
||||||
|
var SETTINGS = {
|
||||||
|
pretokenise : true
|
||||||
|
};
|
||||||
|
global.Const = {
|
||||||
|
/* Are we only putting a single app on a device? If so
|
||||||
|
apps should all be saved as .bootcde and we write info
|
||||||
|
about the current app into app.info */
|
||||||
|
SINGLE_APP_ONLY : false,
|
||||||
|
};
|
||||||
|
|
||||||
|
var apps = [];
|
||||||
|
|
||||||
|
// call with {DEVICEID:"BANGLEJS/BANGLEJS2"}
|
||||||
|
exports.init = function(options) {
|
||||||
|
if (options.DEVICEID)
|
||||||
|
DEVICEID = options.DEVICEID;
|
||||||
|
// Load app metadata
|
||||||
|
var dirs = require("fs").readdirSync(APPSDIR, {withFileTypes: true});
|
||||||
|
dirs.forEach(dir => {
|
||||||
|
var appsFile;
|
||||||
|
if (dir.name.startsWith("_example") || !dir.isDirectory())
|
||||||
|
return;
|
||||||
|
try {
|
||||||
|
appsFile = require("fs").readFileSync(APPSDIR+dir.name+"/metadata.json").toString();
|
||||||
|
} catch (e) {
|
||||||
|
ERROR(dir.name+"/metadata.json does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
apps.push(JSON.parse(appsFile));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.AppInfo = AppInfo;
|
||||||
|
exports.apps = apps;
|
||||||
|
|
||||||
|
// used by getAppFiles
|
||||||
|
function fileGetter(url) {
|
||||||
|
url = BASE_DIR+"/"+url;
|
||||||
|
console.log("Loading "+url)
|
||||||
|
var data;
|
||||||
|
if (MINIFY && url.endsWith(".json")) {
|
||||||
|
var f = url.slice(0,-5);
|
||||||
|
console.log("MINIFYING JSON "+f);
|
||||||
|
var j = eval("("+require("fs").readFileSync(url).toString("binary")+")");
|
||||||
|
data = JSON.stringify(j);
|
||||||
|
} else {
|
||||||
|
var blob = require("fs").readFileSync(url);
|
||||||
|
if (url.endsWith(".js") || url.endsWith(".json"))
|
||||||
|
data = blob.toString(); // allow JS/etc to be written in UTF-8
|
||||||
|
else
|
||||||
|
data = blob.toString("binary")
|
||||||
|
}
|
||||||
|
return Promise.resolve(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getAppFiles = function(app) {
|
||||||
|
return AppInfo.getFiles(app, {
|
||||||
|
fileGetter:fileGetter,
|
||||||
|
settings : SETTINGS,
|
||||||
|
device : { id : DEVICEID }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get all the files for this app as a string of Storage.write commands
|
||||||
|
exports.getAppFilesString = function(app) {
|
||||||
|
return exports.getAppFiles(app).then(files => {
|
||||||
|
return files.map(f=>f.cmd).join("\n")+"\n"
|
||||||
|
})
|
||||||
|
};
|
|
@ -0,0 +1,115 @@
|
||||||
|
/* Node.js library with utilities to handle using the emulator from node.js */
|
||||||
|
|
||||||
|
var EMULATOR = "banglejs2";
|
||||||
|
var DEVICEID = "BANGLEJS2";
|
||||||
|
|
||||||
|
var BASE_DIR = __dirname + "/../..";
|
||||||
|
var DIR_IDE = BASE_DIR + "/../EspruinoWebIDE";
|
||||||
|
|
||||||
|
/* we factory reset ONCE, get this, then we can use it to reset
|
||||||
|
state quickly for each new app */
|
||||||
|
var factoryFlashMemory;
|
||||||
|
|
||||||
|
// Log of messages from app
|
||||||
|
var appLog = "";
|
||||||
|
var lastOutputLine = "";
|
||||||
|
|
||||||
|
function onConsoleOutput(txt) {
|
||||||
|
appLog += txt + "\n";
|
||||||
|
lastOutputLine = txt;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.init = function(options) {
|
||||||
|
if (options.EMULATOR)
|
||||||
|
EMULATOR = options.EMULATOR;
|
||||||
|
if (options.DEVICEID)
|
||||||
|
DEVICEID = options.DEVICEID;
|
||||||
|
|
||||||
|
eval(require("fs").readFileSync(DIR_IDE + "/emu/emulator_"+EMULATOR+".js").toString());
|
||||||
|
eval(require("fs").readFileSync(DIR_IDE + "/emu/emu_"+EMULATOR+".js").toString());
|
||||||
|
eval(require("fs").readFileSync(DIR_IDE + "/emu/common.js").toString()/*.replace('console.log("EMSCRIPTEN:"', '//console.log("EMSCRIPTEN:"')*/);
|
||||||
|
|
||||||
|
jsRXCallback = function() {};
|
||||||
|
jsUpdateGfx = function() {};
|
||||||
|
|
||||||
|
factoryFlashMemory = new Uint8Array(FLASH_SIZE);
|
||||||
|
factoryFlashMemory.fill(255);
|
||||||
|
|
||||||
|
exports.flashMemory = flashMemory;
|
||||||
|
exports.GFX_WIDTH = GFX_WIDTH;
|
||||||
|
exports.GFX_HEIGHT = GFX_HEIGHT;
|
||||||
|
exports.tx = jsTransmitString;
|
||||||
|
exports.idle = jsIdle;
|
||||||
|
exports.stopIdle = jsStopIdle;
|
||||||
|
exports.getGfxContents = jsGetGfxContents;
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(function() {
|
||||||
|
console.log("Emulator Loaded...");
|
||||||
|
jsInit();
|
||||||
|
jsIdle();
|
||||||
|
console.log("Emulator Factory reset");
|
||||||
|
exports.tx("Bangle.factoryReset()\n");
|
||||||
|
factoryFlashMemory.set(flashMemory);
|
||||||
|
console.log("Emulator Ready!");
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
},0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Factory reset
|
||||||
|
exports.factoryReset = function() {
|
||||||
|
exports.flashMemory.set(factoryFlashMemory);
|
||||||
|
exports.tx("reset()\n");
|
||||||
|
appLog="";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Transmit a string
|
||||||
|
exports.tx = function() {}; // placeholder
|
||||||
|
exports.idle = function() {}; // placeholder
|
||||||
|
exports.stopIdle = function() {}; // placeholder
|
||||||
|
exports.getGfxContents = function() {}; // placeholder
|
||||||
|
|
||||||
|
exports.flashMemory = undefined; // placeholder
|
||||||
|
exports.GFX_WIDTH = undefined; // placeholder
|
||||||
|
exports.GFX_HEIGHT = undefined; // placeholder
|
||||||
|
|
||||||
|
// Get last line sent to console
|
||||||
|
exports.getLastLine = function() {
|
||||||
|
return lastOutputLine;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Gets the screenshot as RGBA Uint32Array
|
||||||
|
exports.getScreenshot = function() {
|
||||||
|
var rgba = new Uint8Array(exports.GFX_WIDTH*exports.GFX_HEIGHT*4);
|
||||||
|
exports.getGfxContents(rgba);
|
||||||
|
var rgba32 = new Uint32Array(rgba.buffer);
|
||||||
|
return rgba32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the screenshot to a file options={errorIfBlank}
|
||||||
|
exports.writeScreenshot = function(imageFn, options) {
|
||||||
|
options = options||{};
|
||||||
|
return new Promise((resolve,reject) => {
|
||||||
|
var rgba32 = exports.getScreenshot();
|
||||||
|
|
||||||
|
if (options.errorIfBlank) {
|
||||||
|
var firstPixel = rgba32[0];
|
||||||
|
var blankImage = rgba32.every(col=>col==firstPixel);
|
||||||
|
if (blankImage) reject("Image is blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
var Jimp = require("jimp");
|
||||||
|
let image = new Jimp(exports.GFX_WIDTH, exports.GFX_HEIGHT, function (err, image) {
|
||||||
|
if (err) throw err;
|
||||||
|
let buffer = image.bitmap.data;
|
||||||
|
buffer.set(new Uint8Array(rgba32.buffer));
|
||||||
|
image.write(imageFn, (err) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
console.log("Image written as "+imageFn);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
#!/usr/bin/node
|
||||||
|
/*
|
||||||
|
|
||||||
|
This allows us to test apps using the Bangle.js emulator
|
||||||
|
|
||||||
|
IT IS UNFINISHED
|
||||||
|
|
||||||
|
It searches for `test.json` in each app's directory and will
|
||||||
|
run them in sequence.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
|
||||||
|
* more code to test with
|
||||||
|
* run tests that we have found and loaded (currently we just use TEST)
|
||||||
|
* documentation
|
||||||
|
* actual tests
|
||||||
|
* detecting 'Uncaught Error'
|
||||||
|
* logging of success/fail
|
||||||
|
* ...
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// A si
|
||||||
|
var TEST = {
|
||||||
|
app : "android",
|
||||||
|
tests : [ {
|
||||||
|
load : "messagesgui.app.js",
|
||||||
|
steps : [
|
||||||
|
{t:"gb", "obj":{"t":"notify","id":1234,"src":"Twitter","title":"A Name","body":"message contents"}},
|
||||||
|
{t:"cmd", "js":"X='hello';"},
|
||||||
|
{t:"eval", "js":"X", eq:"hello"}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
var EMULATOR = "banglejs2";
|
||||||
|
var DEVICEID = "BANGLEJS2";
|
||||||
|
|
||||||
|
var BASE_DIR = __dirname + "/..";
|
||||||
|
var APP_DIR = BASE_DIR + "/apps";
|
||||||
|
var DIR_IDE = BASE_DIR + "/../EspruinoWebIDE";
|
||||||
|
|
||||||
|
|
||||||
|
if (!require("fs").existsSync(DIR_IDE)) {
|
||||||
|
console.log("You need to:");
|
||||||
|
console.log(" git clone https://github.com/espruino/EspruinoWebIDE");
|
||||||
|
console.log("At the same level as this project");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var apploader = require(BASE_DIR+"/bin/lib/apploader.js");
|
||||||
|
apploader.init({
|
||||||
|
DEVICEID : DEVICEID
|
||||||
|
});
|
||||||
|
var emu = require(BASE_DIR+"/bin/lib/emulator.js");
|
||||||
|
|
||||||
|
// Last set of text received
|
||||||
|
var lastTxt;
|
||||||
|
|
||||||
|
function ERROR(s) {
|
||||||
|
console.error(s);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTest(test) {
|
||||||
|
var app = apploader.apps.find(a=>a.id==test.app);
|
||||||
|
if (!app) ERROR(`App ${JSON.stringify(test.app)} not found`);
|
||||||
|
if (app.custom) ERROR(`App ${JSON.stringify(appId)} requires HTML customisation`);
|
||||||
|
return apploader.getAppFilesString(app).then(command => {
|
||||||
|
// What about dependencies??
|
||||||
|
test.tests.forEach((subtest,subtestIdx) => {
|
||||||
|
console.log(`==============================`);
|
||||||
|
console.log(`"${test.app}" Test ${subtestIdx}`);
|
||||||
|
console.log(`==============================`);
|
||||||
|
emu.factoryReset();
|
||||||
|
console.log("Sending app "+test.app);
|
||||||
|
emu.tx(command);
|
||||||
|
console.log("Sent app");
|
||||||
|
emu.tx(test.load ? `load(${JSON.stringify(test.load)})\n` : "load()\n");
|
||||||
|
console.log("App Loaded.");
|
||||||
|
var ok = true;
|
||||||
|
subtest.steps.forEach(step => {
|
||||||
|
if (ok) switch(step.t) {
|
||||||
|
case "cmd" : emu.tx(`${step.js}\n`); break;
|
||||||
|
case "gb" : emu.tx(`GB(${JSON.stringify(step.obj)})\n`); break;
|
||||||
|
case "tap" : emu.tx(`Bangle.emit(...)\n`); break;
|
||||||
|
case "eval" :
|
||||||
|
emu.tx(`\x10print(JSON.stringify(${step.js}))\n`);
|
||||||
|
var result = emu.getLastLine();
|
||||||
|
var expected = JSON.stringify(step.eq);
|
||||||
|
console.log("GOT "+result);
|
||||||
|
if (result!=expected) {
|
||||||
|
console.log("EXPECTED "+expected);
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// tap/touch/drag/button press
|
||||||
|
// delay X milliseconds?
|
||||||
|
case "screenshot" :
|
||||||
|
console.log("Compare screenshots");
|
||||||
|
break;
|
||||||
|
default: ERROR("Unknown step type "+step.t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
emu.stopIdle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
emu.init({
|
||||||
|
EMULATOR : EMULATOR,
|
||||||
|
DEVICEID : DEVICEID
|
||||||
|
}).then(function() {
|
||||||
|
// Emulator is now loaded
|
||||||
|
console.log("Loading tests");
|
||||||
|
var tests = [];
|
||||||
|
apploader.apps.forEach(app => {
|
||||||
|
var testFile = APP_DIR+"/"+app.id+"/test.json";
|
||||||
|
if (!require("fs").existsSync(testFile)) return;
|
||||||
|
var test = JSON.parse(require("fs").readFileSync(testFile).toString());
|
||||||
|
test.app = app.id;
|
||||||
|
tests.push(test);
|
||||||
|
});
|
||||||
|
// Running tests
|
||||||
|
runTest(TEST);
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
if (erroredApps.length) {
|
||||||
|
erroredApps.forEach(app => {
|
||||||
|
console.log(`::error file=${app.id}::${app.id}`);
|
||||||
|
console.log("::group::Log");
|
||||||
|
app.log.split("\n").forEach(line => console.log(`\u001b[38;2;255;0;0m${line}`));
|
||||||
|
console.log("::endgroup::");
|
||||||
|
});
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
*/
|
|
@ -7,6 +7,9 @@ var DEVICEID = "BANGLEJS2";
|
||||||
var EMULATOR = "banglejs1";
|
var EMULATOR = "banglejs1";
|
||||||
var DEVICEID = "BANGLEJS";
|
var DEVICEID = "BANGLEJS";
|
||||||
|
|
||||||
|
var emu = require("./lib/emulator.js");
|
||||||
|
var apploader = require("./lib/apploader.js");
|
||||||
|
|
||||||
var singleAppId;
|
var singleAppId;
|
||||||
|
|
||||||
if (process.argv.length!=3 && process.argv.length!=2) {
|
if (process.argv.length!=3 && process.argv.length!=2) {
|
||||||
|
@ -20,126 +23,58 @@ if (process.argv.length!=3 && process.argv.length!=2) {
|
||||||
if (process.argv.length==3)
|
if (process.argv.length==3)
|
||||||
singleAppId = process.argv[2];
|
singleAppId = process.argv[2];
|
||||||
|
|
||||||
if (!require("fs").existsSync(__dirname + "/../../EspruinoWebIDE")) {
|
|
||||||
console.log("You need to:");
|
|
||||||
console.log(" git clone https://github.com/espruino/EspruinoWebIDE");
|
|
||||||
console.log("At the same level as this project");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(require("fs").readFileSync(__dirname + "/../../EspruinoWebIDE/emu/emulator_"+EMULATOR+".js").toString());
|
|
||||||
eval(require("fs").readFileSync(__dirname + "/../../EspruinoWebIDE/emu/emu_"+EMULATOR+".js").toString());
|
|
||||||
eval(require("fs").readFileSync(__dirname + "/../../EspruinoWebIDE/emu/common.js").toString());
|
|
||||||
|
|
||||||
var SETTINGS = {
|
|
||||||
pretokenise : true
|
|
||||||
};
|
|
||||||
var Const = {
|
|
||||||
};
|
|
||||||
module = undefined;
|
|
||||||
var Espruino = require(__dirname + "/../core/lib/espruinotools.js");
|
|
||||||
//eval(require("fs").readFileSync(__dirname + "/../core/lib/espruinotools.js").toString());
|
|
||||||
eval(require("fs").readFileSync(__dirname + "/../core/js/utils.js").toString());
|
|
||||||
eval(require("fs").readFileSync(__dirname + "/../core/js/appinfo.js").toString());
|
|
||||||
var apps = JSON.parse(require("fs").readFileSync(__dirname+"/../apps.json"));
|
|
||||||
|
|
||||||
/* we factory reset ONCE, get this, then we can use it to reset
|
|
||||||
state quickly for each new app */
|
|
||||||
var factoryFlashMemory = new Uint8Array(FLASH_SIZE);
|
|
||||||
// Log of messages from app
|
|
||||||
var appLog = "";
|
|
||||||
// List of apps that errored
|
// List of apps that errored
|
||||||
var erroredApps = [];
|
var erroredApps = [];
|
||||||
|
|
||||||
jsRXCallback = function() {};
|
|
||||||
jsUpdateGfx = function() {};
|
|
||||||
|
|
||||||
function ERROR(s) {
|
function ERROR(s) {
|
||||||
console.error(s);
|
console.error(s);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConsoleOutput(txt) {
|
|
||||||
appLog += txt + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getThumbnail(appId, imageFn) {
|
function getThumbnail(appId, imageFn) {
|
||||||
console.log("Thumbnail for "+appId);
|
console.log("Thumbnail for "+appId);
|
||||||
var app = apps.find(a=>a.id==appId);
|
var app = apploader.apps.find(a=>a.id==appId);
|
||||||
if (!app) ERROR(`App ${JSON.stringify(appId)} not found`);
|
if (!app) ERROR(`App ${JSON.stringify(appId)} not found`);
|
||||||
if (app.custom) ERROR(`App ${JSON.stringify(appId)} requires HTML customisation`);
|
if (app.custom) ERROR(`App ${JSON.stringify(appId)} requires HTML customisation`);
|
||||||
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return apploader.getAppFilesString(app).then(command => {
|
||||||
AppInfo.getFiles(app, {
|
|
||||||
fileGetter:function(url) {
|
|
||||||
console.log(__dirname+"/"+url);
|
|
||||||
return Promise.resolve(require("fs").readFileSync(__dirname+"/../"+url).toString("binary"));
|
|
||||||
},
|
|
||||||
settings : SETTINGS,
|
|
||||||
device : { id : DEVICEID }
|
|
||||||
}).then(files => {
|
|
||||||
console.log(`AppInfo returned for ${appId}`);//, files);
|
console.log(`AppInfo returned for ${appId}`);//, files);
|
||||||
flashMemory.set(factoryFlashMemory);
|
emu.factoryReset();
|
||||||
jsTransmitString("reset()\n");
|
|
||||||
console.log("Uploading...");
|
console.log("Uploading...");
|
||||||
jsTransmitString("g.clear()\n");
|
emu.tx("g.clear()\n");
|
||||||
var command = files.map(f=>f.cmd).join("\n")+"\n";
|
|
||||||
command += `load("${appId}.app.js")\n`;
|
command += `load("${appId}.app.js")\n`;
|
||||||
appLog = "";
|
appLog = "";
|
||||||
jsTransmitString(command);
|
emu.tx(command);
|
||||||
console.log("Done.");
|
console.log("Done.");
|
||||||
jsTransmitString("Bangle.setLCDMode();clearInterval();clearTimeout();\n");
|
emu.tx("Bangle.setLCDMode();clearInterval();clearTimeout();\n");
|
||||||
jsStopIdle();
|
emu.stopIdle();
|
||||||
|
|
||||||
var rgba = new Uint8Array(GFX_WIDTH*GFX_HEIGHT*4);
|
|
||||||
jsGetGfxContents(rgba);
|
|
||||||
var rgba32 = new Uint32Array(rgba.buffer);
|
|
||||||
var firstPixel = rgba32[0];
|
|
||||||
var blankImage = rgba32.every(col=>col==firstPixel)
|
|
||||||
|
|
||||||
if (appLog.replace("Uncaught Storage Updated!", "").indexOf("Uncaught")>=0)
|
|
||||||
erroredApps.push( { id : app.id, log : appLog } );
|
|
||||||
|
|
||||||
if (!blankImage) {
|
|
||||||
var Jimp = require("jimp");
|
|
||||||
let image = new Jimp(GFX_WIDTH, GFX_HEIGHT, function (err, image) {
|
|
||||||
if (err) throw err;
|
|
||||||
let buffer = image.bitmap.data;
|
|
||||||
buffer.set(rgba);
|
|
||||||
image.write(imageFn, (err) => {
|
|
||||||
if (err) throw err;
|
|
||||||
console.log("Image written as "+imageFn);
|
|
||||||
resolve(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log("Image is empty");
|
|
||||||
resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return emu.writeScreenshot(imageFn, { errorIfBlank : true }).then(() => console.log("X")).catch( err => {
|
||||||
|
console.log("Error", err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var screenshots = [];
|
var screenshots = [];
|
||||||
|
|
||||||
|
apploader.init({
|
||||||
|
EMULATOR : EMULATOR,
|
||||||
|
DEVICEID : DEVICEID
|
||||||
|
});
|
||||||
// wait until loaded...
|
// wait until loaded...
|
||||||
setTimeout(function() {
|
emu.init({
|
||||||
console.log("Loaded...");
|
EMULATOR : EMULATOR,
|
||||||
jsInit();
|
DEVICEID : DEVICEID
|
||||||
jsIdle();
|
}).then(function() {
|
||||||
console.log("Factory reset");
|
|
||||||
jsTransmitString("Bangle.factoryReset()\n");
|
|
||||||
factoryFlashMemory.set(flashMemory);
|
|
||||||
console.log("Ready!");
|
|
||||||
|
|
||||||
if (singleAppId) {
|
if (singleAppId) {
|
||||||
|
console.log("Single Screenshot");
|
||||||
getThumbnail(singleAppId, "screenshots/"+singleAppId+"-"+EMULATOR+".png");
|
getThumbnail(singleAppId, "screenshots/"+singleAppId+"-"+EMULATOR+".png");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var appList = apps.filter(app => (!app.type || app.type=="clock") && !app.custom);
|
console.log("Screenshot ALL");
|
||||||
|
var appList = apploader.apps.filter(app => (!app.type || app.type=="clock") && !app.custom);
|
||||||
appList = appList.filter(app => !app.screenshots && app.supports.includes(DEVICEID));
|
appList = appList.filter(app => !app.screenshots && app.supports.includes(DEVICEID));
|
||||||
|
|
||||||
var promise = Promise.resolve();
|
var promise = Promise.resolve();
|
||||||
|
|
Loading…
Reference in New Issue