droidspecter 0.0.1

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
+ SHA1:
3
+ metadata.gz: f5ae753d3428c78efecdfcdd2ec7446c450950ba
4
+ data.tar.gz: 639a541e93ae1999bd131c1339d234b27ec02929
5
+ SHA512:
6
+ metadata.gz: 70dd3eefcb9e688114d38a49b6cd951a24e9df5679039fb4eef9a4bdbb8a94b587d99c731afd2c245bba8adbf6c03041b2f7484b13500bfa6d058d84d75ea8f6
7
+ data.tar.gz: 2b5319ad946df15cbe8b6dd10a1e24b37296377b82282a92d62a6bebfcb018baf5085de144cc86c667eb5c7b4d6841da6b6c645122e7ba3443495f5d78295832
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
data/bin/droidrunner ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'droidrunner'
4
+
5
+ Droidrunner.start(ARGV)
@@ -0,0 +1,169 @@
1
+ require "thor"
2
+ require "droidrunner/apprunner"
3
+ require "droidrunner/vbmanager"
4
+ require "droidrunner/adbrunner"
5
+ require "droidrunner/devicemanager"
6
+
7
+ class Droidrunner < Thor
8
+
9
+ option :execution_time, type: :numeric, default: 60, desc: "Time to execute app after monkey events"
10
+ option :monkey_events, type: :numeric, default: 500, desc: "Number of monkey events to execute"
11
+ option :vm_image_name, type: :string, default: "AndroidX86", desc: "Name of the VM to clone"
12
+ option :dedicated_vm, type: :boolean, default: false, desc: "If a dedicated VM should be created"
13
+ option :accelerate3d, type: :boolean, default: false, desc: "Turn on 3D acceleration"
14
+ desc "run-app-in-vm app_name","Runs an application in a VM."
15
+ def run_app_in_vm(app_name)
16
+
17
+ apk_name = /([\w\.]+).apk$/.match(app_name)[1]
18
+ puts "Running run_app_in_vm for application : #{apk_name}"
19
+
20
+ if options[:dedicated_vm]
21
+ manager = Vbmanager.new(vm_image:options[:vm_image_name])
22
+ #manager.import_ovf
23
+ Vbmanager.list_vms
24
+ manager.clone_vm
25
+ manager.configure_vm(accelerated:options[:accelerate3d])
26
+ manager.start_vm
27
+ else
28
+ manager = Vbmanager.new(vm_image:options[:vm_image_name],reuse:true)
29
+ #manager.create_snapshot #should already be created
30
+ manager.start_vm
31
+ end
32
+
33
+ adbrunner = Adbrunner.new(port:manager.port)
34
+ adbrunner.wait_for_boot
35
+
36
+ apprunner = Apprunner.new(adb:adbrunner)
37
+ apprunner.install_app app_name
38
+ apprunner.run_app(apk_name)
39
+ apprunner.monkey_app(apk_name, options[:monkey_events])
40
+
41
+ puts "Waiting for #{options[:execution_time]}"
42
+ sleep options[:execution_time]
43
+
44
+ apprunner.kill_app(apk_name)
45
+
46
+ if options[:dedicated_vm]
47
+ manager.stop_vm
48
+ manager.del_vm
49
+ else
50
+ manager.stop_vm
51
+ manager.load_snapshot
52
+ end
53
+ end
54
+
55
+ option :execution_time, type: :numeric, default: 60, desc: "Time to execute app"
56
+ option :monkey_events, type: :numeric, default: 500, desc: "Number of monkey events to execute"
57
+ desc "run_app_in_device DEVICE_ID APP","Runs an application in a physical device."
58
+ def run_app_in_device(device_id, app_name)
59
+ apk_name = /([\w\.]+).apk$/.match(app_name)[1]
60
+ puts "Running run_app_in_vm for application : #{apk_name}"
61
+
62
+ manager = Devicemanager.new(id:device_id)
63
+ adb = Adbrunner.new(id:device_id)
64
+ apprunner = Apprunner.new(adb:adb)
65
+
66
+ apprunner.install_app app_name
67
+ apprunner.run_app(apk_name)
68
+ apprunner.monkey_app(apk_name, options[:monkey_events])
69
+
70
+ puts "Waiting for #{options[:execution_time]}"
71
+ sleep options[:execution_time]
72
+
73
+ apprunner.kill_app(apk_name)
74
+ end
75
+
76
+ desc "run_apps_in_isolation APP_FOLDER","Runs the applications in the folder in an isolated manner"
77
+ option :execution_time, type: :numeric, default: 60, desc: "Time to execute app"
78
+ option :monkey_events, type: :numeric, default: 500, desc: "Number of monkey events to execute"
79
+ option :physical_device_ID, type: :string, default: nil, desc: "If it should be run in a physical device this field should be set with the device ID"
80
+ def run_apps_in_isolation(app_folder)
81
+
82
+ manager = nil
83
+ adb = nil
84
+ apprunner = nil
85
+ dedicated = false
86
+
87
+ if options[:physical_device_ID] == nil
88
+ array = Vbmanager.get_existing_pool_images
89
+
90
+ if array.size > 0
91
+ manager = Vbmanager.new(vm_image:options[:vm_image_name],reuse:true)
92
+ else
93
+ dedicated = true
94
+ manager = Vbmanager.new(vm_image:options[:vm_image_name])
95
+ manager.clone_vm
96
+ manager.configure_vm(accelerated:options[:accelerate3d])
97
+ manager.create_snapshot
98
+ manager.start_vm
99
+ end
100
+
101
+ adbrunner = Adbrunner.new(port:manager.port)
102
+ adbrunner.wait_for_boot
103
+ apprunner = Apprunner.new(adb:adbrunner)
104
+
105
+ else
106
+ manager = Devicemanager.new(id:options[:physical_device_ID])
107
+ adb = Adbrunner.new(id:options[:physical_device_ID])
108
+ apprunner = Apprunner.new(adb:adb)
109
+ end
110
+
111
+ Dir.glob("#{app_folder}/*.apk").each do |apk|
112
+
113
+ apk_name = /([\w\.]+).apk$/.match(app_name)[1]
114
+ puts "Running run_app_in_vm for application : #{apk_name}"
115
+
116
+ puts "Processing #{apk_name} in #{apk}..."
117
+
118
+ #Install and run app
119
+ apprunner.install_app app_name
120
+ apprunner.run_app(apk_name)
121
+ apprunner.monkey_app(apk_name, options[:monkey_events])
122
+
123
+ #puts "Waiting for #{options[:execution_time]}"
124
+ #sleep options[:execution_time]
125
+
126
+ #Clean VM/Device
127
+ apprunner.kill_app(apk_name)
128
+
129
+ end
130
+
131
+ #Revert VM state and stop VM
132
+ if options[:physical_device_ID] == nil
133
+ if dedicated
134
+ manager.stop_vm
135
+ manager.del_vm
136
+ else
137
+ manager.stop_vm
138
+ manager.load_snapshot
139
+ end
140
+ end
141
+
142
+
143
+ end
144
+
145
+ option :vm_image_name, type: :string, default: "AndroidX86"
146
+ option :number_of_vms, type: :numeric, default: 2
147
+ desc "allocate_vm_pool","Creates a pool of VMs. VMs are not launched, just created."
148
+ def allocate_vm_pool
149
+
150
+ array = Vbmanager.get_existing_pool_images
151
+ to_create = options[:number_of_vms] - array.size
152
+
153
+ if to_create <= 0
154
+ puts "Pool already contains #{array.size} VMs (requested VMS: #{options[:number_of_vms]})"
155
+ return
156
+ end
157
+
158
+ for i in 1..to_create
159
+ manager = Vbmanager.new(vm_image:options[:vm_image_name],is_pool:true)
160
+ #manager.import_ovf
161
+ manager.clone_vm
162
+ manager.configure_vm
163
+ manager.create_snapshot
164
+ end
165
+ Vbmanager.list_vms
166
+ end
167
+ end
168
+
169
+ #require 'droidrunner/apprunner'
@@ -0,0 +1,97 @@
1
+ require 'thor'
2
+
3
+ class Adbrunner #< Thor
4
+
5
+ def initialize(id:"localhost", port:nil)
6
+
7
+ @id = id
8
+ @port = port
9
+ if port != nil
10
+ @connect_id = "#{id}:#{port}"
11
+ else
12
+ raise "Attempt to create physical device adb without id!" if id == "localhost"
13
+ @connect_id = "#{id}"
14
+ end
15
+
16
+ puts "Initializing adb with #{@connect_id}"
17
+ end
18
+
19
+ #desc "kill","Kills the adb connection"
20
+ def kill(silent:false)
21
+ puts "Kill adb"
22
+ puts %x(adb kill-server)
23
+ end
24
+
25
+ #desc "connect","Connects adb to device"
26
+ def connect(silent:false)
27
+ puts "Connecting adb to #{@connect_id}" if !silent
28
+ res = %x(adb connect #{@connect_id})
29
+ puts res if !silent
30
+ end
31
+
32
+ #desc "install APP","Installs an application via adb"
33
+ def install(app)
34
+ puts "Installing #{app} adb to #{@connect_id}"
35
+ puts %x(adb -s #{@connect_id} install #{app})
36
+ end
37
+
38
+ #desc "shell CMD","Kills the adb connection"
39
+ def shell(cmd:,silent:false)
40
+ if !silent
41
+ puts "Executing shell command adb to #{@connect_id} -> #{cmd}"
42
+ ret= %x(adb -s #{@connect_id} shell #{cmd})
43
+ puts ret
44
+ else
45
+ ret = %x(adb -s #{@connect_id} shell #{cmd} 2>&1)
46
+ end
47
+
48
+ ret
49
+ end
50
+
51
+ def wait_for_boot
52
+ puts "Waiting for boot completion of #{@connect_id}..."
53
+ sl = true
54
+ while sl
55
+ connect(silent:true)
56
+ ret = shell(cmd:"getprop dev.bootcomplete", silent:true)
57
+
58
+ if ret.to_i == 1
59
+ sl = false
60
+ print "1..."
61
+ else
62
+ print "0..."
63
+ end
64
+
65
+ sleep 5
66
+ end
67
+ puts ""
68
+ end
69
+
70
+ def self.get_devices(silent:false)
71
+ array = Array.new
72
+ list = Adbrunner.devices(silent:true)
73
+ list.scan(/^([^\s]+)[\s]+[\w]+$/) { |id|
74
+ array.push(id[0])
75
+ }
76
+ puts array.inspect if !silent
77
+ array
78
+ end
79
+
80
+ def self.get_physical_devices(silent:false)
81
+ array = Adbrunner.get_devices
82
+ res = Array.new
83
+ array.each { |device|
84
+ next if device.include? ":"
85
+ res.push device
86
+ }
87
+ res
88
+ end
89
+
90
+ def self.devices(silent:false)
91
+ puts "Listing adb devices" if !silent
92
+ ret = %x(adb devices)
93
+ puts ret if !silent
94
+ ret
95
+ end
96
+
97
+ end
@@ -0,0 +1,44 @@
1
+ require 'thor'
2
+
3
+ class Apprunner #< Thor
4
+
5
+ def initialize(adb:nil, port:55551)
6
+ puts "Creating adb connection"
7
+ if adb == nil
8
+ @adbrunner = Adbrunner.new(port:port)
9
+ #@adbrunner.kill
10
+ @adbrunner.connect
11
+ else
12
+ @adbrunner = adb
13
+ end
14
+
15
+ Adbrunner.devices
16
+ end
17
+
18
+ #desc "install_app APPNAME","Installs an app in a VM"
19
+ def install_app(app)
20
+ puts "Installing app #{app}"
21
+ @adbrunner.install(app)
22
+ end
23
+
24
+ #desc "run_app APP_PACKAGE MAIN_ACTIVITY","Runs an app in a VM"
25
+ def run_app(app)
26
+ puts "Running app : #{app}"
27
+ #@adbrunner.shell(cmd:"am start -n #{app}/#{activity}")
28
+ #Running monkey with just one event is icon click (i.e. opening app)
29
+ @adbrunner.shell(cmd:"monkey -p #{app} -c android.intent.category.LAUNCHER 1")
30
+ end
31
+
32
+ def monkey_app(app,events)
33
+ puts "Running monkey #{events} events on #{app}"
34
+ @adbrunner.shell(cmd:"monkey -p #{app} -c android.intent.category.LAUNCHER -v #{events}")
35
+ end
36
+
37
+ #desc "kill_app APPNAME","Kills an app in a VM"
38
+ def kill_app(app)
39
+ puts "Killing app #{app}"
40
+ @adbrunner.shell(cmd:"am force-stop #{app}")
41
+ @adbrunner.shell(cmd:"pm clear #{app}")
42
+ end
43
+
44
+ end
@@ -0,0 +1,50 @@
1
+
2
+ class Devicemanager #< Thor
3
+
4
+ attr_accessor :device_id
5
+ @@registered_devices = Set.new
6
+ @@locked_devices = Array.new
7
+
8
+ def initialize(id:nil)
9
+
10
+ physical_devs = Adbrunner.get_physical_devices
11
+
12
+ if id == nil
13
+
14
+ #TODO: this is totally not process safe
15
+ available = @@registered_devices - @@locked_devices
16
+
17
+ raise "There were no physical devices registered" if available.size <= 0
18
+
19
+ available = available & physical_devs
20
+
21
+ raise "There were available registered devices but they were not connected!" if available.size <= 0
22
+
23
+ acquire_device(available[0])
24
+ @device_id = available[0]
25
+
26
+ else
27
+ available = [id] & physical_devs
28
+
29
+ raise "Device id was not connected!" if available.size <= 0
30
+
31
+ acquire_device(id)
32
+ @device_id = id
33
+ end
34
+
35
+ puts "Created a device manager for device id: #{@device_id}"
36
+ end
37
+
38
+ def register_device(id)
39
+ @@registered_devices.push id
40
+ end
41
+
42
+ def acquire_device(id)
43
+ @@locked_devices.push id #TODO: this will be in a sync db or something
44
+ end
45
+
46
+ def release_device(id)
47
+ @@locked_devices.delete id #TODO: this will be in a sync db or something
48
+ end
49
+
50
+ end
@@ -0,0 +1,146 @@
1
+ require 'thor'
2
+
3
+ class Vbmanager #< Thor
4
+
5
+ attr_accessor :port
6
+ @@current_id=1
7
+ @@locked_machines = Array.new
8
+ @@starting_id = 5555
9
+
10
+ def initialize(vm_image:nil,is_pool:false,reuse:false)
11
+ @vm_image=vm_image
12
+ @is_pool = is_pool
13
+
14
+ puts "Locked machines : #{@@locked_machines}"
15
+ running = Vbmanager.get_running_pool_images
16
+ puts "Running VMS : #{running.inspect}"
17
+ vms = Vbmanager.get_existing_pool_images
18
+ puts "Registered VMS : #{vms.inspect}"
19
+ not_running = vms - running
20
+ not_running -= @@locked_machines
21
+ puts "Available VMS : #{not_running.inspect}"
22
+
23
+ if vms.size != 0
24
+ max = vms.max
25
+ @@current_id = max + 1 if max >= @@current_id
26
+ end
27
+
28
+ puts "Setted current VM id to #{@@current_id}..."
29
+
30
+ if !reuse
31
+ @id = @@current_id
32
+ @@current_id +=1
33
+
34
+ @vm_clone = "#{@vm_image}-#{@id}" if !is_pool
35
+ @vm_clone = "#{@vm_image}-pool#{@id}" if is_pool
36
+
37
+ @@locked_machines.push(@id)
38
+ @configured = false
39
+ @is_reused = false
40
+ else
41
+ if not_running.size == 0
42
+ raise "No available machines for reuse!!!"
43
+ end
44
+
45
+ @is_reused = true
46
+ @id = not_running[0]
47
+ @vm_clone = "#{vm_image}-pool#{@id}"
48
+ @@locked_machines.push(@id)
49
+ @configured = true
50
+ end
51
+
52
+ @last_snap = "#{@vm_clone}-snap"
53
+ @port = @@starting_id + @id
54
+
55
+ puts "Setted ID to #{@id}"
56
+ puts "Setted VM Clone name to #{@vm_clone}..."
57
+ puts "Setted port to #{@port}"
58
+
59
+ end
60
+
61
+ #desc "list_vms","Lists Virtual Box VMs"
62
+ def self.list_vms(silent:false)
63
+ puts "Listing vms" if !silent
64
+ ret = %x(VBoxManage list vms)
65
+ puts ret if !silent
66
+ ret
67
+ end
68
+
69
+ def self.list_running_vms(silent:false)
70
+ puts "Listing running vms" if !silent
71
+ ret = %x(vboxmanage list runningvms)
72
+ puts ret if !silent
73
+ ret
74
+ end
75
+
76
+ def self.get_running_pool_images
77
+ ids = Array.new
78
+ list = list_running_vms(silent:true)
79
+ list.scan(/#{@vm_image}-pool([\d]+)/) { |id|
80
+ ids.push(id.to_i)
81
+ }
82
+ ids
83
+ end
84
+
85
+ def self.get_existing_pool_images
86
+ ids = Array.new
87
+ list = list_vms(silent:true)
88
+ list.scan(/#{@vm_image}-pool([\d]+)/) { |id|
89
+ ids.push(id[0].to_i)
90
+ }
91
+ ids
92
+ end
93
+
94
+ #desc "import_ovf VMNAME","Imports a VM image"
95
+ def import_ovf
96
+ puts "Importing vm image: #{@vm_image}"
97
+ puts %x(VBoxManage import #{@vm_image})
98
+ end
99
+
100
+ def configure_vm(accelerated:false, memory:1024)
101
+ puts "Setting Network... NAT"
102
+ puts %x(VBoxManage modifyvm #{@vm_clone} --nic1 nat)
103
+ puts "Setting adb port forwarding...#{@port}"
104
+ puts %x(VBoxManage modifyvm #{@vm_clone} --natpf1 #{@vm_clone}-adb,tcp,*,#{@port},*,5555)
105
+ puts "Setting 3D Acceleration... #{accelerated}"
106
+ puts %x(VBoxManage modifyvm #{@vm_clone} --accelerate3d off) if !accelerated
107
+ puts "Setting Memory... #{memory}"
108
+ puts %x(VBoxManage modifyvm #{@vm_clone} --memory #{memory})
109
+ @configured = true
110
+ end
111
+
112
+ def create_snapshot
113
+ puts "WARNING: #{@vm_clone} was not configured!" if !@configured
114
+ puts "Creating a snapshot #{@last_snap} for machine #{@vm_clone}"
115
+ puts %x(VBoxManage snapshot #{@vm_clone} take #{@last_snap})
116
+ end
117
+
118
+ def load_snapshot
119
+ puts "Loading snapshot #{@last_snap} for machine #{@vm_clone}"
120
+ puts %x(VBoxManage snapshot #{@vm_clone} restore #{@last_snap})
121
+ end
122
+
123
+ def clone_vm
124
+ puts "Cloning VM #{@vm_image} to #{@vm_clone}..."
125
+ puts %x(VBoxManage clonevm --mode machine #{@vm_image} --name #{@vm_clone} --register)
126
+ puts "Unlocking machine : #{@vm_clone}" if !@is_reused && @is_pool
127
+ @@locked_machines.delete(@id) if !@is_reused && @is_pool
128
+ end
129
+
130
+ #desc "start_vm","Starts a VM"
131
+ def start_vm(headless:true)
132
+ puts "WARNING: #{@vm_clone} was not configured!" if !@configured
133
+ puts "Starting VM #{@vm_clone}"
134
+ puts %x(VBoxManage startvm #{@vm_clone} --type headless) if headless
135
+ puts %x(VBoxManage startvm #{@vm_clone}) if !headless
136
+ end
137
+
138
+ def stop_vm
139
+ puts %x(VBoxManage controlvm #{@vm_clone} poweroff soft)
140
+ end
141
+
142
+ def del_vm
143
+ puts %x(VBoxManage unregistervm #{@vm_clone} --delete)
144
+ end
145
+
146
+ end
@@ -0,0 +1,27 @@
1
+ require 'test/unit'
2
+ require 'droidrunner'
3
+
4
+ class DroidrunnerTest < Test::Unit::TestCase
5
+ def test_adb
6
+ assert_nothing_raised( RuntimeError ) {
7
+ adb = Adbrunner.new("127.0.0.1")
8
+ adb.adb_kill
9
+ adb.adb_connect
10
+ adb.adb_install('test-app')
11
+ adb.adb_shell('cmd-test')
12
+ }
13
+ end
14
+
15
+ def test_apprunner
16
+ assert_nothing_raised( RuntimeError ) {
17
+ apprunner = Apprunner.new("127.0.0.1")
18
+ apprunner.install_app "test-app"
19
+ apprunner.run_app("test-app","test-activity")
20
+ apprunner.kill_app "test-app"
21
+ }
22
+ end
23
+
24
+ def test_vbmanager
25
+ #assert_equal "droidrunner mundo", Droidrunner.hi("spanish")
26
+ end
27
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: droidspecter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mario Almeida
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Droidspecter
14
+ email: 4knahs@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - Rakefile
20
+ - bin/droidrunner
21
+ - lib/droidrunner.rb
22
+ - lib/droidrunner/adbrunner.rb
23
+ - lib/droidrunner/apprunner.rb
24
+ - lib/droidrunner/devicemanager.rb
25
+ - lib/droidrunner/vbmanager.rb
26
+ - test/test_droidrunner.rb
27
+ homepage: http://rubygems.org/gems/droidrunner
28
+ licenses: []
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 2.4.3
47
+ signing_key:
48
+ specification_version: 3
49
+ summary: Droidrunner!
50
+ test_files:
51
+ - test/test_droidrunner.rb
52
+ has_rdoc: