replicant-adb 0.0.1 → 1.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 +7 -0
- data/README.md +32 -34
- data/lib/replicant.rb +3 -1
- data/lib/replicant/commands.rb +10 -0
- data/lib/replicant/commands/adb_command.rb +47 -0
- data/lib/replicant/commands/clear_command.rb +23 -0
- data/lib/replicant/commands/command.rb +85 -0
- data/lib/replicant/commands/device_command.rb +87 -0
- data/lib/replicant/commands/devices_command.rb +57 -0
- data/lib/replicant/commands/env_command.rb +18 -0
- data/lib/replicant/commands/list_command.rb +18 -0
- data/lib/replicant/commands/package_command.rb +19 -0
- data/lib/replicant/commands/reset_command.rb +16 -0
- data/lib/replicant/commands/restart_command.rb +13 -0
- data/lib/replicant/device.rb +7 -2
- data/lib/replicant/log_muncher.rb +83 -0
- data/lib/replicant/process_muncher.rb +47 -0
- data/lib/replicant/repl.rb +24 -9
- data/lib/replicant/styles.rb +24 -10
- data/lib/replicant/version.rb +1 -1
- metadata +30 -43
- data/.travis.yml +0 -4
- data/Gemfile +0 -19
- data/Rakefile +0 -40
- data/lib/replicant/command.rb +0 -352
- data/test/commands/adb_command_spec.rb +0 -58
- data/test/commands/command_spec.rb +0 -90
- data/test/commands/command_spec_base.rb +0 -30
- data/test/commands/device_command_spec.rb +0 -40
- data/test/commands/devices_command_spec.rb +0 -65
- data/test/commands/env_command_spec.rb +0 -30
- data/test/commands/list_command_spec.rb +0 -13
- data/test/commands/package_command_spec.rb +0 -34
- data/test/helper.rb +0 -16
data/.travis.yml
DELETED
data/Gemfile
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
source "http://rubygems.org"
|
2
|
-
# Add dependencies required to use your gem here.
|
3
|
-
# Example:
|
4
|
-
# gem "activesupport", ">= 2.3.5"
|
5
|
-
|
6
|
-
# Add dependencies to develop your gem here.
|
7
|
-
# Include everything needed to run rake, tests, features, etc.
|
8
|
-
gem "activesupport-core-ext", "~> 4.0", :require => false
|
9
|
-
|
10
|
-
group :development do
|
11
|
-
gem "rake", "~> 10.1"
|
12
|
-
gem "bundler", "~> 1.0"
|
13
|
-
gem "jeweler", "~> 1.8.7"
|
14
|
-
end
|
15
|
-
|
16
|
-
group :test do
|
17
|
-
gem "minitest", "~> 5.0", :require => 'minitest/autorun'
|
18
|
-
gem "mocha", "~> 0.14.0", :require => 'mocha/setup'
|
19
|
-
end
|
data/Rakefile
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'bundler'
|
5
|
-
require './lib/replicant/version'
|
6
|
-
|
7
|
-
begin
|
8
|
-
Bundler.setup(:default, :development)
|
9
|
-
rescue Bundler::BundlerError => e
|
10
|
-
$stderr.puts e.message
|
11
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
12
|
-
exit e.status_code
|
13
|
-
end
|
14
|
-
require 'rake'
|
15
|
-
|
16
|
-
require 'jeweler'
|
17
|
-
Jeweler::Tasks.new do |gem|
|
18
|
-
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
19
|
-
gem.name = "replicant-adb"
|
20
|
-
gem.version = Replicant::VERSION
|
21
|
-
gem.homepage = "https://github.com/mttkay/replicant"
|
22
|
-
gem.license = "MIT"
|
23
|
-
gem.summary = "A REPL for the Android Debug Bridge"
|
24
|
-
gem.description = "replicant is an interactive shell (a REPL) for ADB, the Android Debug Bridge"
|
25
|
-
gem.email = "m.kaeppler@gmail.com"
|
26
|
-
gem.authors = ["Matthias Kaeppler"]
|
27
|
-
gem.files.exclude 'screenshots/**'
|
28
|
-
# dependencies defined in Gemfile
|
29
|
-
end
|
30
|
-
Jeweler::RubygemsDotOrgTasks.new
|
31
|
-
|
32
|
-
require 'rake/testtask'
|
33
|
-
Rake::TestTask.new(:test) do |test|
|
34
|
-
test.libs << 'lib' << 'test'
|
35
|
-
test.pattern = 'test/**/*_spec.rb'
|
36
|
-
test.verbose = true
|
37
|
-
end
|
38
|
-
|
39
|
-
task :default => :test
|
40
|
-
|
data/lib/replicant/command.rb
DELETED
@@ -1,352 +0,0 @@
|
|
1
|
-
require 'stringio'
|
2
|
-
|
3
|
-
class Command
|
4
|
-
|
5
|
-
def self.inherited(subclass)
|
6
|
-
@@subclasses ||= []
|
7
|
-
@@subclasses << subclass
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.all
|
11
|
-
(@@subclasses - [AdbCommand, ListCommand, EnvCommand]).map do |clazz|
|
12
|
-
clazz.new(nil)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.load(repl, command_line)
|
17
|
-
if command_line == '!'
|
18
|
-
# load command that lists available commands
|
19
|
-
ListCommand.new(repl)
|
20
|
-
elsif command_line == '?'
|
21
|
-
EnvCommand.new(repl)
|
22
|
-
elsif command_line.start_with?('!')
|
23
|
-
# load custom command
|
24
|
-
command_parts = command_line[1..-1].split
|
25
|
-
command_name = command_parts.first
|
26
|
-
command_args = command_parts[1..-1].join(' ')
|
27
|
-
command_class = "#{command_name.capitalize}Command"
|
28
|
-
begin
|
29
|
-
clazz = Object.const_get(command_class)
|
30
|
-
clazz.new(repl, command_args)
|
31
|
-
rescue NameError => e
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
else
|
35
|
-
# forward command to ADB
|
36
|
-
AdbCommand.new(repl, command_line.strip)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
attr_reader :args
|
41
|
-
|
42
|
-
def initialize(repl, args = nil, options = {})
|
43
|
-
@repl = repl
|
44
|
-
@args = args.strip if args
|
45
|
-
@options = options
|
46
|
-
end
|
47
|
-
|
48
|
-
def name
|
49
|
-
"!#{self.class.name.gsub("Command", "").downcase}"
|
50
|
-
end
|
51
|
-
|
52
|
-
# subclasses override this to provide a description of their functionality
|
53
|
-
def description
|
54
|
-
"TODO: description missing"
|
55
|
-
end
|
56
|
-
|
57
|
-
# subclasses override this to provide a usage example
|
58
|
-
def usage
|
59
|
-
end
|
60
|
-
|
61
|
-
def execute
|
62
|
-
if valid_args?
|
63
|
-
run
|
64
|
-
else
|
65
|
-
output "Invalid arguments. Ex.: #{usage}"
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
def valid_args?
|
72
|
-
true
|
73
|
-
end
|
74
|
-
|
75
|
-
def output(message)
|
76
|
-
puts message unless @options[:silent]
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
class AdbCommand < Command
|
82
|
-
|
83
|
-
# the command line program
|
84
|
-
ADB = 'adb'
|
85
|
-
|
86
|
-
def run
|
87
|
-
begin
|
88
|
-
cmd = "#{adb} #{args}"
|
89
|
-
|
90
|
-
if interactive?
|
91
|
-
system cmd
|
92
|
-
else
|
93
|
-
cmd << " #{@repl.default_package}" if @repl.default_package && package_dependent?
|
94
|
-
output cmd if @repl.debug?
|
95
|
-
result = `#{cmd}`
|
96
|
-
output result
|
97
|
-
result
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
private
|
103
|
-
|
104
|
-
def adb
|
105
|
-
adb = "#{ADB}"
|
106
|
-
adb << " -s #{@repl.default_device.id}" if @repl.default_device
|
107
|
-
adb
|
108
|
-
end
|
109
|
-
|
110
|
-
def interactive?
|
111
|
-
args == "shell" || args.start_with?("logcat")
|
112
|
-
end
|
113
|
-
|
114
|
-
def package_dependent?
|
115
|
-
["uninstall"].include?(args)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
class DevicesCommand < Command
|
120
|
-
def description
|
121
|
-
"print a list of connected devices"
|
122
|
-
end
|
123
|
-
|
124
|
-
def run
|
125
|
-
adb = AdbCommand.new(@repl, "devices -l", :silent => true)
|
126
|
-
device_lines = adb.execute.lines.to_a.reject do |line|
|
127
|
-
line.strip.empty? || line.include?("daemon") || line.include?("List of devices")
|
128
|
-
end
|
129
|
-
|
130
|
-
device_ids = device_lines.map { |l| /([\S]+)\s+device/.match(l)[1] }
|
131
|
-
device_products = device_lines.map { |l| /product:([\S]+)/.match(l).try(:[], 1) }
|
132
|
-
|
133
|
-
device_names = device_lines.zip(device_ids).map do |l, id|
|
134
|
-
/model:([\S]+)/.match(l).try(:[], 1) || detect_device_name(id)
|
135
|
-
end
|
136
|
-
|
137
|
-
devices = device_ids.zip(device_names, device_products).map do |id, name, product|
|
138
|
-
Device.new(id, humanize_name(name, product))
|
139
|
-
end
|
140
|
-
|
141
|
-
output ""
|
142
|
-
output devices_string(devices)
|
143
|
-
output ""
|
144
|
-
devices
|
145
|
-
end
|
146
|
-
|
147
|
-
private
|
148
|
-
|
149
|
-
def detect_device_name(id)
|
150
|
-
if id.start_with?("emulator-")
|
151
|
-
"Android emulator"
|
152
|
-
else
|
153
|
-
"Unknown device"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def humanize_name(name_string, product)
|
158
|
-
if product == "vbox86p"
|
159
|
-
"Genymotion " + name_string.gsub(/___[\d_]+___/, "_")
|
160
|
-
else
|
161
|
-
name_string
|
162
|
-
end.gsub('_', ' ').squish
|
163
|
-
end
|
164
|
-
|
165
|
-
def devices_string(devices)
|
166
|
-
device_string = if devices.any?
|
167
|
-
padding = devices.map { |d| d.name.length }.max
|
168
|
-
indices = (0..devices.length - 1).to_a
|
169
|
-
indices.zip(devices).map { |i, d| "[#{i}] #{d.name}#{' ' * (padding - d.name.length)} | #{d.id}" }
|
170
|
-
else
|
171
|
-
"No devices found"
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
class PackageCommand < Command
|
177
|
-
|
178
|
-
def description
|
179
|
-
"set a default package to work with"
|
180
|
-
end
|
181
|
-
|
182
|
-
def usage
|
183
|
-
"#{name} com.mydomain.mypackage"
|
184
|
-
end
|
185
|
-
|
186
|
-
def valid_args?
|
187
|
-
args.present? && /^\w+(\.\w+)*$/ =~ args
|
188
|
-
end
|
189
|
-
|
190
|
-
def run
|
191
|
-
output "Setting default package to #{args.inspect}"
|
192
|
-
@repl.default_package = args
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
class DeviceCommand < Command
|
197
|
-
def description
|
198
|
-
"set a default device to work with"
|
199
|
-
end
|
200
|
-
|
201
|
-
def usage
|
202
|
-
"#{name} [<index>|<device_id>]"
|
203
|
-
end
|
204
|
-
|
205
|
-
def valid_args?
|
206
|
-
args.present? && /\S+/ =~ args
|
207
|
-
end
|
208
|
-
|
209
|
-
def run
|
210
|
-
default_device = if index?
|
211
|
-
# user selected by index
|
212
|
-
devices[args.to_i]
|
213
|
-
else
|
214
|
-
# user selected by device ID
|
215
|
-
devices.detect { |d| d.id == args }
|
216
|
-
end
|
217
|
-
|
218
|
-
if default_device
|
219
|
-
output "Setting default device to #{default_device.inspect}"
|
220
|
-
@repl.default_device = default_device
|
221
|
-
else
|
222
|
-
output "No such device"
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
private
|
227
|
-
|
228
|
-
def index?
|
229
|
-
/^\d+$/ =~ args
|
230
|
-
end
|
231
|
-
|
232
|
-
def devices
|
233
|
-
@devices ||= DevicesCommand.new(@repl, nil, :silent => true).execute
|
234
|
-
end
|
235
|
-
|
236
|
-
end
|
237
|
-
|
238
|
-
class ResetCommand < Command
|
239
|
-
|
240
|
-
def description
|
241
|
-
"clear current device and package"
|
242
|
-
end
|
243
|
-
|
244
|
-
def valid_args?
|
245
|
-
args.blank?
|
246
|
-
end
|
247
|
-
|
248
|
-
def run
|
249
|
-
@repl.default_device = nil
|
250
|
-
@repl.default_package = nil
|
251
|
-
end
|
252
|
-
|
253
|
-
end
|
254
|
-
|
255
|
-
class ListCommand < Command
|
256
|
-
def valid_args?
|
257
|
-
args.blank?
|
258
|
-
end
|
259
|
-
|
260
|
-
def description
|
261
|
-
"print a list of available commands"
|
262
|
-
end
|
263
|
-
|
264
|
-
def run
|
265
|
-
command_list = Command.all.sort_by {|c| c.name}.map do |command|
|
266
|
-
padding = 20 - command.name.length
|
267
|
-
desc = "#{command.name} #{' ' * padding} -- #{command.description}"
|
268
|
-
desc
|
269
|
-
end
|
270
|
-
output command_list.join("\n")
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
class RestartCommand < Command
|
275
|
-
def description
|
276
|
-
"restart ADB"
|
277
|
-
end
|
278
|
-
|
279
|
-
def run
|
280
|
-
# Faster than kill-server, and also catches ADB instances launched by
|
281
|
-
# IntelliJ. Moreover, start-server after kill-server sometimes makes the
|
282
|
-
# server fail to start up unless you sleep for a second or so
|
283
|
-
`killall adb`
|
284
|
-
AdbCommand.new(@repl, "start-server").execute
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
class LogcatCommand < Command
|
289
|
-
|
290
|
-
def description
|
291
|
-
"access device logs"
|
292
|
-
end
|
293
|
-
|
294
|
-
def valid_args?
|
295
|
-
args.blank?
|
296
|
-
end
|
297
|
-
|
298
|
-
def run
|
299
|
-
pid = if @repl.default_package
|
300
|
-
processes = AdbCommand.new(@repl, "shell ps", :silent => true).execute
|
301
|
-
pid_line = processes.lines.detect {|l| l.include?(@repl.default_package)}
|
302
|
-
pid_line.split[1].strip if pid_line
|
303
|
-
end
|
304
|
-
|
305
|
-
logcat = "logcat -v time"
|
306
|
-
logcat << " | grep -E '\(\s*#{pid}\)'"
|
307
|
-
AdbCommand.new(@repl, logcat).execute
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
class ClearCommand < Command
|
312
|
-
|
313
|
-
def description
|
314
|
-
"clear application data"
|
315
|
-
end
|
316
|
-
|
317
|
-
# TODO: this is not a very good argument validator
|
318
|
-
def valid_args?
|
319
|
-
args.present? || @repl.default_package
|
320
|
-
end
|
321
|
-
|
322
|
-
def usage
|
323
|
-
"#{name} [com.example.package|<empty>(when default package is set)]"
|
324
|
-
end
|
325
|
-
|
326
|
-
def run
|
327
|
-
package = args.present? ? args : @repl.default_package
|
328
|
-
# Clear app data - cache, SharedPreferences, Databases
|
329
|
-
AdbCommand.new(@repl, "shell su -c \"rm -r /data/data/#{package}/*\"").execute
|
330
|
-
# Force application stop to recreate shared preferences, databases with new launch
|
331
|
-
AdbCommand.new(@repl, "shell am force-stop #{package}").execute
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
class EnvCommand < Command
|
336
|
-
|
337
|
-
def valid_args?
|
338
|
-
args.blank?
|
339
|
-
end
|
340
|
-
|
341
|
-
def run
|
342
|
-
env = "Package: #{@repl.default_package || 'Not set'}\n"
|
343
|
-
env << "Device: "
|
344
|
-
device = @repl.default_device
|
345
|
-
env << if device
|
346
|
-
"#{device.name} (#{device.id})"
|
347
|
-
else
|
348
|
-
'Not set'
|
349
|
-
end
|
350
|
-
output env
|
351
|
-
end
|
352
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
|
3
|
-
class AdbCommandSpec < CommandSpecBase
|
4
|
-
|
5
|
-
describe "a basic adb command" do
|
6
|
-
before do
|
7
|
-
@command = silent AdbCommand.new(@repl, "devices")
|
8
|
-
@command.execute
|
9
|
-
end
|
10
|
-
|
11
|
-
it "sends a command to adb and captures the output" do
|
12
|
-
@command.backtick_capture.must_equal "adb devices"
|
13
|
-
end
|
14
|
-
|
15
|
-
it "does not use Kernel#system" do
|
16
|
-
@command.system_capture.must_be_nil
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe "an interactive command" do
|
21
|
-
before do
|
22
|
-
@command = AdbCommand.new(@repl, "shell")
|
23
|
-
@command.execute
|
24
|
-
end
|
25
|
-
|
26
|
-
it "is executed using a Kernel#system call" do
|
27
|
-
@command.system_capture.must_equal "adb shell"
|
28
|
-
end
|
29
|
-
|
30
|
-
describe "when it's 'shell'" do
|
31
|
-
it "is not treated as interactive when arguments are present" do
|
32
|
-
command = AdbCommand.new(@repl, "shell ps")
|
33
|
-
command.execute
|
34
|
-
command.system_capture.must_be_nil
|
35
|
-
command.backtick_capture.must_equal "adb shell ps"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe "with a default package set" do
|
41
|
-
before do
|
42
|
-
@repl.stubs(:default_package).returns("com.myapp")
|
43
|
-
end
|
44
|
-
|
45
|
-
it "does not set the default package if command is not package dependent" do
|
46
|
-
command = silent AdbCommand.new(@repl, "devices")
|
47
|
-
command.execute
|
48
|
-
command.backtick_capture.must_equal "adb devices"
|
49
|
-
end
|
50
|
-
|
51
|
-
it "adds the default package if command is package dependent" do
|
52
|
-
command = silent AdbCommand.new(@repl, "uninstall")
|
53
|
-
command.execute
|
54
|
-
command.backtick_capture.must_equal "adb uninstall com.myapp"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|