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 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
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ monkey_logs
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in monkey_master.gemspec
4
+ gemspec
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
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
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,5 @@
1
+ module MonkeyMaster
2
+ end
3
+
4
+ require 'monkey_master/version'
5
+ require 'monkey_master/monkey_commander.rb'
@@ -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,3 @@
1
+ module MonkeyMaster
2
+ VERSION = "1.0.0"
3
+ 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