monkey_master 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|