first commit

This commit is contained in:
2026-09-08 20:28:54 +08:00
commit 2ada8d3d5a
37380 changed files with 4886169 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
Copyright © CyberAgent, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+469
View File
@@ -0,0 +1,469 @@
# adbkit-monkey
**adbkit-monkey** provides a [Node.js][nodejs] interface for working with the Android [`monkey` tool][monkey-site]. Albeit undocumented, they monkey program can be started in TCP mode with the `--port` argument. In this mode, it accepts a [range of commands][monkey-proto] that can be used to interact with the UI in a non-random manner. This mode is also used internally by the [`monkeyrunner` tool][monkeyrunner-site], although the documentation claims no relation to the monkey tool.
## Getting started
Install via NPM:
```bash
npm install --save adbkit-monkey
```
Note that while adbkit-monkey is written in CoffeeScript, it is compiled to JavaScript before publishing to NPM, which means that you are not required to use CoffeeScript.
### Examples
The following examples assume that monkey is already running (via `adb shell monkey --port 1080`) and a port forwarding (`adb forward tcp:1080 tcp:1080`) has been set up.
#### Press the home button
```javascript
var assert = require('assert');
var monkey = require('adbkit-monkey');
var client = monkey.connect({ port: 1080 });
client.press(3 /* KEYCODE_HOME */, function(err) {
assert.ifError(err);
console.log('Pressed home button');
client.end();
});
```
#### Drag out the notification bar
```javascript
var assert = require('assert');
var monkey = require('adbkit-monkey');
var client = monkey.connect({ port: 1080 });
client.multi()
.touchDown(100, 0)
.sleep(5)
.touchMove(100, 20)
.sleep(5)
.touchMove(100, 40)
.sleep(5)
.touchMove(100, 60)
.sleep(5)
.touchMove(100, 80)
.sleep(5)
.touchMove(100, 100)
.sleep(5)
.touchUp(100, 100)
.sleep(5)
.execute(function(err) {
assert.ifError(err);
console.log('Dragged out the notification bar');
client.end();
});
```
#### Get display size
```javascript
var assert = require('assert');
var monkey = require('adbkit-monkey');
var client = monkey.connect({ port: 1080 });
client.getDisplayWidth(function(err, width) {
assert.ifError(err);
client.getDisplayHeight(function(err, height) {
assert.ifError(err);
console.log('Display size is %dx%d', width, height);
client.end();
});
});
```
#### Type text
Note that you should manually focus a text field first.
```javascript
var assert = require('assert');
var monkey = require('adbkit-monkey');
var client = monkey.connect({ port: 1080 });
client.type('hello monkey!', function(err) {
assert.ifError(err);
console.log('Said hello to monkey');
client.end();
});
```
## API
### Monkey
#### monkey.connect(options)
Uses [Net.connect()][node-net] to open a new TCP connection to monkey. Useful when combined with `adb forward`.
* **options** Any options [`Net.connect()`][node-net] accepts.
* Returns: A new monkey `Client` instance.
#### monkey.connectStream(stream)
Attaches a monkey client to an existing monkey protocol stream.
* **stream** The monkey protocol [`Stream`][node-stream].
* Returns: A new monkey `Client` instance.
### Client
Implements `Api`. See below for details.
#### Events
The following events are available:
* **error** **(err)** Emitted when an error occurs.
* **err** An `Error`.
* **end** Emitted when the stream ends.
* **finish** Emitted when the stream finishes.
#### client.end()
Ends the underlying stream/connection.
* Returns: The `Client` instance.
#### client.multi()
Returns a new API wrapper that buffers commands for simultaneous delivery instead of sending them individually. When used with `api.sleep()`, allows simple gestures to be executed.
* Returns: A new `Multi` instance. See `Multi` below.
#### client.send(command, callback)
Sends a raw protocol command to monkey.
* **command** The command to send. When `String`, a single command is sent. When `Array`, a series of commands is sent at once.
* **callback(err, value, command)** Called when monkey responds to the command. If multiple commands were sent, the callback will be called once for each command.
* **err** `null` when successful, `Error` otherwise.
* **value** The response value, if any.
* **command** The command the response is for.
* Returns: The `Client` instance.
### Api
The monkey API implemented by `Client` and `Multi`.
#### api.done(callback)
Closes the current monkey session and allows a new session to connect.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.flipClose(callback)
Simulates closing the keyboard.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.flipOpen(callback)
Simulates opening the keyboard.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.get(name, callback)
Gets the value of a variable. Use `api.list()` to retrieve a list of supported variables.
* **name** The name of the variable.
* **callback(err, value)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* **value** The value of the variable.
* Returns: The `Api` implementation instance.
#### api.getAmCurrentAction(callback)
Alias for `api.get('am.current.action', callback)`.
#### api.getAmCurrentCategories(callback)
Alias for `api.get('am.current.categories', callback)`.
#### api.getAmCurrentCompClass(callback)
Alias for `api.get('am.current.comp.class', callback)`.
#### api.getAmCurrentCompPackage(callback)
Alias for `api.get('am.current.comp.package', callback)`.
#### api.getCurrentData(callback)
Alias for `api.get('am.current.data', callback)`.
#### api.getAmCurrentPackage(callback)
Alias for `api.get('am.current.package', callback)`.
#### api.getBuildBoard(callback)
Alias for `api.get('build.board', callback)`.
#### api.getBuildBrand(callback)
Alias for `api.get('build.brand', callback)`.
#### api.getBuildCpuAbi(callback)
Alias for `api.get('build.cpu_abi', callback)`.
#### api.getBuildDevice(callback)
Alias for `api.get('build.device', callback)`.
#### api.getBuildDisplay(callback)
Alias for `api.get('build.display', callback)`.
#### api.getBuildFingerprint(callback)
Alias for `api.get('build.fingerprint', callback)`.
#### api.getBuildHost(callback)
Alias for `api.get('build.host', callback)`.
#### api.getBuildId(callback)
Alias for `api.get('build.id', callback)`.
#### api.getBuildManufacturer(callback)
Alias for `api.get('build.manufacturer', callback)`.
#### api.getBuildModel(callback)
Alias for `api.get('build.model', callback)`.
#### api.getBuildProduct(callback)
Alias for `api.get('build.product', callback)`.
#### api.getBuildTags(callback)
Alias for `api.get('build.tags', callback)`.
#### api.getBuildType(callback)
Alias for `api.get('build.type', callback)`.
#### api.getBuildUser(callback)
Alias for `api.get('build.user', callback)`.
#### api.getBuildVersionCodename(callback)
Alias for `api.get('build.version.codename', callback)`.
#### api.getBuildVersionIncremental(callback)
Alias for `api.get('build.version.incremental', callback)`.
#### api.getBuildVersionRelease(callback)
Alias for `api.get('build.version.release', callback)`.
#### api.getBuildVersionSdk(callback)
Alias for `api.get('build.version.sdk', callback)`.
#### api.getClockMillis(callback)
Alias for `api.get('clock.millis', callback)`.
#### api.getClockRealtime(callback)
Alias for `api.get('clock.realtime', callback)`.
#### api.getClockUptime(callback)
Alias for `api.get('clock.uptime', callback)`.
#### api.getDisplayDensity(callback)
Alias for `api.get('display.density', callback)`.
#### api.getDisplayHeight(callback)
Alias for `api.get('display.height', callback)`. Note that the height may exclude any virtual home button row.
#### api.getDisplayWidth(callback)
Alias for `api.get('display.width', callback)`.
#### api.keyDown(keyCode, callback)
Sends a key down event. Should be coupled with `api.keyUp()`. Note that `api.press()` performs the two events automatically.
* **keyCode** The [key code][android-keycodes]. All monkeys support numeric keycodes, and some support automatic conversion from key names to key codes (e.g. `'home'` to `KEYCODE_HOME`). This will not work for number keys however. The most portable method is to simply use numeric key codes.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.keyUp(keyCode, callback)
Sends a key up event. Should be coupled with `api.keyDown()`. Note that `api.press()` performs the two events automatically.
* **keyCode** See `api.keyDown()`.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.list(callback)
Lists supported variables.
* **callback(err, vars)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* **vars** An array of supported variable names, to be used with `api.get()`.
* Returns: The `Api` implementation instance.
#### api.press(keyCode, callback)
Sends a key press event.
* **keyCode** See `api.keyDown()`.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.quit(callback)
Closes the current monkey session and quits monkey.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.sleep(ms, callback)
Sleeps for the given duration. Can be useful for simulating gestures.
* **ms** How many milliseconds to sleep for.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.tap(x, y, callback)
Taps the given coordinates.
* **x** The x coordinate.
* **y** The y coordinate.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.touchDown(x, y, callback)
Sends a touch down event on the given coordinates.
* **x** The x coordinate.
* **y** The y coordinate.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.touchMove(x, y, callback)
Sends a touch move event on the given coordinates.
* **x** The x coordinate.
* **y** The y coordinate.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.touchUp(x, y, callback)
Sends a touch up event on the given coordinates.
* **x** The x coordinate.
* **y** The y coordinate.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.trackball(x, y, callback)
Sends a trackball event on the given coordinates.
* **x** The x coordinate.
* **y** The y coordinate.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.type(text, callback)
Types the given text.
* **text** A text `String`. Note that only characters for which [key codes][android-keycodes] exist can be entered. Also note that any IME in use may or may not transform the text.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
#### api.wake(callback)
Wakes the device from sleep and allows user input.
* **callback(err)** Called when monkey responds.
* **err** `null` when successful, `Error` otherwise.
* Returns: The `Api` implementation instance.
### Multi
Buffers `Api` commands and delivers them simultaneously for greater control over timing.
Implements all `Api` methods, but without the last `callback` parameter.
#### multi.execute(callback)
Sends all buffered commands.
* **callback(err, values)** Called when monkey has responded to all commands (i.e. just once at the end).
* **err** `null` when successful, `Error` otherwise.
* **values** An array of all response values, identical to individual `Api` responses.
## More information
* [Monkey][monkey-site]
- [Source code][monkey-source]
- [Protocol][monkey-proto]
* [Monkeyrunner][monkeyrunner-site]
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md).
## License
See [LICENSE](LICENSE).
Copyright © CyberAgent, Inc. All Rights Reserved.
[nodejs]: <http://nodejs.org/>
[monkey-site]: <http://developer.android.com/tools/help/monkey.html>
[monkey-source]: <https://github.com/android/platform_development/blob/master/cmds/monkey/>
[monkey-proto]: <https://github.com/android/platform_development/blob/master/cmds/monkey/README.NETWORK.txt>
[monkeyrunner-site]: <http://developer.android.com/tools/help/monkeyrunner_concepts.html>
[node-net]: <http://nodejs.org/api/net.html>
[node-stream]: <http://nodejs.org/api/stream.html>
[android-keycodes]: <http://developer.android.com/reference/android/view/KeyEvent.html>
+15
View File
@@ -0,0 +1,15 @@
(function() {
var Path;
Path = require('path');
module.exports = (function() {
switch (Path.extname(__filename)) {
case '.coffee':
return require('./src/monkey');
default:
return require('./lib/monkey');
}
})();
}).call(this);
+29
View File
@@ -0,0 +1,29 @@
(function() {
var Client, Connection, Monkey;
Client = require('./monkey/client');
Connection = require('./monkey/connection');
Monkey = (function() {
function Monkey() {}
Monkey.connect = function(options) {
return new Connection().connect(options);
};
Monkey.connectStream = function(stream) {
return new Client().connect(stream);
};
return Monkey;
})();
Monkey.Connection = Connection;
Monkey.Client = Client;
module.exports = Monkey;
}).call(this);
+276
View File
@@ -0,0 +1,276 @@
(function() {
var Api, EventEmitter, _ref,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
EventEmitter = require('events').EventEmitter;
Api = (function(_super) {
__extends(Api, _super);
function Api() {
_ref = Api.__super__.constructor.apply(this, arguments);
return _ref;
}
Api.prototype.send = function() {
throw new Error("send is not implemented");
};
Api.prototype.keyDown = function(keyCode, callback) {
this.send("key down " + keyCode, callback);
return this;
};
Api.prototype.keyUp = function(keyCode, callback) {
this.send("key up " + keyCode, callback);
return this;
};
Api.prototype.touchDown = function(x, y, callback) {
this.send("touch down " + x + " " + y, callback);
return this;
};
Api.prototype.touchUp = function(x, y, callback) {
this.send("touch up " + x + " " + y, callback);
return this;
};
Api.prototype.touchMove = function(x, y, callback) {
this.send("touch move " + x + " " + y, callback);
return this;
};
Api.prototype.trackball = function(dx, dy, callback) {
this.send("trackball " + dx + " " + dy, callback);
return this;
};
Api.prototype.flipOpen = function(callback) {
this.send("flip open", callback);
return this;
};
Api.prototype.flipClose = function(callback) {
this.send("flip close", callback);
return this;
};
Api.prototype.wake = function(callback) {
this.send("wake", callback);
return this;
};
Api.prototype.tap = function(x, y, callback) {
this.send("tap " + x + " " + y, callback);
return this;
};
Api.prototype.press = function(keyCode, callback) {
this.send("press " + keyCode, callback);
return this;
};
Api.prototype.type = function(str, callback) {
str = str.replace(/"/g, '\\"');
if (str.indexOf(' ') === -1) {
this.send("type " + str, callback);
} else {
this.send("type \"" + str + "\"", callback);
}
return this;
};
Api.prototype.list = function(callback) {
var _this = this;
this.send("listvar", function(err, vars) {
if (err) {
return _this(callback(err));
}
if (err) {
return callback(err);
} else {
return callback(null, vars.split(/\s+/g));
}
});
return this;
};
Api.prototype.get = function(name, callback) {
this.send("getvar " + name, callback);
return this;
};
Api.prototype.quit = function(callback) {
this.send("quit", callback);
return this;
};
Api.prototype.done = function(callback) {
this.send("done", callback);
return this;
};
Api.prototype.sleep = function(ms, callback) {
this.send("sleep " + ms, callback);
return this;
};
Api.prototype.getAmCurrentAction = function(callback) {
this.get('am.current.action', callback);
return this;
};
Api.prototype.getAmCurrentCategories = function(callback) {
this.get('am.current.categories', callback);
return this;
};
Api.prototype.getAmCurrentCompClass = function(callback) {
this.get('am.current.comp.class', callback);
return this;
};
Api.prototype.getAmCurrentCompPackage = function(callback) {
this.get('am.current.comp.package', callback);
return this;
};
Api.prototype.getAmCurrentData = function(callback) {
this.get('am.current.data', callback);
return this;
};
Api.prototype.getAmCurrentPackage = function(callback) {
this.get('am.current.package', callback);
return this;
};
Api.prototype.getBuildBoard = function(callback) {
this.get('build.board', callback);
return this;
};
Api.prototype.getBuildBrand = function(callback) {
this.get('build.brand', callback);
return this;
};
Api.prototype.getBuildCpuAbi = function(callback) {
this.get('build.cpu_abi', callback);
return this;
};
Api.prototype.getBuildDevice = function(callback) {
this.get('build.device', callback);
return this;
};
Api.prototype.getBuildDisplay = function(callback) {
this.get('build.display', callback);
return this;
};
Api.prototype.getBuildFingerprint = function(callback) {
this.get('build.fingerprint', callback);
return this;
};
Api.prototype.getBuildHost = function(callback) {
this.get('build.host', callback);
return this;
};
Api.prototype.getBuildId = function(callback) {
this.get('build.id', callback);
return this;
};
Api.prototype.getBuildManufacturer = function(callback) {
this.get('build.manufacturer', callback);
return this;
};
Api.prototype.getBuildModel = function(callback) {
this.get('build.model', callback);
return this;
};
Api.prototype.getBuildProduct = function(callback) {
this.get('build.product', callback);
return this;
};
Api.prototype.getBuildTags = function(callback) {
this.get('build.tags', callback);
return this;
};
Api.prototype.getBuildType = function(callback) {
this.get('build.type', callback);
return this;
};
Api.prototype.getBuildUser = function(callback) {
this.get('build.user', callback);
return this;
};
Api.prototype.getBuildVersionCodename = function(callback) {
this.get('build.version.codename', callback);
return this;
};
Api.prototype.getBuildVersionIncremental = function(callback) {
this.get('build.version.incremental', callback);
return this;
};
Api.prototype.getBuildVersionRelease = function(callback) {
this.get('build.version.release', callback);
return this;
};
Api.prototype.getBuildVersionSdk = function(callback) {
this.get('build.version.sdk', callback);
return this;
};
Api.prototype.getClockMillis = function(callback) {
this.get('clock.millis', callback);
return this;
};
Api.prototype.getClockRealtime = function(callback) {
this.get('clock.realtime', callback);
return this;
};
Api.prototype.getClockUptime = function(callback) {
this.get('clock.uptime', callback);
return this;
};
Api.prototype.getDisplayDensity = function(callback) {
this.get('display.density', callback);
return this;
};
Api.prototype.getDisplayHeight = function(callback) {
this.get('display.height', callback);
return this;
};
Api.prototype.getDisplayWidth = function(callback) {
this.get('display.width', callback);
return this;
};
return Api;
})(EventEmitter);
module.exports = Api;
}).call(this);
+98
View File
@@ -0,0 +1,98 @@
(function() {
var Api, Client, Command, Multi, Parser, Queue, Reply,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
Api = require('./api');
Command = require('./command');
Reply = require('./reply');
Queue = require('./queue');
Multi = require('./multi');
Parser = require('./parser');
Client = (function(_super) {
__extends(Client, _super);
function Client() {
this.commandQueue = new Queue;
this.parser = new Parser;
this.stream = null;
}
Client.prototype._hook = function() {
var _this = this;
this.stream.on('data', function(data) {
return _this.parser.parse(data);
});
this.stream.on('error', function(err) {
return _this.emit('error', err);
});
this.stream.on('end', function() {
return _this.emit('end');
});
this.stream.on('finish', function() {
return _this.emit('finish');
});
this.parser.on('reply', function(reply) {
return _this._consume(reply);
});
this.parser.on('error', function(err) {
return _this.emit('error', err);
});
};
Client.prototype._consume = function(reply) {
var command;
if (command = this.commandQueue.dequeue()) {
if (reply.isError()) {
command.callback(reply.toError(), null, command.command);
} else {
command.callback(null, reply.value, command.command);
}
} else {
throw new Error("Command queue depleted, but replies still coming in");
}
};
Client.prototype.connect = function(stream) {
this.stream = stream;
this._hook();
return this;
};
Client.prototype.end = function() {
this.stream.end();
return this;
};
Client.prototype.send = function(commands, callback) {
var command, _i, _len;
if (Array.isArray(commands)) {
for (_i = 0, _len = commands.length; _i < _len; _i++) {
command = commands[_i];
this.commandQueue.enqueue(new Command(command, callback));
}
this.stream.write("" + (commands.join('\n')) + "\n");
} else {
this.commandQueue.enqueue(new Command(commands, callback));
this.stream.write("" + commands + "\n");
}
return this;
};
Client.prototype.multi = function() {
return new Multi(this);
};
return Client;
})(Api);
module.exports = Client;
}).call(this);
+17
View File
@@ -0,0 +1,17 @@
(function() {
var Command;
Command = (function() {
function Command(command, callback) {
this.command = command;
this.callback = callback;
this.next = null;
}
return Command;
})();
module.exports = Command;
}).call(this);
+42
View File
@@ -0,0 +1,42 @@
(function() {
var Client, Connection, Net, _ref,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
Net = require('net');
Client = require('./client');
Connection = (function(_super) {
__extends(Connection, _super);
function Connection() {
_ref = Connection.__super__.constructor.apply(this, arguments);
return _ref;
}
Connection.prototype.connect = function(options) {
var stream;
stream = Net.connect(options);
stream.setNoDelay(true);
return Connection.__super__.connect.call(this, stream);
};
Connection.prototype._hook = function() {
var _this = this;
this.stream.on('connect', function() {
return _this.emit('connect');
});
this.stream.on('close', function(hadError) {
return _this.emit('close', hadError);
});
return Connection.__super__._hook.call(this);
};
return Connection;
})(Client);
module.exports = Connection;
}).call(this);
+85
View File
@@ -0,0 +1,85 @@
(function() {
var Api, Command, Multi,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
Api = require('./api');
Command = require('./command');
Multi = (function(_super) {
__extends(Multi, _super);
function Multi(monkey) {
var _this = this;
this.monkey = monkey;
this.commands = [];
this.replies = [];
this.errors = [];
this.counter = 0;
this.sent = false;
this.callback = null;
this.collector = function(err, result, cmd) {
if (err) {
_this.errors.push("" + cmd + ": " + err.message);
}
_this.replies.push(result);
_this.counter -= 1;
return _this._maybeFinish();
};
}
Multi.prototype._maybeFinish = function() {
var _this = this;
if (this.counter === 0) {
if (this.errors.length) {
setImmediate(function() {
return _this.callback(new Error(_this.errors.join(', ')));
});
} else {
setImmediate(function() {
return _this.callback(null, _this.replies);
});
}
}
};
Multi.prototype._forbidReuse = function() {
if (this.sent) {
throw new Error("Reuse not supported");
}
};
Multi.prototype.send = function(command) {
this._forbidReuse();
this.commands.push(new Command(command, this.collector));
};
Multi.prototype.execute = function(callback) {
var command, parts, _i, _len, _ref;
this._forbidReuse();
this.counter = this.commands.length;
this.sent = true;
this.callback = callback;
if (this.counter === 0) {
return;
}
parts = [];
_ref = this.commands;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
command = _ref[_i];
this.monkey.commandQueue.enqueue(command);
parts.push(command.command);
}
parts.push('');
this.commands = [];
this.monkey.stream.write(parts.join('\n'));
};
return Multi;
})(Api);
module.exports = Multi;
}).call(this);
+66
View File
@@ -0,0 +1,66 @@
(function() {
var EventEmitter, Parser, Reply,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
EventEmitter = require('events').EventEmitter;
Reply = require('./reply');
Parser = (function(_super) {
__extends(Parser, _super);
function Parser(options) {
this.column = 0;
this.buffer = new Buffer('');
}
Parser.prototype.parse = function(chunk) {
this.buffer = Buffer.concat([this.buffer, chunk]);
while (this.column < this.buffer.length) {
if (this.buffer[this.column] === 0x0a) {
this._parseLine(this.buffer.slice(0, this.column));
this.buffer = this.buffer.slice(this.column + 1);
this.column = 0;
}
this.column += 1;
}
if (this.buffer.length) {
this.emit('wait');
} else {
this.emit('drain');
}
};
Parser.prototype._parseLine = function(line) {
switch (line[0]) {
case 0x4f:
if (line.length === 2) {
this.emit('reply', new Reply(Reply.OK, null));
} else {
this.emit('reply', new Reply(Reply.OK, line.toString('ascii', 3)));
}
break;
case 0x45:
if (line.length === 5) {
this.emit('reply', new Reply(Reply.ERROR, null));
} else {
this.emit('reply', new Reply(Reply.ERROR, line.toString('ascii', 6)));
}
break;
default:
this._complain(line);
}
};
Parser.prototype._complain = function(line) {
this.emit('error', new SyntaxError("Unparseable line '" + line + "'"));
};
return Parser;
})(EventEmitter);
module.exports = Parser;
}).call(this);
+38
View File
@@ -0,0 +1,38 @@
(function() {
var Queue;
Queue = (function() {
function Queue() {
this.head = null;
this.tail = null;
}
Queue.prototype.enqueue = function(item) {
if (this.tail) {
this.tail.next = item;
} else {
this.head = item;
}
this.tail = item;
};
Queue.prototype.dequeue = function() {
var item;
item = this.head;
if (item) {
if (item === this.tail) {
this.tail = null;
}
this.head = item.next;
item.next = null;
}
return item;
};
return Queue;
})();
module.exports = Queue;
}).call(this);
+31
View File
@@ -0,0 +1,31 @@
(function() {
var Reply;
Reply = (function() {
Reply.ERROR = 'ERROR';
Reply.OK = 'OK';
function Reply(type, value) {
this.type = type;
this.value = value;
}
Reply.prototype.isError = function() {
return this.type === Reply.ERROR;
};
Reply.prototype.toError = function() {
if (!this.isError()) {
throw new Error('toError() cannot be called for non-errors');
}
return new Error(this.value);
};
return Reply;
})();
module.exports = Reply;
}).call(this);
+52
View File
@@ -0,0 +1,52 @@
{
"name": "adbkit-monkey",
"version": "1.0.1",
"description": "A Node.js interface to the Android monkey tool.",
"keywords": [
"adb",
"adbkit",
"monkey",
"monkeyrunner"
],
"bugs": {
"url": "https://github.com/CyberAgent/adbkit-monkey/issues"
},
"license": "Apache-2.0",
"author": {
"name": "CyberAgent, Inc.",
"email": "npm@cyberagent.co.jp",
"url": "http://www.cyberagent.co.jp/"
},
"main": "./index",
"repository": {
"type": "git",
"url": "https://github.com/CyberAgent/adbkit-monkey.git"
},
"scripts": {
"postpublish": "grunt clean",
"prepublish": "grunt coffee",
"test": "grunt test"
},
"dependencies": {
"async": "~0.2.9"
},
"devDependencies": {
"chai": "~1.8.1",
"coffee-script": "~1.6.3",
"grunt": "~0.4.1",
"grunt-cli": "~0.1.11",
"grunt-coffeelint": "~0.0.7",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-coffee": "~0.7.0",
"grunt-contrib-watch": "~0.5.3",
"grunt-exec": "~0.4.2",
"grunt-jsonlint": "~1.0.2",
"grunt-notify": "~0.2.16",
"mocha": "~1.14.0",
"sinon": "~1.7.3",
"sinon-chai": "~2.4.0"
},
"engines": {
"node": ">= 0.10.4"
}
}