appium_instrumenter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,539 @@
1
+ require 'rexml/document'
2
+ require 'timeout'
3
+ require 'luffa'
4
+
5
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
6
+ require 'win32/registry'
7
+ end
8
+
9
+ module Calabash
10
+ module Android
11
+ module Dependencies
12
+ private
13
+
14
+ class ScanningTimedOutError < RuntimeError; end
15
+
16
+ def self.set_android_dependencies(android_dependencies)
17
+ @@android_dependencies = android_dependencies
18
+ end
19
+
20
+ def self.set_java_dependencies(java_dependencies)
21
+ @@java_dependencies = java_dependencies
22
+ end
23
+
24
+ def self.android_dependencies(key)
25
+ setup unless defined?(@@android_dependencies)
26
+
27
+ if @@android_dependencies.has_key?(key)
28
+ file = @@android_dependencies[key]
29
+
30
+ unless File.exists?(file)
31
+ raise "No such file '#{file}'"
32
+ end
33
+
34
+ file
35
+ else
36
+ raise "No such dependency '#{key}'"
37
+ end
38
+ end
39
+
40
+ def self.java_dependencies(key)
41
+ setup unless defined?(@@java_dependencies)
42
+
43
+ if key == :ant_path
44
+ ant_executable
45
+ elsif @@java_dependencies.has_key?(key)
46
+ file = @@java_dependencies[key]
47
+
48
+ unless File.exists?(file)
49
+ raise "No such file '#{file}'"
50
+ end
51
+
52
+ file
53
+ else
54
+ raise "No such dependency '#{key}'"
55
+ end
56
+ end
57
+
58
+ public
59
+
60
+ def self.adb_path
61
+ android_dependencies(:adb_path)
62
+ end
63
+
64
+ def self.aapt_path
65
+ android_dependencies(:aapt_path)
66
+ end
67
+
68
+ def self.zipalign_path
69
+ android_dependencies(:zipalign_path)
70
+ end
71
+
72
+ def self.android_jar_path
73
+ android_dependencies(:android_jar_path)
74
+ end
75
+
76
+ def self.java_path
77
+ java_dependencies(:java_path)
78
+ end
79
+
80
+ def self.keytool_path
81
+ java_dependencies(:keytool_path)
82
+ end
83
+
84
+ def self.jarsigner_path
85
+ java_dependencies(:jarsigner_path)
86
+ end
87
+
88
+ def self.ant_path
89
+ java_dependencies(:ant_path)
90
+ end
91
+
92
+ def self.setup
93
+ if ENV['CI_NO_ANDROID_RUNTIME'] == '1'
94
+ @@android_dependencies = {}
95
+ @@java_dependencies = {}
96
+ return
97
+ end
98
+
99
+ @@halt_scanning = false
100
+ @@halt_scanning_thread = nil
101
+
102
+ if ENV['ANDROID_HOME']
103
+ android_sdk_location = ENV['ANDROID_HOME']
104
+ p ("Setting Android SDK location to $ANDROID_HOME")
105
+ else
106
+ android_sdk_location = detect_android_sdk_location
107
+ end
108
+
109
+ if android_sdk_location.nil?
110
+ p 'Could not find an Android SDK please make sure it is installed.'
111
+ p 'You can read about how Calabash is searching for an Android SDK and how you can help here:'
112
+ p 'https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites'
113
+
114
+ raise 'Could not find an Android SDK'
115
+ end
116
+
117
+ p ("Android SDK location set to '#{android_sdk_location}'")
118
+
119
+ @@halt_scanning_thread = Thread.new do
120
+ sleep 60
121
+ @@halt_scanning = true
122
+ end
123
+
124
+ begin
125
+ set_android_dependencies(locate_android_dependencies(android_sdk_location))
126
+ rescue ScanningTimedOutError => e
127
+ p 'Timed out locating Android dependency'
128
+ p 'You can read about how Calabash is searching for an Android SDK and how you can help here:'
129
+ p 'https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites'
130
+
131
+ raise e.message
132
+ rescue Environment::InvalidEnvironmentError => e
133
+ p 'Could not locate Android dependency'
134
+ p 'You can read about how Calabash is searching for an Android SDK and how you can help here:'
135
+ p 'https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites'
136
+
137
+ raise e
138
+ end
139
+
140
+ if ENV['JAVA_HOME']
141
+ java_sdk_home = ENV['JAVA_HOME']
142
+ p ("Setting Java SDK location to $JAVA_HOME")
143
+ else
144
+ java_sdk_home = detect_java_sdk_location
145
+ end
146
+
147
+ p ("Java SDK location set to '#{java_sdk_home}'")
148
+
149
+ Thread.kill(@@halt_scanning_thread) if @@halt_scanning_thread
150
+ @@halt_scanning = false
151
+
152
+ @@halt_scanning_thread = Thread.new do
153
+ sleep 60
154
+ @@halt_scanning = true
155
+ end
156
+
157
+ begin
158
+ set_java_dependencies(locate_java_dependencies(java_sdk_home))
159
+ rescue ScanningTimedOutError => e
160
+ p 'Timed out locating Java dependency'
161
+ p "You can read about how Calabash is searching for a JDK and how you can help here:"
162
+ p "https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites"
163
+
164
+ raise e.message
165
+ rescue Environment::InvalidJavaSDKHome => e
166
+ p "Could not find Java Development Kit please make sure it is installed."
167
+ p "You can read about how Calabash is searching for a JDK and how you can help here:"
168
+ p "https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites"
169
+
170
+ raise e
171
+ rescue Environment::InvalidEnvironmentError => e
172
+ p "Could not find Java dependency"
173
+ p "You can read about how Calabash is searching for a JDK and how you can help here:"
174
+ p "https://github.com/calabash/calabash-android/blob/master/documentation/installation.md#prerequisites"
175
+
176
+ raise e
177
+ end
178
+
179
+ Thread.kill(@@halt_scanning_thread) if @@halt_scanning_thread
180
+ end
181
+
182
+ private
183
+
184
+ def self.tools_directory
185
+ tools_directories = tools_directories(ENV['ANDROID_HOME'])
186
+
187
+ File.join(ENV['ANDROID_HOME'], tools_directories.first)
188
+ end
189
+
190
+ def self.tools_directories(android_sdk_location)
191
+ build_tools_files = list_files(File.join(android_sdk_location, 'build-tools')).select {|file| File.directory?(file)}
192
+
193
+ build_tools_directories =
194
+ build_tools_files.select do |dir|
195
+ begin
196
+ Luffa::Version.new(File.basename(dir))
197
+ true
198
+ rescue ArgumentError
199
+ false
200
+ end
201
+ end.sort do |a, b|
202
+ Luffa::Version.compare(Luffa::Version.new(File.basename(a)), Luffa::Version.new(File.basename(b)))
203
+ end.reverse.map{|dir| File.join('build-tools', File.basename(dir))}
204
+
205
+ if build_tools_directories.empty?
206
+ unless build_tools_files.reverse.first.nil?
207
+ build_tools_directories = [File.join('build-tools', File.basename(build_tools_files.reverse.first))]
208
+ end
209
+ end
210
+
211
+ build_tools_directories + ['platform-tools', 'tools']
212
+ end
213
+
214
+ def self.platform_directory(android_sdk_location)
215
+ files = list_files(File.join(android_sdk_location, 'platforms'))
216
+ .select {|file| File.directory?(file)}
217
+
218
+ sorted_files = files.sort_by {|item| '%08s' % item.split('-').last}.reverse
219
+
220
+ if sorted_files.first.nil?
221
+ raise Environment::InvalidEnvironmentError,
222
+ "Could not find any platform directory in '#{File.join(android_sdk_location, 'platforms')}'"
223
+ end
224
+
225
+ File.join('platforms', File.basename(sorted_files.first))
226
+ end
227
+
228
+ def self.locate_android_dependencies(android_sdk_location)
229
+ adb_path = scan_for_path(android_sdk_location, adb_executable, ['platform-tools'])
230
+ aapt_path = scan_for_path(android_sdk_location, aapt_executable, tools_directories(android_sdk_location))
231
+ zipalign_path = scan_for_path(android_sdk_location, zipalign_executable, tools_directories(android_sdk_location))
232
+
233
+ if adb_path.nil?
234
+ raise Environment::InvalidEnvironmentError,
235
+ "Could not find '#{adb_executable}' in '#{android_sdk_location}'"
236
+ end
237
+
238
+ if aapt_path.nil?
239
+ raise Environment::InvalidEnvironmentError,
240
+ "Could not find '#{aapt_executable}' in '#{android_sdk_location}'"
241
+ end
242
+
243
+ if zipalign_path.nil?
244
+ raise Environment::InvalidEnvironmentError,
245
+ "Could not find '#{zipalign_executable}' in '#{android_sdk_location}'"
246
+ end
247
+
248
+ p ("Set aapt path to '#{aapt_path}'")
249
+ p ("Set zipalign path to '#{zipalign_path}'")
250
+ p ("Set adb path to '#{adb_path}'")
251
+
252
+ android_jar_path = scan_for_path(File.join(android_sdk_location, 'platforms'), 'android.jar', [File.basename(platform_directory(android_sdk_location))])
253
+
254
+ if android_jar_path.nil?
255
+ raise Environment::InvalidEnvironmentError,
256
+ "Could not find 'android.jar' in '#{File.join(android_sdk_location, 'platforms')}'"
257
+ end
258
+
259
+ p ("Set android jar path to '#{android_jar_path}'")
260
+
261
+ {
262
+ aapt_path: aapt_path,
263
+ zipalign_path: zipalign_path,
264
+ adb_path: adb_path,
265
+ android_jar_path: android_jar_path
266
+ }
267
+ end
268
+
269
+ def self.locate_java_dependencies(java_sdk_location)
270
+ # For the Java dependencies, we will use the PATH elements of they exist
271
+ on_path = find_executable_on_path(java_executable)
272
+
273
+ if on_path
274
+ p ('Found java on PATH')
275
+ java_path = on_path
276
+ else
277
+ if java_sdk_location.nil? || java_sdk_location.empty?
278
+ raise Environment::InvalidJavaSDKHome,
279
+ "Could not locate '#{java_executable}' on path, and Java SDK Home is invalid."
280
+ end
281
+
282
+ java_path = scan_for_path(java_sdk_location, java_executable, ['bin'])
283
+ end
284
+
285
+ p ("Set java path to '#{java_path}'")
286
+
287
+ on_path = find_executable_on_path(keytool_executable)
288
+
289
+ if on_path
290
+ p ('Found keytool on PATH')
291
+ keytool_path = on_path
292
+ else
293
+ if java_sdk_location.nil? || java_sdk_location.empty?
294
+ raise Environment::InvalidJavaSDKHome,
295
+ "Could not locate '#{keytool_executable}' on path, and Java SDK Home is invalid."
296
+ end
297
+
298
+ keytool_path = scan_for_path(java_sdk_location, keytool_executable, ['bin'])
299
+ end
300
+
301
+ p ("Set keytool path to '#{keytool_path}'")
302
+
303
+ on_path = find_executable_on_path(jarsigner_executable)
304
+
305
+ if on_path
306
+ p ('Found jarsigner on PATH')
307
+ jarsigner_path = on_path
308
+ else
309
+ if java_sdk_location.nil? || java_sdk_location.empty?
310
+ raise Environment::InvalidJavaSDKHome,
311
+ "Could not locate '#{jarsigner_executable}' on path, and Java SDK Home is invalid."
312
+ end
313
+
314
+ jarsigner_path = scan_for_path(java_sdk_location, jarsigner_executable, ['bin'])
315
+ end
316
+
317
+ p ("Set jarsigner path to '#{jarsigner_path}'")
318
+
319
+ if java_path.nil?
320
+ raise Environment::InvalidEnvironmentError,
321
+ "Could not find '#{java_executable}' on PATH or in '#{java_sdk_location}'"
322
+ end
323
+
324
+ if keytool_path.nil?
325
+ raise Environment::InvalidEnvironmentError,
326
+ "Could not find '#{keytool_executable}' on PATH or in '#{java_sdk_location}'"
327
+ end
328
+
329
+ if jarsigner_path.nil?
330
+ raise Environment::InvalidEnvironmentError,
331
+ "Could not find '#{jarsigner_executable}' on PATH or in '#{java_sdk_location}'"
332
+ end
333
+
334
+ {
335
+ java_path: java_path,
336
+ keytool_path: keytool_path,
337
+ jarsigner_path: jarsigner_path
338
+ }
339
+ end
340
+
341
+ def self.halt_scanning?
342
+ @@halt_scanning
343
+ end
344
+
345
+ def self.scan_for_path(path, file_name, expected_sub_folders = nil)
346
+ if self.halt_scanning?
347
+ p ("Timed out looking for '#{file_name}', currently looking in '#{path}'")
348
+ raise ScanningTimedOutError, "Timed out looking for '#{file_name}'"
349
+ end
350
+
351
+ # Optimization for expected folders
352
+ if expected_sub_folders && !expected_sub_folders.empty?
353
+ expected_sub_folders.each do |expected_sub_folder|
354
+ result = scan_for_path(File.join(path, expected_sub_folder), file_name)
355
+
356
+ return result if result
357
+ end
358
+
359
+ p ("Did not find '#{file_name}' in any standard directory of '#{path}'. Calabash will therefore take longer to load")
360
+ p (" - Expected to find '#{file_name}' in any of:")
361
+
362
+ expected_sub_folders.each do |expected_sub_folder|
363
+ p (" - #{File.join(path, expected_sub_folder)}")
364
+ end
365
+ end
366
+
367
+ files = list_files(path).sort.reverse
368
+
369
+ if files.reject{|file| File.directory?(file)}.
370
+ map{|file| File.basename(file)}.include?(file_name)
371
+ return File.join(path, file_name)
372
+ else
373
+ files.select{|file| File.directory?(file)}.each do |dir|
374
+ result = scan_for_path(dir, file_name)
375
+
376
+ return result if result
377
+ end
378
+ end
379
+
380
+ nil
381
+ end
382
+
383
+ def self.detect_android_sdk_location
384
+ if File.exist?(monodroid_config_file)
385
+ sdk_location = read_attribute_from_monodroid_config('android-sdk', 'path')
386
+
387
+ if sdk_location
388
+ p ("Setting Android SDK location from '#{monodroid_config_file}'")
389
+
390
+ return sdk_location
391
+ end
392
+ end
393
+
394
+ if File.exist?(File.expand_path('~/Library/Developer/Xamarin/android-sdk-mac_x86/'))
395
+ return File.expand_path('~/Library/Developer/Xamarin/android-sdk-mac_x86/')
396
+ end
397
+
398
+ if File.exist?(File.expand_path('~/Library/Developer/Xamarin/android-sdk-macosx/'))
399
+ return File.expand_path('~/Library/Developer/Xamarin/android-sdk-macosx/')
400
+ end
401
+
402
+ # Default location when installing with Android Studio
403
+ if File.exist?(File.expand_path('~/Library/Android/sdk/'))
404
+ return File.expand_path('~/Library/Android/sdk/')
405
+ end
406
+
407
+ if File.exist?('C:\\Android\\android-sdk')
408
+ return 'C:\\Android\\android-sdk'
409
+ end
410
+
411
+ if is_windows?
412
+ from_registry = read_registry(::Win32::Registry::HKEY_CURRENT_USER, "Software\\Novell\\Mono for Android", 'AndroidSdkDirectory')
413
+
414
+ if from_registry && File.exist?(from_registry)
415
+ p ("Setting Android SDK location from HKEY_CURRENT_USER Software\\Novell\\Mono for Android")
416
+ return from_registry
417
+ end
418
+
419
+ from_registry = read_registry(::Win32::Registry::HKEY_LOCAL_MACHINE, 'Software\\Android SDK Tools', 'Path')
420
+
421
+ if from_registry && File.exist?(from_registry)
422
+ p ("Setting Android SDK location from HKEY_LOCAL_MACHINE Software\\Android SDK Tools")
423
+ return from_registry
424
+ end
425
+ end
426
+
427
+ nil
428
+ end
429
+
430
+ def self.detect_java_sdk_location
431
+ if File.exist?(monodroid_config_file)
432
+ sdk_location = read_attribute_from_monodroid_config('java-sdk', 'path')
433
+
434
+ if sdk_location
435
+ p ("Setting Java SDK location from '#{monodroid_config_file}'")
436
+
437
+ return sdk_location
438
+ end
439
+ end
440
+
441
+ java_versions = ['1.9', '1.8', '1.7', '1.6']
442
+
443
+ if is_windows?
444
+ java_versions.each do |java_version|
445
+ key = "SOFTWARE\\JavaSoft\\Java Development Kit\\#{java_version}"
446
+ from_registry = read_registry(::Win32::Registry::HKEY_LOCAL_MACHINE, key, 'JavaHome')
447
+
448
+ if from_registry && File.exist?(from_registry)
449
+ p ("Setting Java SDK location from HKEY_LOCAL_MACHINE #{key}")
450
+ return from_registry
451
+ end
452
+ end
453
+ end
454
+
455
+ nil
456
+ end
457
+
458
+ def self.monodroid_config_file
459
+ File.expand_path('~/.config/xbuild/monodroid-config.xml')
460
+ end
461
+
462
+ def self.read_attribute_from_monodroid_config(element, attribute)
463
+ element = REXML::Document.new(IO.read(monodroid_config_file)).elements["//#{element}"]
464
+
465
+ if element
466
+ element.attributes[attribute]
467
+ else
468
+ nil
469
+ end
470
+ end
471
+
472
+ def self.find_executable_on_path(executable)
473
+ path_elements.each do |x|
474
+ f = File.join(x, executable)
475
+ return f if File.exists?(f)
476
+ end
477
+
478
+ nil
479
+ end
480
+
481
+ def self.path_elements
482
+ return [] unless ENV['PATH']
483
+ ENV['PATH'].split (/[:;]/)
484
+ end
485
+
486
+ def self.zipalign_executable
487
+ is_windows? ? 'zipalign.exe' : 'zipalign'
488
+ end
489
+
490
+ def self.jarsigner_executable
491
+ is_windows? ? 'jarsigner.exe' : 'jarsigner'
492
+ end
493
+
494
+ def self.java_executable
495
+ is_windows? ? 'java.exe' : 'java'
496
+ end
497
+
498
+ def self.keytool_executable
499
+ is_windows? ? 'keytool.exe' : 'keytool'
500
+ end
501
+
502
+ def self.adb_executable
503
+ is_windows? ? 'adb.exe' : 'adb'
504
+ end
505
+
506
+ def self.aapt_executable
507
+ is_windows? ? 'aapt.exe' : 'aapt'
508
+ end
509
+
510
+ def self.ant_executable
511
+ is_windows? ? 'ant.exe' : 'ant'
512
+ end
513
+
514
+ def self.is_windows?
515
+ (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
516
+ end
517
+
518
+ def self.read_registry(root_key, key, value)
519
+ begin
520
+ root_key.open(key)[value]
521
+ rescue
522
+ nil
523
+ end
524
+ end
525
+
526
+ def self.list_files(path)
527
+ # Dir.glob does not accept backslashes, even on windows. We have to
528
+ # substitute all backwards slashes to forward.
529
+ # C:\foo becomes C:/foo
530
+
531
+ if is_windows?
532
+ Dir.glob(File.join(path, '*').gsub('\\', '/'))
533
+ else
534
+ Dir.glob(File.join(path, '*'))
535
+ end
536
+ end
537
+ end
538
+ end
539
+ end