kraken-mobile 1.0.4 → 1.0.5

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -11
  3. data/bin/kraken-mobile +55 -67
  4. data/bin/kraken_mobile_calabash_android.rb +39 -0
  5. data/bin/kraken_mobile_helpers.rb +91 -0
  6. data/bin/kraken_mobile_setup.rb +134 -0
  7. data/calabash-android-features-skeleton/step_definitions/mobile_steps.rb +1 -0
  8. data/calabash-android-features-skeleton/support/app_installation_hooks.rb +2 -0
  9. data/calabash-android-features-skeleton/support/app_life_cycle_hooks.rb +3 -4
  10. data/calabash-android-features-skeleton/support/env.rb +1 -1
  11. data/calabash-android-features-skeleton/web/step_definitions/web_steps.rb +3 -0
  12. data/calabash-android-features-skeleton/web/support/app_life_cycle_hooks.rb +15 -0
  13. data/lib/kraken-mobile/device_process.rb +94 -0
  14. data/lib/kraken-mobile/helpers/devices_helper/adb_helper.rb +100 -105
  15. data/lib/kraken-mobile/helpers/kraken_faker.rb +107 -0
  16. data/lib/kraken-mobile/hooks/mobile_kraken_hooks.rb +15 -0
  17. data/lib/kraken-mobile/hooks/mobile_operations.rb +36 -0
  18. data/lib/kraken-mobile/hooks/web_operations.rb +33 -0
  19. data/lib/kraken-mobile/mobile/adb.rb +66 -0
  20. data/lib/kraken-mobile/mobile/android_commands.rb +43 -0
  21. data/lib/kraken-mobile/mobile/mobile_process.rb +82 -0
  22. data/lib/kraken-mobile/models/android_device.rb +121 -0
  23. data/lib/kraken-mobile/models/device.rb +113 -31
  24. data/lib/kraken-mobile/models/feature_file.rb +108 -0
  25. data/lib/kraken-mobile/models/feature_scenario.rb +24 -0
  26. data/lib/kraken-mobile/models/web_device.rb +86 -0
  27. data/lib/kraken-mobile/monkeys/mobile/android_monkey.rb +30 -0
  28. data/lib/kraken-mobile/monkeys/mobile/kraken_android_monkey.rb +54 -0
  29. data/lib/kraken-mobile/runners/calabash/android/steps/communication_steps.rb +15 -0
  30. data/lib/kraken-mobile/steps/general_steps.rb +82 -0
  31. data/lib/kraken-mobile/steps/mobile/kraken_steps.rb +72 -0
  32. data/lib/kraken-mobile/steps/web/kraken_steps.rb +81 -0
  33. data/lib/kraken-mobile/test_scenario.rb +193 -0
  34. data/lib/kraken-mobile/utils/feature_reader.rb +17 -0
  35. data/lib/kraken-mobile/utils/k.rb +67 -0
  36. data/lib/kraken-mobile/utils/mobile_cucumber.rb +2 -0
  37. data/lib/kraken-mobile/utils/reporter.rb +453 -0
  38. data/lib/kraken-mobile/version.rb +2 -2
  39. data/lib/kraken-mobile/web/web_process.rb +39 -0
  40. data/lib/kraken_mobile.rb +77 -0
  41. data/reporter/index.html.erb +6 -6
  42. metadata +89 -9
  43. data/bin/kraken-mobile-calabash-android.rb +0 -85
  44. data/bin/kraken-mobile-generate.rb +0 -19
  45. data/bin/kraken-mobile-helpers.rb +0 -48
  46. data/bin/kraken-mobile-setup.rb +0 -50
  47. data/calabash-android-features-skeleton/step_definitions/kraken_steps.rb +0 -1
  48. data/lib/kraken-mobile.rb +0 -29
