monkey_master 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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