cukunity 0.1.0
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.
- 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
|