@@ -0,0 +1,15 @@
1
+ require 'calabash-android/management/adb'
2
+ require 'calabash-android/operations'
3
+
4
+ Before do |scenario|
5
+ # install_app_with_calabash
6
+ @scenario_tags = scenario.source_tag_names
7
+ end
8
+
9
+ After do |scenario|
10
+ @scenario_tags = scenario.source_tag_names
11
+ end
12
+
13
+ AfterStep do |_scenario|
14
+ screenshot_embed
15
+ end
@@ -0,0 +1,36 @@
1
+ require 'calabash-android/management/adb'
2
+ require 'calabash-android/operations'
3
+ require 'kraken-mobile/test_scenario'
4
+
5
+ def start_test_kraken_server_in_background
6
+ start_test_server_in_background
7
+
8
+ DeviceProcess.notify_process_state(
9
+ process_id: process_id,
10
+ state: K::PROCESS_STATES[:ready_to_start]
11
+ )
12
+
13
+ Timeout.timeout(K::DEFAULT_START_TIMEOUT_SECONDS, RuntimeError) do
14
+ sleep(1) until TestScenario.ready_to_start?
15
+ end
16
+ end
17
+
18
+ def shutdown_test_kraken_server
19
+ DeviceProcess.notify_process_state(
20
+ process_id: process_id,
21
+ state: K::PROCESS_STATES[:ready_to_finish]
22
+ )
23
+
24
+ Timeout.timeout(K::DEFAULT_FINISH_TIMEOUT_SECONDS, RuntimeError) do
25
+ sleep(1) until TestScenario.ready_to_finish?
26
+ end
27
+
28
+ shutdown_test_server
29
+ end
30
+
31
+ private
32
+
33
+ def process_id
34
+ tag_process_id = @scenario_tags.grep(/@user/).first
35
+ tag_process_id.delete_prefix('@user')
36
+ end
@@ -0,0 +1,33 @@
1
+ require 'kraken-mobile/test_scenario'
2
+
3
+ def start_test_kraken_server_in_background(scenario:)
4
+ @scenario_tags = scenario.source_tag_names
5
+ DeviceProcess.notify_process_state(
6
+ process_id: process_id(scenario: scenario),
7
+ state: K::PROCESS_STATES[:ready_to_start]
8
+ )
9
+
10
+ Timeout.timeout(K::DEFAULT_START_TIMEOUT_SECONDS, RuntimeError) do
11
+ sleep(1) until TestScenario.ready_to_start?
12
+ end
13
+ end
14
+
15
+ def shutdown_test_kraken_server(scenario:)
16
+ @scenario_tags = scenario.source_tag_names
17
+ DeviceProcess.notify_process_state(
18
+ process_id: process_id(scenario: scenario),
19
+ state: K::PROCESS_STATES[:ready_to_finish]
20
+ )
21
+
22
+ Timeout.timeout(K::DEFAULT_FINISH_TIMEOUT_SECONDS, RuntimeError) do
23
+ sleep(1) until TestScenario.ready_to_finish?
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def process_id(scenario:)
30
+ scenario_tags = scenario.source_tag_names
31
+ tag_process_id = scenario_tags.grep(/@user/).first
32
+ tag_process_id.delete_prefix('@user')
33
+ end
@@ -0,0 +1,66 @@
1
+ require 'kraken-mobile/models/android_device'
2
+ require 'kraken-mobile/constants'
3
+ require 'kraken-mobile/mobile/android_commands.rb'
4
+
5
+ class ADB
6
+ class << self
7
+ include Android::Commands
8
+
9
+ def connected_devices
10
+ devices = []
11
+ adb_devices_l.split("\n").each do |line|
12
+ id = extract_device_id(line)
13
+ model = extract_device_model(line)
14
+ next if id.nil? || model.nil?
15
+
16
+ devices << AndroidDevice.new(id: id, model: model)
17
+ end
18
+ devices
19
+ rescue StandardError => _e
20
+ raise 'ERROR: Can\'t read Android devices connected.'
21
+ end
22
+
23
+ def device_screen_size(device_id:)
24
+ adb_size = screen_size_for_device_with_id(device_id: device_id)
25
+ extract_device_screen_size_info(adb_size)
26
+ rescue StandardError => _e
27
+ raise "ERROR: Can\'t read Android device #{device_id} screen size."
28
+ end
29
+
30
+ def save_snapshot_for_device_with_id_in_file_path(device_id:, file_path:)
31
+ save_snapshot_for_device_with_id_in_path(
32
+ device_id: device_id,
33
+ file_path: file_path
34
+ )
35
+ rescue StandardError => _e
36
+ raise "ERROR: Can\'t save snapshot for device #{device_id}."
37
+ end
38
+
39
+ def device_sdk_version(device_id:)
40
+ version = sdk_version_for_device_with_id(device_id: device_id)
41
+ version.strip
42
+ rescue StandardError => _e
43
+ raise "ERROR: Can\'t get SDK version for device #{device_id}."
44
+ end
45
+
46
+ private
47
+
48
+ def extract_device_id(line)
49
+ return line.split(' ').first if line.match(/device(?!s)/)
50
+ end
51
+
52
+ def extract_device_model(line)
53
+ return unless line.match(/device(?!s)/)
54
+
55
+ line.scan(/model:(.*) device/).flatten.first
56
+ end
57
+
58
+ def extract_device_screen_size_info(line)
59
+ parts = line.strip!.split(' ')
60
+ size = parts[parts.count - 1]
61
+ return [0, 0] unless size.include?('x')
62
+
63
+ size.split('x').map(&:to_i)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,43 @@
1
+ module Android
2
+ module Commands
3
+ # List of connected devices/emulators
4
+ def adb_devices_l
5
+ `adb devices -l`
6
+ end
7
+
8
+ def file_content(device_id:, file_name:)
9
+ `adb -s #{device_id} shell "cat /sdcard/#{file_name} 2> /dev/null"`
10
+ end
11
+
12
+ def write_content_to_file_with_name_in_device(
13
+ content:, device_id:, file_name:
14
+ )
15
+ `adb -s #{device_id} shell "echo "#{content}" > /sdcard/#{file_name}"`
16
+ end
17
+
18
+ def create_file_with_name_in_device(device_id:, file_name:)
19
+ `adb -s #{device_id} shell "> /sdcard/#{file_name}"`
20
+ end
21
+
22
+ def delete_file_with_name_in_device(device_id:, file_name:)
23
+ `adb -s #{device_id} shell "rm -rf /sdcard/#{file_name}"`
24
+ end
25
+
26
+ def device_orientation(device_id:)
27
+ `adb -s #{device_id} shell dumpsys input | grep 'SurfaceOrientation' \
28
+ | awk '{ print $2 }'`
29
+ end
30
+
31
+ def screen_size_for_device_with_id(device_id:)
32
+ `adb -s #{device_id} shell wm size`
33
+ end
34
+
35
+ def save_snapshot_for_device_with_id_in_path(device_id:, file_path:)
36
+ `adb -s #{device_id} shell cat /sdcard/window_dump.xml > #{file_path}`
37
+ end
38
+
39
+ def sdk_version_for_device_with_id(device_id:)
40
+ `adb -s #{device_id} shell getprop ro.build.version.sdk`
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,82 @@
1
+ require 'kraken-mobile/device_process.rb'
2
+ require 'kraken-mobile/utils/k'
3
+
4
+ class MobileProcess < DeviceProcess
5
+ #-------------------------------
6
+ # Required methods
7
+ #-------------------------------
8
+ def before_execute
9
+ register_process_to_directory
10
+ device.create_inbox
11
+ end
12
+
13
+ def after_execute
14
+ device.delete_inbox
15
+ end
16
+
17
+ def execute
18
+ open(execution_command, 'r') do |output|
19
+ loop do
20
+ $stdout.print output.readline.to_s
21
+ $stdout.flush
22
+ end
23
+ end
24
+ $CHILD_STATUS.exitstatus
25
+ rescue EOFError
26
+ nil
27
+ end
28
+
29
+ #-------------------------------
30
+ # Methods
31
+ #-------------------------------
32
+
33
+ def apk_path
34
+ return config_apk_path if @test_scenario.requires_predefined_devices?
35
+
36
+ path = @test_scenario&.kraken_app&.apk_path
37
+ raise 'ERROR: Invalid APK file path' if path.nil?
38
+
39
+ path
40
+ end
41
+
42
+ private
43
+
44
+ def execution_command
45
+ feature_path = test_scenario.feature_file.file_path
46
+ raise 'ERROR: Invalid feature file path' if feature_path.nil?
47
+
48
+ process_apk_path = apk_path
49
+ raise 'ERROR: Invalid APK file path' if process_apk_path.nil?
50
+
51
+ "|ADB_DEVICE_ARG=#{device.id} calabash-android run #{process_apk_path} "\
52
+ "#{feature_path} --tags @user#{id} --format pretty --format json -o "\
53
+ "#{K::REPORT_PATH}/#{@test_scenario.execution_id}/#{device.id}/#{K::FILE_REPORT_NAME}" # TODO, folder to save all things
54
+ end
55
+
56
+ #-------------------------------
57
+ # Helpers
58
+ #-------------------------------
59
+
60
+ def config_json
61
+ config_absolute_path = File.expand_path(ENV[K::CONFIG_PATH])
62
+ file = open(config_absolute_path)
63
+ content = file.read
64
+ JSON.parse(content)[@id.to_s] || {}
65
+ end
66
+
67
+ def config_apk_path
68
+ device_config_json = config_json
69
+ return if device_config_json['config'].nil?
70
+ return if device_config_json['config']['apk_path'].nil?
71
+
72
+ absolute_config_apk_path = File.expand_path(
73
+ device_config_json['config']['apk_path']
74
+ )
75
+
76
+ raise 'ERROR: Invalid config apk path' unless File.file?(
77
+ absolute_config_apk_path
78
+ )
79
+
80
+ absolute_config_apk_path
81
+ end
82
+ end
@@ -0,0 +1,121 @@
1
+ require 'kraken-mobile/models/device'
2
+ require 'kraken-mobile/mobile/adb'
3
+ require 'kraken-mobile/monkeys/mobile/kraken_android_monkey'
4
+ require 'kraken-mobile/monkeys/mobile/android_monkey'
5
+
6
+ class AndroidDevice < Device
7
+ include KrakenAndroidMonkey
8
+ include AndroidMonkey
9
+
10
+ #-------------------------------
11
+ # Signaling
12
+ #-------------------------------
13
+ def create_inbox
14
+ raise 'ERROR: Device is disconnected.' unless connected?
15
+
16
+ ADB.create_file_with_name_in_device(
17
+ device_id: @id,
18
+ file_name: K::INBOX_FILE_NAME
19
+ )
20
+ end
21
+
22
+ def delete_inbox
23
+ raise 'ERROR: Device is disconnected.' unless connected?
24
+
25
+ ADB.delete_file_with_name_in_device(
26
+ device_id: @id,
27
+ file_name: K::INBOX_FILE_NAME
28
+ )
29
+ end
30
+
31
+ def write_signal(signal)
32
+ ADB.write_content_to_file_with_name_in_device(
33
+ content: signal,
34
+ device_id: @id,
35
+ file_name: K::INBOX_FILE_NAME
36
+ )
37
+ end
38
+
39
+ def read_signal(signal, timeout = K::DEFAULT_TIMEOUT_SECONDS)
40
+ Timeout.timeout(timeout, RuntimeError) do
41
+ sleep(1) until inbox_last_signal == signal
42
+ end
43
+ end
44
+
45
+ #-------------------------------
46
+ # More interface methods
47
+ #-------------------------------
48
+ def connected?
49
+ ADB.connected_devices.any? do |device|
50
+ device.id == @id
51
+ end
52
+ end
53
+
54
+ def orientation
55
+ ADB.device_orientation(
56
+ device_id: @id
57
+ ).strip!.to_i
58
+ rescue StandardError => _e
59
+ K::ANDROID_PORTRAIT
60
+ end
61
+
62
+ def screen_size
63
+ size = ADB.device_screen_size(device_id: @id)
64
+
65
+ height = orientation == K::ANDROID_PORTRAIT ? size[1] : size[0]
66
+ width = orientation == K::ANDROID_PORTRAIT ? size[0] : size[1]
67
+
68
+ [height, width]
69
+ end
70
+
71
+ def sdk_version
72
+ ADB.device_sdk_version(device_id: @id)
73
+ end
74
+
75
+ def type
76
+ K::ANDROID_DEVICE
77
+ end
78
+
79
+ def save_snapshot_in_file_path(file_path)
80
+ raise 'ERROR: Invalid snapshot file path' if file_path.nil?
81
+
82
+ absolute_path = File.expand_path(file_path)
83
+ raise 'ERROR: File already exists' if File.file?(absolute_path)
84
+
85
+ ADB.save_snapshot_for_device_with_id_in_file_path(
86
+ device_id: @id,
87
+ file_path: absolute_path
88
+ )
89
+ end
90
+
91
+ #-------------------------------
92
+ # Random testing
93
+ #-------------------------------
94
+ def run_monkey_with_number_of_events(number_of_events)
95
+ execute_monkey(number_of_events)
96
+ end
97
+
98
+ def run_kraken_monkey_with_number_of_events(number_of_events)
99
+ execute_kraken_monkey(number_of_events)
100
+ end
101
+
102
+ #-------------------------------
103
+ # Helprs
104
+ #-------------------------------
105
+ def calabash_default_device
106
+ operations_module = Calabash::Android::Operations
107
+ operations_module::Device.new(
108
+ operations_module,
109
+ ENV['ADB_DEVICE_ARG'],
110
+ ENV['TEST_SERVER_PORT'],
111
+ ENV['APP_PATH'],
112
+ ENV['TEST_APP_PATH']
113
+ )
114
+ end
115
+
116
+ private
117
+
118
+ def inbox_last_signal
119
+ ADB.file_content(device_id: @id, file_name: K::INBOX_FILE_NAME).strip
120
+ end
121
+ end
@@ -1,32 +1,114 @@
1
- module KrakenMobile
2
- module Models
3
- class Device
4
-
5
- #-------------------------------
6
- # Fields
7
- #-------------------------------
8
- attr_accessor :id
9
- attr_accessor :model
10
- attr_accessor :position
11
- attr_accessor :config
12
-
13
- #-------------------------------
14
- # Constructors
15
- #-------------------------------
16
- def initialize(id, model, position, config = {})
17
- @id = id
18
- @model = model
19
- @position = position
20
- @config = config
21
- end
22
-
23
- #-------------------------------
24
- # Helpers
25
- #-------------------------------
26
- def screenshot_prefix
27
- @id.gsub('.', '_').gsub(/:(.*)/, '').to_s + '_'
28
- end
29
-
30
- end
31
- end
1
+ require 'kraken-mobile/device_process'
2
+
3
+ class Device
4
+ #-------------------------------
5
+ # Fields
6
+ #-------------------------------
7
+ attr_accessor :id
8
+ attr_accessor :model
9
+
10
+ #-------------------------------
11
+ # Constructors
12
+ #-------------------------------
13
+ def initialize(id:, model:)
14
+ @id = id
15
+ @model = model
16
+ end
17
+
18
+ #-------------------------------
19
+ # Signaling
20
+ #-------------------------------
21
+ def create_inbox
22
+ raise 'ERROR: create_inbox not implemented.'
23
+ end
24
+
25
+ def delete_inbox
26
+ raise 'ERROR: delete_inbox not implemented.'
27
+ end
28
+
29
+ def write_signal(_signal)
30
+ raise 'ERROR: write_signal not implemented.'
31
+ end
32
+
33
+ def read_signal(_signal, _timeout = K::DEFAULT_TIMEOUT_SECONDS)
34
+ raise 'ERROR: read_signal not implemented.'
35
+ end
36
+
37
+ #-------------------------------
38
+ # Random testing
39
+ #-------------------------------
40
+ def run_monkey_with_number_of_events(_number_of_events)
41
+ raise 'ERROR: run_monkey_with_number_of_events not implemented.'
42
+ end
43
+
44
+ def run_kraken_monkey_with_number_of_events(_number_of_events)
45
+ raise 'ERROR: run_kraken_monkey_with_number_of_events not implemented.'
46
+ end
47
+
48
+ #-------------------------------
49
+ # More interface methods
50
+ #-------------------------------
51
+ def connected?
52
+ raise 'ERROR: connected? not implemented.'
53
+ end
54
+
55
+ def orientation
56
+ raise 'ERROR: orientation not implemented.'
57
+ end
58
+
59
+ def screen_size
60
+ raise 'ERROR: screen_size not implemented.'
61
+ end
62
+
63
+ def sdk_version
64
+ raise 'ERROR: sdk_version not implemented.'
65
+ end
66
+
67
+ def type
68
+ raise 'ERROR: Unsupported device'
69
+ end
70
+
71
+ def save_snapshot_in_path(_file_path)
72
+ raise 'ERROR: save_snapshot_in_path not implemented.'
73
+ end
74
+
75
+ #-------------------------------
76
+ # Helpers
77
+ #-------------------------------
78
+ def to_s
79
+ @id + K::SEPARATOR + @model + K::SEPARATOR + type
80
+ end
81
+
82
+ def screenshot_prefix
83
+ @id.gsub('.', '_').gsub(/:(.*)/, '').to_s + '_'
84
+ end
85
+
86
+ def self.find_by_process_id(id)
87
+ DeviceProcess.directory.each do |process_info|
88
+ info = process_info.strip.split(K::SEPARATOR)
89
+ process_id = info[0]
90
+ next unless process_id.to_s == id.to_s
91
+
92
+ return device_from_type(
93
+ id: info[1], model: info[2],
94
+ device_type: info[3]
95
+ )
96
+ end
97
+
98
+ nil
99
+ end
100
+
101
+ def self.device_from_type(id:, model:, device_type:)
102
+ device_class = nil
103
+ if device_type == K::ANDROID_DEVICE
104
+ device_class = AndroidDevice
105
+ elsif device_type == K::WEB_DEVICE
106
+ device_class = WebDevice
107
+ end
108
+ raise 'ERROR: Unsupported device' if device_class.nil?
109
+
110
+ device_class.new(
111
+ id: id, model: model
112
+ )
113
+ end
32
114
  end