cukunity 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitmodules +0 -0
- data/.rspec +1 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +60 -0
- data/LICENSE.txt +20 -0
- data/README.md +214 -0
- data/Rakefile +51 -0
- data/Unity/Assets/Demo/Art/Buttons/down.png +0 -0
- data/Unity/Assets/Demo/Art/Buttons/down.png.meta +28 -0
- data/Unity/Assets/Demo/Art/Buttons/left.png +0 -0
- data/Unity/Assets/Demo/Art/Buttons/left.png.meta +28 -0
- data/Unity/Assets/Demo/Art/Buttons/ok.png +0 -0
- data/Unity/Assets/Demo/Art/Buttons/ok.png.meta +28 -0
- data/Unity/Assets/Demo/Art/Buttons/options.png +0 -0
- data/Unity/Assets/Demo/Art/Buttons/options.png.meta +28 -0
- data/Unity/Assets/Demo/Art/Buttons/play.png +0 -0
- data/Unity/Assets/Demo/Art/Buttons/play.png.meta +28 -0
- data/Unity/Assets/Demo/Art/Buttons/quit.png +0 -0
- data/Unity/Assets/Demo/Art/Buttons/quit.png.meta +28 -0
- data/Unity/Assets/Demo/Art/Buttons/retry.png +0 -0
- data/Unity/Assets/Demo/Art/Buttons/retry.png.meta +28 -0
- data/Unity/Assets/Demo/Art/Buttons/right.png +0 -0
- data/Unity/Assets/Demo/Art/Buttons/right.png.meta +28 -0
- data/Unity/Assets/Demo/Art/Buttons/up.png +0 -0
- data/Unity/Assets/Demo/Art/Buttons/up.png.meta +28 -0
- data/Unity/Assets/Demo/Art/Buttons.meta +2 -0
- data/Unity/Assets/Demo/Art/clear.png +0 -0
- data/Unity/Assets/Demo/Art/clear.png.meta +28 -0
- data/Unity/Assets/Demo/Art/options.png +0 -0
- data/Unity/Assets/Demo/Art/options.png.meta +28 -0
- data/Unity/Assets/Demo/Art/sokoban.png +0 -0
- data/Unity/Assets/Demo/Art/sokoban.png.meta +28 -0
- data/Unity/Assets/Demo/Art.meta +2 -0
- data/Unity/Assets/Demo/Cubes/CrateCube.mat +0 -0
- data/Unity/Assets/Demo/Cubes/CrateCube.mat.meta +2 -0
- data/Unity/Assets/Demo/Cubes/CrateCube.prefab +0 -0
- data/Unity/Assets/Demo/Cubes/CrateCube.prefab.meta +2 -0
- data/Unity/Assets/Demo/Cubes/FloorCube.mat +0 -0
- data/Unity/Assets/Demo/Cubes/FloorCube.mat.meta +2 -0
- data/Unity/Assets/Demo/Cubes/FloorCube.prefab +0 -0
- data/Unity/Assets/Demo/Cubes/FloorCube.prefab.meta +2 -0
- data/Unity/Assets/Demo/Cubes/GoalCube.mat +0 -0
- data/Unity/Assets/Demo/Cubes/GoalCube.mat.meta +2 -0
- data/Unity/Assets/Demo/Cubes/GoalCube.prefab +0 -0
- data/Unity/Assets/Demo/Cubes/GoalCube.prefab.meta +2 -0
- data/Unity/Assets/Demo/Cubes/PlayerCube.mat +0 -0
- data/Unity/Assets/Demo/Cubes/PlayerCube.mat.meta +2 -0
- data/Unity/Assets/Demo/Cubes/PlayerCube.prefab +0 -0
- data/Unity/Assets/Demo/Cubes/PlayerCube.prefab.meta +2 -0
- data/Unity/Assets/Demo/Cubes/WallCube.mat +0 -0
- data/Unity/Assets/Demo/Cubes/WallCube.mat.meta +2 -0
- data/Unity/Assets/Demo/Cubes/WallCube.prefab +0 -0
- data/Unity/Assets/Demo/Cubes/WallCube.prefab.meta +2 -0
- data/Unity/Assets/Demo/Cubes.meta +2 -0
- data/Unity/Assets/Demo/Resources/MainMenu.prefab +0 -0
- data/Unity/Assets/Demo/Resources/MainMenu.prefab.meta +2 -0
- data/Unity/Assets/Demo/Resources/OptionsMenu.prefab +0 -0
- data/Unity/Assets/Demo/Resources/OptionsMenu.prefab.meta +2 -0
- data/Unity/Assets/Demo/Resources.meta +2 -0
- data/Unity/Assets/Demo/Scripts/CameraController.js +18 -0
- data/Unity/Assets/Demo/Scripts/CameraController.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/EnterPlayerName.js +21 -0
- data/Unity/Assets/Demo/Scripts/EnterPlayerName.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/GameLeftButton.js +18 -0
- data/Unity/Assets/Demo/Scripts/GameLeftButton.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/GameQuitButton.js +6 -0
- data/Unity/Assets/Demo/Scripts/GameQuitButton.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/GameRetryButton.js +7 -0
- data/Unity/Assets/Demo/Scripts/GameRetryButton.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/GameRightButton.js +18 -0
- data/Unity/Assets/Demo/Scripts/GameRightButton.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/GameUpButton.js +18 -0
- data/Unity/Assets/Demo/Scripts/GameUpButton.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/OnTouchGUIElementDown.js +34 -0
- data/Unity/Assets/Demo/Scripts/OnTouchGUIElementDown.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/PlayerData.js +3 -0
- data/Unity/Assets/Demo/Scripts/PlayerData.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/SokobanController.js +240 -0
- data/Unity/Assets/Demo/Scripts/SokobanController.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/TakeScreenShot.js +26 -0
- data/Unity/Assets/Demo/Scripts/TakeScreenShot.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/TitleOptionsButton.js +8 -0
- data/Unity/Assets/Demo/Scripts/TitleOptionsButton.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/TitleOptionsOkButton.js +8 -0
- data/Unity/Assets/Demo/Scripts/TitleOptionsOkButton.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/TitlePlayButton.js +6 -0
- data/Unity/Assets/Demo/Scripts/TitlePlayButton.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/TitleQuitButton.js +7 -0
- data/Unity/Assets/Demo/Scripts/TitleQuitButton.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/UpdateMovesLabel.js +26 -0
- data/Unity/Assets/Demo/Scripts/UpdateMovesLabel.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts/UpdatePlayerName.js +14 -0
- data/Unity/Assets/Demo/Scripts/UpdatePlayerName.js.meta +2 -0
- data/Unity/Assets/Demo/Scripts.meta +2 -0
- data/Unity/Assets/Demo/demo_game.unity +0 -0
- data/Unity/Assets/Demo/demo_game.unity.meta +2 -0
- data/Unity/Assets/Demo/demo_title.unity +0 -0
- data/Unity/Assets/Demo/demo_title.unity.meta +2 -0
- data/Unity/Assets/Demo.meta +2 -0
- data/Unity/Assets/Plugins/CukunityInstance.prefab +0 -0
- data/Unity/Assets/Plugins/CukunityInstance.prefab.meta +2 -0
- data/Unity/Assets/Plugins/CukunityServer.cs +871 -0
- data/Unity/Assets/Plugins/CukunityServer.cs.meta +2 -0
- data/Unity/Assets/Plugins/JsonFx.Json.dll +0 -0
- data/Unity/Assets/Plugins/JsonFx.Json.dll.meta +2 -0
- data/Unity/Assets/Plugins/iOS/CukunityServer.mm +242 -0
- data/Unity/Assets/Plugins/iOS/CukunityServer.mm.meta +2 -0
- data/Unity/Assets/Plugins/iOS.meta +2 -0
- data/Unity/Assets/Plugins.meta +2 -0
- data/Unity/Library/AudioManager.asset +0 -0
- data/Unity/Library/BuildSettings.asset +0 -0
- data/Unity/Library/DynamicsManager.asset +0 -0
- data/Unity/Library/EditorBuildSettings.asset +0 -0
- data/Unity/Library/EditorSettings.asset +0 -0
- data/Unity/Library/EditorUserBuildSettings.asset +0 -0
- data/Unity/Library/EditorUserSettings.asset +0 -0
- data/Unity/Library/InputManager.asset +0 -0
- data/Unity/Library/InspectorExpandedItems.asset +0 -0
- data/Unity/Library/MonoManager.asset +0 -0
- data/Unity/Library/NetworkManager.asset +0 -0
- data/Unity/Library/ProjectSettings.asset +0 -0
- data/Unity/Library/QualitySettings.asset +0 -0
- data/Unity/Library/TagManager.asset +0 -0
- data/Unity/Library/TimeManager.asset +0 -0
- data/Unity/ProjectSettings/AudioManager.asset +0 -0
- data/Unity/ProjectSettings/DynamicsManager.asset +0 -0
- data/Unity/ProjectSettings/EditorBuildSettings.asset +0 -0
- data/Unity/ProjectSettings/EditorSettings.asset +0 -0
- data/Unity/ProjectSettings/InputManager.asset +0 -0
- data/Unity/ProjectSettings/NavMeshLayers.asset +0 -0
- data/Unity/ProjectSettings/NetworkManager.asset +0 -0
- data/Unity/ProjectSettings/ProjectSettings.asset +0 -0
- data/Unity/ProjectSettings/QualitySettings.asset +0 -0
- data/Unity/ProjectSettings/TagManager.asset +0 -0
- data/Unity/ProjectSettings/TimeManager.asset +0 -0
- data/VERSION +1 -0
- data/bin/cukunity +7 -0
- data/cukunity.gemspec +298 -0
- data/features/get_scene_command.feature +39 -0
- data/features/i18n/ja/sokoban_clear.feature +109 -0
- data/features/install.feature +23 -0
- data/features/keyboard_input.feature +26 -0
- data/features/load_level_command.feature +36 -0
- data/features/run.feature +33 -0
- data/features/select_visible.feature +29 -0
- data/features/support/env.rb +11 -0
- data/features/touch_input.feature +96 -0
- data/lib/cukunity/cli/argument_parser.rb +103 -0
- data/lib/cukunity/cli/bootstrap_command.rb +39 -0
- data/lib/cukunity/cli/doctor_command.rb +150 -0
- data/lib/cukunity/cli/features_command.rb +35 -0
- data/lib/cukunity/cli/main.rb +18 -0
- data/lib/cukunity/cli/options.rb +8 -0
- data/lib/cukunity/cli.rb +11 -0
- data/lib/cukunity/cucumber/step_definitions/get_scene_command_steps.rb +37 -0
- data/lib/cukunity/cucumber/step_definitions/i18n/ja/get_scene_command_steps.rb +37 -0
- data/lib/cukunity/cucumber/step_definitions/i18n/ja/install_steps.rb +25 -0
- data/lib/cukunity/cucumber/step_definitions/i18n/ja/keyboard_input_steps.rb +17 -0
- data/lib/cukunity/cucumber/step_definitions/i18n/ja/load_level_command_steps.rb +17 -0
- data/lib/cukunity/cucumber/step_definitions/i18n/ja/run_steps.rb +25 -0
- data/lib/cukunity/cucumber/step_definitions/i18n/ja/select_visible_steps.rb +9 -0
- data/lib/cukunity/cucumber/step_definitions/i18n/ja/touch_input_steps.rb +19 -0
- data/lib/cukunity/cucumber/step_definitions/i18n/ja.rb +9 -0
- data/lib/cukunity/cucumber/step_definitions/install_steps.rb +33 -0
- data/lib/cukunity/cucumber/step_definitions/keyboard_input_steps.rb +13 -0
- data/lib/cukunity/cucumber/step_definitions/load_level_command_steps.rb +17 -0
- data/lib/cukunity/cucumber/step_definitions/run_steps.rb +32 -0
- data/lib/cukunity/cucumber/step_definitions/select_visible_steps.rb +15 -0
- data/lib/cukunity/cucumber/step_definitions/touch_input_steps.rb +18 -0
- data/lib/cukunity/cucumber/support/platform_helpers.rb +105 -0
- data/lib/cukunity/cucumber/support/spec_helpers.rb +21 -0
- data/lib/cukunity/cucumber/support/transforms.rb +5 -0
- data/lib/cukunity/cucumber/support/unity_helpers.rb +35 -0
- data/lib/cukunity/cucumber.rb +28 -0
- data/lib/cukunity/drivers/android/app.rb +43 -0
- data/lib/cukunity/drivers/android/appkiller/.gitignore +9 -0
- data/lib/cukunity/drivers/android/appkiller/AndroidManifest.xml +17 -0
- data/lib/cukunity/drivers/android/appkiller/ant.properties +17 -0
- data/lib/cukunity/drivers/android/appkiller/build.xml +85 -0
- data/lib/cukunity/drivers/android/appkiller/proguard.cfg +40 -0
- data/lib/cukunity/drivers/android/appkiller/project.properties +11 -0
- data/lib/cukunity/drivers/android/appkiller/res/layout/main.xml +13 -0
- data/lib/cukunity/drivers/android/appkiller/res/values/strings.xml +4 -0
- data/lib/cukunity/drivers/android/appkiller/src/cukunity/appkiller/AppKiller.java +29 -0
- data/lib/cukunity/drivers/android/keyboard_input.rb +149 -0
- data/lib/cukunity/drivers/android/keycodes.rb +531 -0
- data/lib/cukunity/drivers/android/monkey_client.rb +65 -0
- data/lib/cukunity/drivers/android/process.rb +103 -0
- data/lib/cukunity/drivers/android/touch_input.rb +24 -0
- data/lib/cukunity/drivers/android/tunnel_client_methods.rb +22 -0
- data/lib/cukunity/drivers/android/unity_client.rb +27 -0
- data/lib/cukunity/drivers/android/utils.rb +50 -0
- data/lib/cukunity/drivers/android.rb +9 -0
- data/lib/cukunity/drivers/iOS/app.rb +32 -0
- data/lib/cukunity/drivers/iOS/keyboard_input.rb +104 -0
- data/lib/cukunity/drivers/iOS/process.rb +42 -0
- data/lib/cukunity/drivers/iOS/touch_input.rb +18 -0
- data/lib/cukunity/drivers/iOS/tunnel_client_methods.rb +27 -0
- data/lib/cukunity/drivers/iOS/uiautomation.rb +97 -0
- data/lib/cukunity/drivers/iOS/uiautomation_master.rb +152 -0
- data/lib/cukunity/drivers/iOS/uiautomation_slave.js +238 -0
- data/lib/cukunity/drivers/iOS/uiautomation_slave.rb +41 -0
- data/lib/cukunity/drivers/iOS/unity_client.rb +27 -0
- data/lib/cukunity/drivers/iOS/utils.rb +46 -0
- data/lib/cukunity/drivers/iOS.rb +9 -0
- data/lib/cukunity/exceptions.rb +24 -0
- data/lib/cukunity/keyboard_input_methods.rb +35 -0
- data/lib/cukunity/request_line_response_client_methods.rb +80 -0
- data/lib/cukunity/tasks/cucumber.rake +22 -0
- data/lib/cukunity/touch_input_methods.rb +19 -0
- data/lib/cukunity/unity/client_methods.rb +46 -0
- data/lib/cukunity/unity/command_facade.rb +67 -0
- data/lib/cukunity/unity/component.rb +39 -0
- data/lib/cukunity/unity/gameobject.rb +53 -0
- data/lib/cukunity/unity/hint.rb +27 -0
- data/lib/cukunity/unity/json_container.rb +48 -0
- data/lib/cukunity/unity/level.rb +6 -0
- data/lib/cukunity/unity/scene.rb +18 -0
- data/lib/cukunity/unity/screen.rb +45 -0
- data/lib/cukunity/unity/screen_hint_methods.rb +61 -0
- data/lib/cukunity/unity/text_hint_methods.rb +17 -0
- data/lib/cukunity/unity.rb +11 -0
- data/lib/cukunity/utils.rb +56 -0
- data/lib/cukunity.rb +19 -0
- metadata +411 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
module Cukunity
|
2
|
+
module Android
|
3
|
+
module Process
|
4
|
+
extend Android::Utils
|
5
|
+
extend Cukunity::Utils
|
6
|
+
|
7
|
+
def self.launch(path, extras = {}, max_time = 30)
|
8
|
+
unless App::installed?(path)
|
9
|
+
App::install(path)
|
10
|
+
end
|
11
|
+
package = package_name(path)
|
12
|
+
activity = launchable_activity_name(path)
|
13
|
+
extras_args = extras.inject([]) do |extras, kvp|
|
14
|
+
key, value = kvp
|
15
|
+
type =
|
16
|
+
if value.kind_of? String
|
17
|
+
'--es'
|
18
|
+
elsif value.kind_of? Fixnum
|
19
|
+
'--ei'
|
20
|
+
elsif value === true or value === false
|
21
|
+
'--ez'
|
22
|
+
else
|
23
|
+
raise ArgumentError.new("Unsupported type #{value.class} as extra parameter to activity")
|
24
|
+
end
|
25
|
+
extras.push %Q[#{type} "#{key}" "#{value}"]
|
26
|
+
end.join(" ")
|
27
|
+
shell %Q[am start #{extras_args} -n "#{package}/#{activity}" -W]
|
28
|
+
raise Exception::LaunchTimeout.new if max_time > 0 and check_timeout(max_time) do
|
29
|
+
foreground?(path)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.running
|
34
|
+
headers = /^\S+\s+(\d+)\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S.*)/
|
35
|
+
shell('ps').scan(headers).inject({}) do |packages, m|
|
36
|
+
packages[m[0].to_i] = m[1].chomp
|
37
|
+
packages
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.foreground
|
42
|
+
shell('dumpsys activity').match(/TaskRecord\{[^\n\}]+\s+([^\s\}]+)}/) do |m|
|
43
|
+
return m[1]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.foreground?(path)
|
48
|
+
fg = foreground
|
49
|
+
if fg
|
50
|
+
fg.downcase == package_name(path).downcase
|
51
|
+
else
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.running?(path)
|
57
|
+
package = package_name(path).downcase
|
58
|
+
running.any? do |pid, p|
|
59
|
+
p.downcase == package
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.pids(path)
|
64
|
+
package = package_name(path).downcase
|
65
|
+
running.select do |pid, p|
|
66
|
+
p.downcase == package
|
67
|
+
end.keys
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.relaunch(path, max_time = 30)
|
71
|
+
unless App::installed?(path)
|
72
|
+
App::install(path)
|
73
|
+
end
|
74
|
+
before_pids = pids(path)
|
75
|
+
unless before_pids.empty?
|
76
|
+
# launch restarter
|
77
|
+
_relaunch(path)
|
78
|
+
raise Exception::LaunchTimeout.new if check_timeout(max_time) do
|
79
|
+
now_pids = pids(path)
|
80
|
+
before_pids.all? do |pid|
|
81
|
+
# old process does not exist?
|
82
|
+
not now_pids.include?(pid)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
# wait for a new app
|
87
|
+
start = Time.now
|
88
|
+
raise Exception::LaunchTimeout.new if check_timeout(max_time) do
|
89
|
+
foreground?(path)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def self._relaunch(path)
|
95
|
+
app_killer = File.join(File.expand_path(File.dirname(__FILE__)), 'appkiller', 'bin', 'AppKiller.apk')
|
96
|
+
unless App::installed?(app_killer)
|
97
|
+
App::install(app_killer)
|
98
|
+
end
|
99
|
+
launch(app_killer, {'package' => package_name(path)}, 0)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Cukunity
|
2
|
+
module Android
|
3
|
+
module TouchInput
|
4
|
+
extend Android::Utils
|
5
|
+
extend Cukunity::TouchInputMethods
|
6
|
+
|
7
|
+
def self.touch_screen(options)
|
8
|
+
options = merge_options(options, { :x => 0, :y => 0, :type => :tap })
|
9
|
+
wait = (options.delete(:wait) || 0.1).to_f
|
10
|
+
options[:x] = options[:x].to_i
|
11
|
+
options[:y] = options[:y].to_i
|
12
|
+
prefix =
|
13
|
+
if options[:type] == :tap
|
14
|
+
"tap"
|
15
|
+
else
|
16
|
+
"touch #{type}"
|
17
|
+
end
|
18
|
+
req = %Q[#{prefix} #{options[:x]} #{options[:y]}]
|
19
|
+
monkey.command(req)
|
20
|
+
sleep(wait) unless wait.nil? or wait <= 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Cukunity
|
2
|
+
module Android
|
3
|
+
module TunnelClientMethods
|
4
|
+
include Cukunity::Android::Utils
|
5
|
+
|
6
|
+
def open_tunnel(lport, rport)
|
7
|
+
return @tunnel if @tunnel == [lport, rport]
|
8
|
+
# create tunnel to device
|
9
|
+
output, status = adb ['forward', "tcp:#{lport.to_i}", "tcp:#{rport.to_i}"]
|
10
|
+
unless status.exited? and status.exitstatus == 0
|
11
|
+
raise Exception::TunnelError.new
|
12
|
+
end
|
13
|
+
@tunnel = [lport, rport]
|
14
|
+
end
|
15
|
+
|
16
|
+
def close_tunnel
|
17
|
+
# FIXME: remove tunnel
|
18
|
+
@tunnel = nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Cukunity
|
2
|
+
module Android
|
3
|
+
module Unity
|
4
|
+
class Client
|
5
|
+
include Cukunity::Android::TunnelClientMethods
|
6
|
+
include Cukunity::Unity::ClientMethods
|
7
|
+
alias_method :client_connect_original, :connect
|
8
|
+
alias_method :client_close_original, :close
|
9
|
+
|
10
|
+
def platform
|
11
|
+
Cukunity::Android
|
12
|
+
end
|
13
|
+
|
14
|
+
def close
|
15
|
+
client_close_original
|
16
|
+
close_tunnel
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def connect(host = DEFAULT_HOSTNAME, port = DEFAULT_PORT)
|
21
|
+
open_tunnel(port + 1, port)
|
22
|
+
client_connect_original(host, port + 1)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Cukunity
|
2
|
+
module Android
|
3
|
+
module Utils
|
4
|
+
include Cukunity::Utils
|
5
|
+
|
6
|
+
def monkey
|
7
|
+
Android::MonkeyClient.instance
|
8
|
+
end
|
9
|
+
|
10
|
+
def shell(cmd_line)
|
11
|
+
`adb -d shell #{cmd_line}`
|
12
|
+
end
|
13
|
+
|
14
|
+
def adb(cmd, options = {})
|
15
|
+
options = merge_options(options, { :wait => true })
|
16
|
+
cmd_line = ['adb', '-d'] + cmd + [:err => [:child, :out]]
|
17
|
+
return IO.popen(cmd_line) if !options[:wait]
|
18
|
+
output = IO.popen(cmd_line) do |io|
|
19
|
+
if block_given?
|
20
|
+
yield io
|
21
|
+
else
|
22
|
+
io.readlines
|
23
|
+
end
|
24
|
+
end
|
25
|
+
[output, $?]
|
26
|
+
end
|
27
|
+
|
28
|
+
def launchable_activity_name(path)
|
29
|
+
return path unless File.exists?(path)
|
30
|
+
dump_badging(path).match(/launchable-activity:.*name='(.[^']+)'/) do |m|
|
31
|
+
return m[1]
|
32
|
+
end
|
33
|
+
raise Exception::InvalidApp.new(path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def package_name(path)
|
37
|
+
return path unless File.exists?(path)
|
38
|
+
dump_badging(path).match(/package:.*name='(.[^']+)'/) do |m|
|
39
|
+
return m[1]
|
40
|
+
end
|
41
|
+
throw InvalidApp.new(path)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def dump_badging(path)
|
46
|
+
`aapt dump badging "#{path}"`
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'cukunity/drivers/android/utils'
|
2
|
+
require 'cukunity/drivers/android/tunnel_client_methods'
|
3
|
+
require 'cukunity/drivers/android/monkey_client'
|
4
|
+
require 'cukunity/drivers/android/app'
|
5
|
+
require 'cukunity/drivers/android/process'
|
6
|
+
require 'cukunity/drivers/android/touch_input'
|
7
|
+
require 'cukunity/drivers/android/keycodes'
|
8
|
+
require 'cukunity/drivers/android/keyboard_input'
|
9
|
+
require 'cukunity/drivers/android/unity_client'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Cukunity
|
2
|
+
module IOS
|
3
|
+
module App
|
4
|
+
extend IOS::Utils
|
5
|
+
extend Cukunity::Utils
|
6
|
+
|
7
|
+
def self.install(path, options = {})
|
8
|
+
options = merge_options(options, { :max_time => 30 })
|
9
|
+
output = mobile_device_cmd(['install_app', path], options).first.chomp
|
10
|
+
raise Exception::InstallFailed.new unless output == 'OK'
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.uninstall(path, options = {})
|
14
|
+
options = merge_options(options, { :max_time => 30 })
|
15
|
+
bundle_id = bundle_identifier(path)
|
16
|
+
output = mobile_device_cmd(['uninstall_app', bundle_id], options).first.chomp
|
17
|
+
raise Exception::UninstallFailed.new unless output == 'OK'
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.installed
|
21
|
+
mobile_device_cmd(['list_installed_apps']).map do |bundle_id|
|
22
|
+
bundle_id.chomp
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.installed?(path)
|
27
|
+
bundle_id = bundle_identifier(path)
|
28
|
+
installed.include?(bundle_id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Cukunity
|
2
|
+
module IOS
|
3
|
+
module KeyboardInput
|
4
|
+
extend IOS::Utils
|
5
|
+
extend Cukunity::KeyboardInputMethods
|
6
|
+
|
7
|
+
def self.typed_text(options = {})
|
8
|
+
res = keyboard_command(options) do
|
9
|
+
'typedText'
|
10
|
+
end
|
11
|
+
res['text']
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.type_text(text, options = {})
|
15
|
+
keyboard_command(options) do
|
16
|
+
['typeText', { 'text' => text.to_s }]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.clear_text(options = {})
|
21
|
+
keyboard_command(options) do
|
22
|
+
'clearText'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.press_key(code, options = {})
|
27
|
+
keyboard_command(options) do
|
28
|
+
['tapKeyboardKey', { 'key' => code }]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.press_button(code, options = {})
|
33
|
+
keyboard_command(options) do
|
34
|
+
['tapKeyboardButton', { 'button' => code }]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.press_toolbar_button(code, options = {})
|
39
|
+
keyboard_command(options) do
|
40
|
+
['tapKeyboardToolbarButton', { 'button' => code }]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.keyboard_buttons(options = {})
|
45
|
+
keyboard_command(options) do
|
46
|
+
'keyboardButtons'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.keyboard_keys(options = {})
|
51
|
+
keyboard_command(options) do
|
52
|
+
'keyboardKeys'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.press_done(options = {})
|
57
|
+
press_toolbar_button('Done', options)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.press_back(options = {})
|
61
|
+
press_done(options)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.press_enter(options = {})
|
65
|
+
press_button('return', options)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.press_space(options = {})
|
69
|
+
press_key('space', options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.press_delete(options = {})
|
73
|
+
press_button('Delete', options)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def self.closed?(options = {})
|
78
|
+
res = keyboard_command(options) do
|
79
|
+
'isKeyboardAvailable'
|
80
|
+
end
|
81
|
+
res['available'] != true
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.platform_open_keyboard(options = {})
|
85
|
+
raise Exception::KeyboardWaitTimeout.new if check_timeout(10) do
|
86
|
+
!closed?(options)
|
87
|
+
end
|
88
|
+
if options[:clear]
|
89
|
+
clear_text
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.platform_keyboard_command(options, *cmd)
|
94
|
+
uiautomation_cmd(*cmd)
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.toolbar_buttons(options = {})
|
98
|
+
keyboard_command(options) do
|
99
|
+
'keyboardToolbarButtons'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
3
|
+
module Cukunity
|
4
|
+
module IOS
|
5
|
+
module Process
|
6
|
+
extend IOS::Utils
|
7
|
+
extend Cukunity::Utils
|
8
|
+
|
9
|
+
def self.launch(path, max_time = 30)
|
10
|
+
unless App::installed?(path)
|
11
|
+
App::install(path)
|
12
|
+
end
|
13
|
+
pipe = Cukunity::IOS::UIAutomation.instance.popen(bundle_identifier(path))
|
14
|
+
raise Exception::LaunchTimeout.new if max_time > 0 and check_timeout(max_time) do
|
15
|
+
foreground?(path)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.foreground?(path)
|
20
|
+
running?(path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.running?(path)
|
24
|
+
path_bundle_id = bundle_identifier(path)
|
25
|
+
begin
|
26
|
+
# it should be already running and polling
|
27
|
+
res = uiautomation_cmd('bundleIdentifier', :max_time => 3)
|
28
|
+
res['bundleIdentifier'] == path_bundle_id
|
29
|
+
rescue Exception::UIAutomationSlaveTimeout
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.relaunch(path, max_time = 30)
|
35
|
+
Cukunity::IOS::Unity::Client.instance.close
|
36
|
+
Cukunity::IOS::UIAutomationMaster.instance.close
|
37
|
+
Cukunity::IOS::UIAutomation.instance.close
|
38
|
+
launch(path, max_time)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Cukunity
|
2
|
+
module IOS
|
3
|
+
module TouchInput
|
4
|
+
extend IOS::Utils
|
5
|
+
extend Cukunity::TouchInputMethods
|
6
|
+
|
7
|
+
def self.touch_screen(options)
|
8
|
+
options = merge_options(options, { :x => 0, :y => 0, :method => 'tap', :tapCount => 1 })
|
9
|
+
wait = (options.delete(:wait) || self.default_tap_delay).to_f
|
10
|
+
# FIXME: screen dimensions
|
11
|
+
options[:x] = options[:x].to_i / 2
|
12
|
+
options[:y] = options[:y].to_i / 2
|
13
|
+
unity_command('touch_screen', restrict_options(options, :x, :y, :method, :tapCount))
|
14
|
+
sleep(wait) unless wait.nil? or wait <= 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Cukunity
|
2
|
+
module IOS
|
3
|
+
module TunnelClientMethods
|
4
|
+
include Cukunity::IOS::Utils
|
5
|
+
|
6
|
+
def open_tunnel(lport, rport)
|
7
|
+
return @tunnel if lport == 0 and !@tunnel.nil?
|
8
|
+
# create tunnel to device
|
9
|
+
@mobiledevice_pipe = mobile_device_cmd(['tunnel', lport.to_s, rport.to_s, :err => [:child, :out]], :wait => false)
|
10
|
+
output = @mobiledevice_pipe.readline
|
11
|
+
match = output.match(/^Tunneling from local port (\d+) to device port/)
|
12
|
+
raise Exception::TunnelError.new(output) if match.nil?
|
13
|
+
lport = match[1].to_i
|
14
|
+
@tunnel = [lport, rport]
|
15
|
+
end
|
16
|
+
|
17
|
+
def close_tunnel
|
18
|
+
@tunnel = nil
|
19
|
+
unless @mobiledevice_pipe.nil?
|
20
|
+
::Process.kill('KILL', @mobiledevice_pipe.pid) rescue ::Exception
|
21
|
+
@mobiledevice_pipe.close rescue ::Exception
|
22
|
+
@mobiledevice_pipe = nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Cukunity
|
4
|
+
module IOS
|
5
|
+
class UIAutomation
|
6
|
+
include Singleton
|
7
|
+
include IOS::Utils
|
8
|
+
include Cukunity::Utils
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@pipe = nil
|
12
|
+
@tmpdir = []
|
13
|
+
@pipes = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def popen(bundle_id)
|
17
|
+
udid = device_udid
|
18
|
+
|
19
|
+
# create a temp directory for instrument logs and js file
|
20
|
+
tmpdir = File.expand_path(Dir.mktmpdir(['cukunity_', '_uiautomation']))
|
21
|
+
cmd = nil
|
22
|
+
begin
|
23
|
+
# export javascript to folder too
|
24
|
+
slave_js = File.join(tmpdir, 'uiautomation_slave.js')
|
25
|
+
slave_rb = File.join(File.dirname(__FILE__), 'uiautomation_slave.rb')
|
26
|
+
slave_js_opts = {
|
27
|
+
:ruby => File.expand_path(RbConfig.ruby),
|
28
|
+
:slave => slave_rb,
|
29
|
+
:address => UIAutomationMaster.instance.address,
|
30
|
+
:port => UIAutomationMaster.instance.port,
|
31
|
+
:bundle_id => bundle_id
|
32
|
+
}
|
33
|
+
|
34
|
+
generate_slave_js(slave_js, slave_js_opts)
|
35
|
+
|
36
|
+
# as of Xcode 4.3.2, the Developer folder is now in a different place.
|
37
|
+
templates = ['/Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate']
|
38
|
+
template = templates.find do |template|
|
39
|
+
File.file?(template)
|
40
|
+
end
|
41
|
+
|
42
|
+
# build path to command
|
43
|
+
cmd = ['instruments', '-w', udid,
|
44
|
+
'-t', template,
|
45
|
+
bundle_id,
|
46
|
+
'-e', 'UIASCRIPT',
|
47
|
+
slave_js,
|
48
|
+
'-e', 'UIARESULTSPATH', tmpdir,
|
49
|
+
:err => [:child, :out]]
|
50
|
+
rescue => err
|
51
|
+
FileUtils.remove_entry_secure(tmpdir)
|
52
|
+
raise err
|
53
|
+
end
|
54
|
+
|
55
|
+
# remove later
|
56
|
+
@tmpdir << tmpdir
|
57
|
+
# execute UIAutomation instruments
|
58
|
+
Dir.chdir tmpdir do
|
59
|
+
@pipes << IO.popen(cmd)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def close
|
64
|
+
# close all created pipes
|
65
|
+
@pipes.each do |pipe|
|
66
|
+
::Process.kill('KILL', pipe.pid) rescue ::Exception
|
67
|
+
pipe.close rescue ::Exception
|
68
|
+
end
|
69
|
+
@pipes = []
|
70
|
+
# remove all created temporary dirs
|
71
|
+
@tmpdir.each do |tmpdir|
|
72
|
+
FileUtils.remove_entry_secure(tmpdir, true)
|
73
|
+
end
|
74
|
+
@tmpdir = []
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def generate_slave_js(path, interpolations)
|
79
|
+
slave_js = File.join(File.dirname(__FILE__), 'uiautomation_slave.js')
|
80
|
+
# read original js file contents
|
81
|
+
contents = File.open(slave_js) do |f|
|
82
|
+
f.read
|
83
|
+
end
|
84
|
+
# perform necessary interpolations
|
85
|
+
interpolations.each_pair do |key, value|
|
86
|
+
if contents.gsub!("{{#{key}}}", value.to_s).nil?
|
87
|
+
raise "Could not perform replacement"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
# output
|
91
|
+
File.open(path, 'w') do |f|
|
92
|
+
f.write(contents)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|