monkey_master 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +57 -0
- data/Rakefile +9 -0
- data/bin/monkey_master +52 -0
- data/lib/monkey_master.rb +5 -0
- data/lib/monkey_master/monkey_commander.rb +155 -0
- data/lib/monkey_master/version.rb +3 -0
- data/monkey_master.gemspec +23 -0
- data/test/test_monkey_master.rb +50 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA512:
|
3
|
+
metadata.gz: b983e4acff0f8b829bdd925d79fc8c6ceaa822f8b8f2fe7d78504d6878ed7c2c12a6f1297491672db250b088a458345d28795edde2a32fe02d16803cae58df45
|
4
|
+
data.tar.gz: df069919c860e5d650e979fed3153b9d6c6451cd0194a65be571bb8a364418e9f3163e223960444ae8f77d4e11a29f0e1039e7a3591ad76217533ed3ca1926f2
|
5
|
+
SHA1:
|
6
|
+
metadata.gz: 4772c7790a6dc9aaab02b3c51af3c7861e46b22a
|
7
|
+
data.tar.gz: 8204783357e7bacbef55815c00b8039f28d2c27c
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Innovaptor OG
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
*monkey_master* - A tool for conveniently employing Android adb monkeys.
|
2
|
+
================================================================
|
3
|
+
Android's adb offers the ui/application exerciser [monkey](http://developer.android.com/tools/help/monkey.html). Conveniently employing it can be cumbersome, though:
|
4
|
+
|
5
|
+
* It's inconvenient to kill a running monkey.
|
6
|
+
* You can't let it run unobserved for extended periods of time because it ends after a crash or a freeze.
|
7
|
+
* You either watch the log in your (running) sdk, or you manually handle logcat.
|
8
|
+
* Managing all of the above on multiple devices is a real pain.
|
9
|
+
|
10
|
+
*monkey_master* is a convenience tool for solving these issues. It can easily be combined with other tools, for example to build a fully automated build & test system.
|
11
|
+
|
12
|
+
Besides having convenience commands for starting and killing adb monkeys, it has multi-device support (simultaneously running monkeys on multiple devices) and automatically creates log files for each device.
|
13
|
+
|
14
|
+
For an example of a *monkey_master* test setup, and the reasoning behind the project, visit: [http://innovaptor.com/blog/2013/08/18/building-an-automated-testing-and-error-reporting-system-for-android-apps-with-monkey-master-and-crashlytics.html](http://innovaptor.com/blog/2013/08/18/building-an-automated-testing-and-error-reporting-system-for-android-apps-with-monkey-master-and-crashlytics.html)
|
15
|
+
|
16
|
+
Installation
|
17
|
+
================================================================
|
18
|
+
*monkey_master* is available as a ruby gem:
|
19
|
+
|
20
|
+
gem install monkey_master
|
21
|
+
|
22
|
+
`adb` needs to be in your *PATH* variable. To do this, you could add the following to your `~/.bashrc` or `~/.profile` for example:
|
23
|
+
|
24
|
+
export PATH=/YOUR/PATH/android-sdks/platform-tools:$PATH
|
25
|
+
|
26
|
+
Furthermore, you need to have a device in development mode connected. Currently, *monkey_master* is not tested with an emulator. For a list of connected devices, use `adb devices`.
|
27
|
+
|
28
|
+
Usage
|
29
|
+
================================================================
|
30
|
+
|
31
|
+
Usage:
|
32
|
+
monkey_master <app_id> [--devices <devices>] [--iterations <iterations>] [-k]
|
33
|
+
monkey_master -k
|
34
|
+
monkey_master -h | --help
|
35
|
+
monkey_master --version
|
36
|
+
|
37
|
+
Usage Examples
|
38
|
+
----------------------------------------------------------------
|
39
|
+
An example for a test run could be:
|
40
|
+
|
41
|
+
monkey_master com.my.App --iterations 100
|
42
|
+
|
43
|
+
If you want to stop the monkeys, either SIGINT the *monkey_master* during execution,
|
44
|
+
or call:
|
45
|
+
|
46
|
+
monkey_master -k
|
47
|
+
|
48
|
+
If you have multiple devices connected, and want to use *monkey_master* on some of them only,
|
49
|
+
call:
|
50
|
+
|
51
|
+
monkey_master com.my.App --devices DEVICEID1,DEVICEID2 --iterations 100
|
52
|
+
|
53
|
+
Contributing
|
54
|
+
================================================================
|
55
|
+
The initial version of *monkey_master* has been created with little ruby knowledge and with the pressure of an immediate need.
|
56
|
+
|
57
|
+
Code style or beauty fixes are just as welcome as pull requests, bug reports or ideas.
|
data/Rakefile
ADDED
data/bin/monkey_master
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'docopt'
|
4
|
+
|
5
|
+
require 'monkey_master'
|
6
|
+
|
7
|
+
# Parse Command Line Options
|
8
|
+
doc = <<DOCOPT
|
9
|
+
A tool for conveniently employing Android adb monkeys.
|
10
|
+
|
11
|
+
Usage:
|
12
|
+
#{__FILE__} <app_id> [--devices <devices>] [--iterations <iterations>] [-k]
|
13
|
+
#{__FILE__} -k
|
14
|
+
#{__FILE__} -h | --help
|
15
|
+
#{__FILE__} --version
|
16
|
+
|
17
|
+
Options:
|
18
|
+
-h --help Show this screen.
|
19
|
+
--version Show version.
|
20
|
+
--iterations <iterations> The number of monkeys that should be run consecutively.
|
21
|
+
It is preferable to run a high number of iterations of short-lived monkeys in order to handle freezes better.
|
22
|
+
--devices <devices> Devices which should be used by the monkey commander separated by a ','. If not given, uses all devices.
|
23
|
+
|
24
|
+
DOCOPT
|
25
|
+
|
26
|
+
begin
|
27
|
+
opts = Docopt::docopt(doc)
|
28
|
+
commander = MonkeyMaster::MonkeyCommander.new(opts["<app_id>"])
|
29
|
+
|
30
|
+
devices = opts["--devices"]
|
31
|
+
commander.detect_devices(devices)
|
32
|
+
|
33
|
+
if(opts["-k"])
|
34
|
+
commander.kill_monkeys
|
35
|
+
end
|
36
|
+
|
37
|
+
if(opts["<app_id>"])
|
38
|
+
# An app id has been given, proceed with starting monkeys on the devices
|
39
|
+
iterations = opts["--iterations"]
|
40
|
+
if(iterations)
|
41
|
+
commander.iterations = iterations
|
42
|
+
end
|
43
|
+
commander.command_monkeys
|
44
|
+
end
|
45
|
+
exit 0
|
46
|
+
rescue Docopt::Exit => e
|
47
|
+
puts e.message
|
48
|
+
rescue ArgumentError => e
|
49
|
+
puts "ERROR: Invalid arguments: " + e.message
|
50
|
+
end
|
51
|
+
|
52
|
+
exit 1
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'logger'
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
module MonkeyMaster
|
6
|
+
# A class for conveniently employing Android adb monkeys.
|
7
|
+
#
|
8
|
+
# Author:: Lukas Nagl (mailto:lukas.nagl@innovaptor.com)
|
9
|
+
# Copyright:: Copyright (c) 2013 Innovaptor OG
|
10
|
+
# License:: MIT
|
11
|
+
class MonkeyCommander
|
12
|
+
# Directory of the monkey logs.
|
13
|
+
attr_reader :log_dir
|
14
|
+
# Logger used for the monkey output.
|
15
|
+
attr_reader :logger
|
16
|
+
# The id of the app that should be tested by monkeys. E.g.: com.innovaptor.MonkeyTestApp
|
17
|
+
attr_writer :app_id
|
18
|
+
# The number of monkey iterations that should be run on each device.
|
19
|
+
attr_writer :iterations
|
20
|
+
# List of devices that should be used by the MonkeyCommander.
|
21
|
+
attr_writer :device_list
|
22
|
+
public
|
23
|
+
# Initialize the monkey master.
|
24
|
+
#
|
25
|
+
# +app_id+:: The id of the app that should be tested by the monkeys, e.g. com.innovaptor.MonkeyTestApp
|
26
|
+
def initialize(app_id)
|
27
|
+
@app_id = app_id
|
28
|
+
@iterations = 1 # Default to a single iteration
|
29
|
+
@base_dir = Dir.pwd
|
30
|
+
time = Time.new
|
31
|
+
@log_dir = "monkey_logs" + time.strftime("%Y%m%d_%H%M%S")
|
32
|
+
@logger = Logger.new(STDOUT)
|
33
|
+
@logger.formatter = proc { |severity, datetime, progname, msg|
|
34
|
+
"#{severity}|#{datetime}: #{msg}\n"
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Either create a list of devices from the parameter,
|
39
|
+
# or detect connected devices using adb.
|
40
|
+
#
|
41
|
+
# +devices+:: nil, for automatic device detection; or a list of device IDs separated by ','
|
42
|
+
def detect_devices(devices)
|
43
|
+
if(devices)
|
44
|
+
# Devices are given, create a list
|
45
|
+
devices = devices.split(',')
|
46
|
+
@device_list = devices
|
47
|
+
else
|
48
|
+
# No devices specified, detect them
|
49
|
+
device_list = %x(adb devices | grep -v "List" | grep "device" | awk '{print $1}')
|
50
|
+
device_list = device_list.split("\n")
|
51
|
+
@device_list = device_list
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Kill the monkey on each device.
|
56
|
+
def kill_monkeys
|
57
|
+
if(@device_list)
|
58
|
+
@device_list.each{|device|
|
59
|
+
@logger.info("[CLEANUP] KILLING the monkey on device #{device}.")
|
60
|
+
%x(adb -s #{device} shell ps | awk '/com\.android\.commands\.monkey/ { system("adb -s #{device} shell kill " $2) }')
|
61
|
+
}
|
62
|
+
else
|
63
|
+
@logger.warn("[CLEANUP] No devices specified yet.")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Start running monkeys on all specified devices.
|
68
|
+
def command_monkeys
|
69
|
+
if(!@device_list || @device_list.empty?)
|
70
|
+
raise ArgumentError, "No devices found or specified."
|
71
|
+
end
|
72
|
+
if(!@app_id)
|
73
|
+
raise ArgumentError, "No app id specified."
|
74
|
+
end
|
75
|
+
prepare
|
76
|
+
|
77
|
+
masters = []
|
78
|
+
begin
|
79
|
+
@device_list.each{|device|
|
80
|
+
master = Thread.new{
|
81
|
+
# Monkey around in parallel
|
82
|
+
|
83
|
+
log_device_name = "monkey_current" + device + ".txt";
|
84
|
+
current_log = File.join(@log_dir, log_device_name)
|
85
|
+
start_logging(device, current_log)
|
86
|
+
@logger.info("[MASTER #{device}] Starting to command monkeys.")
|
87
|
+
@iterations.to_i.times do |i|
|
88
|
+
@logger.info("\t[MASTER #{device}] Monkey " + i.to_s + " is doing its thing...")
|
89
|
+
|
90
|
+
# Start the monkey
|
91
|
+
%x(adb -s #{device} shell monkey -p #{@app_id} -v 80000 --throttle 100 --ignore-timeouts --pct-majornav 10 --pct-appswitch 0 --kill-process-after-error)
|
92
|
+
if($? != 0)
|
93
|
+
@logger.info("\t\t[MASTER #{device}] Monkey encountered an error!")
|
94
|
+
end
|
95
|
+
|
96
|
+
# Archive the log
|
97
|
+
log_archiving_name = "monkeylog_" + device + "_" + i.to_s + ".txt"
|
98
|
+
FileUtils.cp(current_log, File.join(@log_dir, log_archiving_name))
|
99
|
+
|
100
|
+
# Clean the current log
|
101
|
+
File.truncate(current_log, 0)
|
102
|
+
@logger.info("\t\t[MASTER #{device}] Monkey " + i.to_s + " is killing the app now in preparation for the next monkey.")
|
103
|
+
%x(adb -s #{device} shell am force-stop #{@app_id})
|
104
|
+
end
|
105
|
+
@logger.info("[MASTER #{device}] All monkeys are done.")
|
106
|
+
}
|
107
|
+
masters.push(master)
|
108
|
+
}
|
109
|
+
|
110
|
+
masters.each{|master| master.join} # wait for all masters to finish
|
111
|
+
rescue SystemExit, Interrupt
|
112
|
+
# Clean and graceful shutdown, if possible
|
113
|
+
@logger.info("[MASTER] Received interrupt. Stopping all masters.")
|
114
|
+
masters.each{|master| master.terminate}
|
115
|
+
end
|
116
|
+
|
117
|
+
kill_monkeys
|
118
|
+
end_logging
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
# Do all necessary preparations that are necessary for the monkeys to run.
|
123
|
+
def prepare
|
124
|
+
if(!File.directory?(@log_dir))
|
125
|
+
Dir.mkdir(@log_dir);
|
126
|
+
@logger.info("[SETUP] Writing to the following folder: #{@log_dir}")
|
127
|
+
end
|
128
|
+
kill_monkeys
|
129
|
+
end
|
130
|
+
|
131
|
+
# Start logging on all devices.
|
132
|
+
def start_logging(device, current_log)
|
133
|
+
begin
|
134
|
+
Timeout::timeout(5) {
|
135
|
+
@logger.info("[SETUP] Creating the following log file: #{current_log}")
|
136
|
+
%x(adb -s #{device} logcat -c #{current_log} &)
|
137
|
+
%x(adb -s #{device} logcat *:W > #{current_log} &)
|
138
|
+
}
|
139
|
+
rescue Timeout::Error
|
140
|
+
end_logging
|
141
|
+
raise ArgumentError, "It doesn't seem like there are ready, connected devices."
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# End logging on all devices.
|
146
|
+
def end_logging
|
147
|
+
@device_list.each{|device|
|
148
|
+
@logger.info("[CLEANUP] KILLING the logcat process on device #{device}.")
|
149
|
+
%x(adb -s #{device} shell ps | grep -m1 logcat | awk '{print $2}' | xargs adb -s #{device} shell kill)
|
150
|
+
@logger.info("[CLEANUP] KILLING the logcat process for the device #{device} on the machine.")
|
151
|
+
%x(ps ax | grep -m1 "adb -s #{device} logcat" | awk '{print $1}' | xargs kill)
|
152
|
+
}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'monkey_master/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "monkey_master"
|
8
|
+
spec.version = MonkeyMaster::VERSION
|
9
|
+
spec.authors = ["Lukas Nagl"]
|
10
|
+
spec.email = ["lukas.nagl@innovaptor.com"]
|
11
|
+
spec.description = %q{A tool for conveniently employing Android adb monkeys.}
|
12
|
+
spec.summary = %q{A tool for conveniently employing Android adb monkeys.}
|
13
|
+
spec.homepage = "https://github.com/j4zz/monkey_master.git"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'monkey_master'
|
3
|
+
|
4
|
+
class MonkeyCommanderTest < Test::Unit::TestCase
|
5
|
+
# Test the kill option in case no devices are connected
|
6
|
+
def test_kill_no_devices
|
7
|
+
assert_nothing_raised do
|
8
|
+
commander = MonkeyMaster::MonkeyCommander.new("com.test.Example")
|
9
|
+
commander.kill_monkeys
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Test the kill option with a list of invalid devices
|
14
|
+
def test_kill_invalid_devices
|
15
|
+
assert_nothing_raised do
|
16
|
+
commander = MonkeyMaster::MonkeyCommander.new("com.test.Example")
|
17
|
+
commander.device_list = ["80123","34555"]
|
18
|
+
commander.kill_monkeys
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Test trying regular execution in case no devices are connected
|
23
|
+
def test_command_no_devices
|
24
|
+
assert_raise ArgumentError do
|
25
|
+
commander = MonkeyMaster::MonkeyCommander.new("com.test.Example")
|
26
|
+
# no devices
|
27
|
+
commander.command_monkeys
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Test trying regular execution with an invalid app id
|
32
|
+
def test_command_no_app_id
|
33
|
+
assert_raise ArgumentError do
|
34
|
+
commander = MonkeyMaster::MonkeyCommander.new(nil)
|
35
|
+
commander.device_list = ["80123","34555"]
|
36
|
+
# no app id
|
37
|
+
commander.command_monkeys
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Test trying regular execution when invalid devices are given
|
42
|
+
def test_command_invalid_devices
|
43
|
+
assert_raise ArgumentError do
|
44
|
+
commander = MonkeyMaster::MonkeyCommander.new("com.test.Example")
|
45
|
+
commander.device_list = ["80123","34555"]
|
46
|
+
# invalid devices, should raise an exception
|
47
|
+
commander.command_monkeys
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: monkey_master
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lukas Nagl
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2013-08-18 00:00:00 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
prerelease: false
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: "1.3"
|
22
|
+
type: :development
|
23
|
+
version_requirements: *id001
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: rake
|
26
|
+
prerelease: false
|
27
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- &id003
|
30
|
+
- ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id002
|
35
|
+
description: A tool for conveniently employing Android adb monkeys.
|
36
|
+
email:
|
37
|
+
- lukas.nagl@innovaptor.com
|
38
|
+
executables:
|
39
|
+
- monkey_master
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- Gemfile
|
47
|
+
- LICENSE.txt
|
48
|
+
- README.md
|
49
|
+
- Rakefile
|
50
|
+
- bin/monkey_master
|
51
|
+
- lib/monkey_master.rb
|
52
|
+
- lib/monkey_master/monkey_commander.rb
|
53
|
+
- lib/monkey_master/version.rb
|
54
|
+
- monkey_master.gemspec
|
55
|
+
- test/test_monkey_master.rb
|
56
|
+
homepage: https://github.com/j4zz/monkey_master.git
|
57
|
+
licenses:
|
58
|
+
- MIT
|
59
|
+
metadata: {}
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- *id003
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- *id003
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 2.0.3
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: A tool for conveniently employing Android adb monkeys.
|
79
|
+
test_files:
|
80
|
+
- test/test_monkey_master.rb
|