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.
Files changed (226) hide show
  1. data/.document +5 -0
  2. data/.gitmodules +0 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +15 -0
  5. data/Gemfile.lock +60 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +214 -0
  8. data/Rakefile +51 -0
  9. data/Unity/Assets/Demo/Art/Buttons/down.png +0 -0
  10. data/Unity/Assets/Demo/Art/Buttons/down.png.meta +28 -0
  11. data/Unity/Assets/Demo/Art/Buttons/left.png +0 -0
  12. data/Unity/Assets/Demo/Art/Buttons/left.png.meta +28 -0
  13. data/Unity/Assets/Demo/Art/Buttons/ok.png +0 -0
  14. data/Unity/Assets/Demo/Art/Buttons/ok.png.meta +28 -0
  15. data/Unity/Assets/Demo/Art/Buttons/options.png +0 -0
  16. data/Unity/Assets/Demo/Art/Buttons/options.png.meta +28 -0
  17. data/Unity/Assets/Demo/Art/Buttons/play.png +0 -0
  18. data/Unity/Assets/Demo/Art/Buttons/play.png.meta +28 -0
  19. data/Unity/Assets/Demo/Art/Buttons/quit.png +0 -0
  20. data/Unity/Assets/Demo/Art/Buttons/quit.png.meta +28 -0
  21. data/Unity/Assets/Demo/Art/Buttons/retry.png +0 -0
  22. data/Unity/Assets/Demo/Art/Buttons/retry.png.meta +28 -0
  23. data/Unity/Assets/Demo/Art/Buttons/right.png +0 -0
  24. data/Unity/Assets/Demo/Art/Buttons/right.png.meta +28 -0
  25. data/Unity/Assets/Demo/Art/Buttons/up.png +0 -0
  26. data/Unity/Assets/Demo/Art/Buttons/up.png.meta +28 -0
  27. data/Unity/Assets/Demo/Art/Buttons.meta +2 -0
  28. data/Unity/Assets/Demo/Art/clear.png +0 -0
  29. data/Unity/Assets/Demo/Art/clear.png.meta +28 -0
  30. data/Unity/Assets/Demo/Art/options.png +0 -0
  31. data/Unity/Assets/Demo/Art/options.png.meta +28 -0
  32. data/Unity/Assets/Demo/Art/sokoban.png +0 -0
  33. data/Unity/Assets/Demo/Art/sokoban.png.meta +28 -0
  34. data/Unity/Assets/Demo/Art.meta +2 -0
  35. data/Unity/Assets/Demo/Cubes/CrateCube.mat +0 -0
  36. data/Unity/Assets/Demo/Cubes/CrateCube.mat.meta +2 -0
  37. data/Unity/Assets/Demo/Cubes/CrateCube.prefab +0 -0
  38. data/Unity/Assets/Demo/Cubes/CrateCube.prefab.meta +2 -0
  39. data/Unity/Assets/Demo/Cubes/FloorCube.mat +0 -0
  40. data/Unity/Assets/Demo/Cubes/FloorCube.mat.meta +2 -0
  41. data/Unity/Assets/Demo/Cubes/FloorCube.prefab +0 -0
  42. data/Unity/Assets/Demo/Cubes/FloorCube.prefab.meta +2 -0
  43. data/Unity/Assets/Demo/Cubes/GoalCube.mat +0 -0
  44. data/Unity/Assets/Demo/Cubes/GoalCube.mat.meta +2 -0
  45. data/Unity/Assets/Demo/Cubes/GoalCube.prefab +0 -0
  46. data/Unity/Assets/Demo/Cubes/GoalCube.prefab.meta +2 -0
  47. data/Unity/Assets/Demo/Cubes/PlayerCube.mat +0 -0
  48. data/Unity/Assets/Demo/Cubes/PlayerCube.mat.meta +2 -0
  49. data/Unity/Assets/Demo/Cubes/PlayerCube.prefab +0 -0
  50. data/Unity/Assets/Demo/Cubes/PlayerCube.prefab.meta +2 -0
  51. data/Unity/Assets/Demo/Cubes/WallCube.mat +0 -0
  52. data/Unity/Assets/Demo/Cubes/WallCube.mat.meta +2 -0
  53. data/Unity/Assets/Demo/Cubes/WallCube.prefab +0 -0
  54. data/Unity/Assets/Demo/Cubes/WallCube.prefab.meta +2 -0
  55. data/Unity/Assets/Demo/Cubes.meta +2 -0
  56. data/Unity/Assets/Demo/Resources/MainMenu.prefab +0 -0
  57. data/Unity/Assets/Demo/Resources/MainMenu.prefab.meta +2 -0
  58. data/Unity/Assets/Demo/Resources/OptionsMenu.prefab +0 -0
  59. data/Unity/Assets/Demo/Resources/OptionsMenu.prefab.meta +2 -0
  60. data/Unity/Assets/Demo/Resources.meta +2 -0
  61. data/Unity/Assets/Demo/Scripts/CameraController.js +18 -0
  62. data/Unity/Assets/Demo/Scripts/CameraController.js.meta +2 -0
  63. data/Unity/Assets/Demo/Scripts/EnterPlayerName.js +21 -0
  64. data/Unity/Assets/Demo/Scripts/EnterPlayerName.js.meta +2 -0
  65. data/Unity/Assets/Demo/Scripts/GameLeftButton.js +18 -0
  66. data/Unity/Assets/Demo/Scripts/GameLeftButton.js.meta +2 -0
  67. data/Unity/Assets/Demo/Scripts/GameQuitButton.js +6 -0
  68. data/Unity/Assets/Demo/Scripts/GameQuitButton.js.meta +2 -0
  69. data/Unity/Assets/Demo/Scripts/GameRetryButton.js +7 -0
  70. data/Unity/Assets/Demo/Scripts/GameRetryButton.js.meta +2 -0
  71. data/Unity/Assets/Demo/Scripts/GameRightButton.js +18 -0
  72. data/Unity/Assets/Demo/Scripts/GameRightButton.js.meta +2 -0
  73. data/Unity/Assets/Demo/Scripts/GameUpButton.js +18 -0
  74. data/Unity/Assets/Demo/Scripts/GameUpButton.js.meta +2 -0
  75. data/Unity/Assets/Demo/Scripts/OnTouchGUIElementDown.js +34 -0
  76. data/Unity/Assets/Demo/Scripts/OnTouchGUIElementDown.js.meta +2 -0
  77. data/Unity/Assets/Demo/Scripts/PlayerData.js +3 -0
  78. data/Unity/Assets/Demo/Scripts/PlayerData.js.meta +2 -0
  79. data/Unity/Assets/Demo/Scripts/SokobanController.js +240 -0
  80. data/Unity/Assets/Demo/Scripts/SokobanController.js.meta +2 -0
  81. data/Unity/Assets/Demo/Scripts/TakeScreenShot.js +26 -0
  82. data/Unity/Assets/Demo/Scripts/TakeScreenShot.js.meta +2 -0
  83. data/Unity/Assets/Demo/Scripts/TitleOptionsButton.js +8 -0
  84. data/Unity/Assets/Demo/Scripts/TitleOptionsButton.js.meta +2 -0
  85. data/Unity/Assets/Demo/Scripts/TitleOptionsOkButton.js +8 -0
  86. data/Unity/Assets/Demo/Scripts/TitleOptionsOkButton.js.meta +2 -0
  87. data/Unity/Assets/Demo/Scripts/TitlePlayButton.js +6 -0
  88. data/Unity/Assets/Demo/Scripts/TitlePlayButton.js.meta +2 -0
  89. data/Unity/Assets/Demo/Scripts/TitleQuitButton.js +7 -0
  90. data/Unity/Assets/Demo/Scripts/TitleQuitButton.js.meta +2 -0
  91. data/Unity/Assets/Demo/Scripts/UpdateMovesLabel.js +26 -0
  92. data/Unity/Assets/Demo/Scripts/UpdateMovesLabel.js.meta +2 -0
  93. data/Unity/Assets/Demo/Scripts/UpdatePlayerName.js +14 -0
  94. data/Unity/Assets/Demo/Scripts/UpdatePlayerName.js.meta +2 -0
  95. data/Unity/Assets/Demo/Scripts.meta +2 -0
  96. data/Unity/Assets/Demo/demo_game.unity +0 -0
  97. data/Unity/Assets/Demo/demo_game.unity.meta +2 -0
  98. data/Unity/Assets/Demo/demo_title.unity +0 -0
  99. data/Unity/Assets/Demo/demo_title.unity.meta +2 -0
  100. data/Unity/Assets/Demo.meta +2 -0
  101. data/Unity/Assets/Plugins/CukunityInstance.prefab +0 -0
  102. data/Unity/Assets/Plugins/CukunityInstance.prefab.meta +2 -0
  103. data/Unity/Assets/Plugins/CukunityServer.cs +871 -0
  104. data/Unity/Assets/Plugins/CukunityServer.cs.meta +2 -0
  105. data/Unity/Assets/Plugins/JsonFx.Json.dll +0 -0
  106. data/Unity/Assets/Plugins/JsonFx.Json.dll.meta +2 -0
  107. data/Unity/Assets/Plugins/iOS/CukunityServer.mm +242 -0
  108. data/Unity/Assets/Plugins/iOS/CukunityServer.mm.meta +2 -0
  109. data/Unity/Assets/Plugins/iOS.meta +2 -0
  110. data/Unity/Assets/Plugins.meta +2 -0
  111. data/Unity/Library/AudioManager.asset +0 -0
  112. data/Unity/Library/BuildSettings.asset +0 -0
  113. data/Unity/Library/DynamicsManager.asset +0 -0
  114. data/Unity/Library/EditorBuildSettings.asset +0 -0
  115. data/Unity/Library/EditorSettings.asset +0 -0
  116. data/Unity/Library/EditorUserBuildSettings.asset +0 -0
  117. data/Unity/Library/EditorUserSettings.asset +0 -0
  118. data/Unity/Library/InputManager.asset +0 -0
  119. data/Unity/Library/InspectorExpandedItems.asset +0 -0
  120. data/Unity/Library/MonoManager.asset +0 -0
  121. data/Unity/Library/NetworkManager.asset +0 -0
  122. data/Unity/Library/ProjectSettings.asset +0 -0
  123. data/Unity/Library/QualitySettings.asset +0 -0
  124. data/Unity/Library/TagManager.asset +0 -0
  125. data/Unity/Library/TimeManager.asset +0 -0
  126. data/Unity/ProjectSettings/AudioManager.asset +0 -0
  127. data/Unity/ProjectSettings/DynamicsManager.asset +0 -0
  128. data/Unity/ProjectSettings/EditorBuildSettings.asset +0 -0
  129. data/Unity/ProjectSettings/EditorSettings.asset +0 -0
  130. data/Unity/ProjectSettings/InputManager.asset +0 -0
  131. data/Unity/ProjectSettings/NavMeshLayers.asset +0 -0
  132. data/Unity/ProjectSettings/NetworkManager.asset +0 -0
  133. data/Unity/ProjectSettings/ProjectSettings.asset +0 -0
  134. data/Unity/ProjectSettings/QualitySettings.asset +0 -0
  135. data/Unity/ProjectSettings/TagManager.asset +0 -0
  136. data/Unity/ProjectSettings/TimeManager.asset +0 -0
  137. data/VERSION +1 -0
  138. data/bin/cukunity +7 -0
  139. data/cukunity.gemspec +298 -0
  140. data/features/get_scene_command.feature +39 -0
  141. data/features/i18n/ja/sokoban_clear.feature +109 -0
  142. data/features/install.feature +23 -0
  143. data/features/keyboard_input.feature +26 -0
  144. data/features/load_level_command.feature +36 -0
  145. data/features/run.feature +33 -0
  146. data/features/select_visible.feature +29 -0
  147. data/features/support/env.rb +11 -0
  148. data/features/touch_input.feature +96 -0
  149. data/lib/cukunity/cli/argument_parser.rb +103 -0
  150. data/lib/cukunity/cli/bootstrap_command.rb +39 -0
  151. data/lib/cukunity/cli/doctor_command.rb +150 -0
  152. data/lib/cukunity/cli/features_command.rb +35 -0
  153. data/lib/cukunity/cli/main.rb +18 -0
  154. data/lib/cukunity/cli/options.rb +8 -0
  155. data/lib/cukunity/cli.rb +11 -0
  156. data/lib/cukunity/cucumber/step_definitions/get_scene_command_steps.rb +37 -0
  157. data/lib/cukunity/cucumber/step_definitions/i18n/ja/get_scene_command_steps.rb +37 -0
  158. data/lib/cukunity/cucumber/step_definitions/i18n/ja/install_steps.rb +25 -0
  159. data/lib/cukunity/cucumber/step_definitions/i18n/ja/keyboard_input_steps.rb +17 -0
  160. data/lib/cukunity/cucumber/step_definitions/i18n/ja/load_level_command_steps.rb +17 -0
  161. data/lib/cukunity/cucumber/step_definitions/i18n/ja/run_steps.rb +25 -0
  162. data/lib/cukunity/cucumber/step_definitions/i18n/ja/select_visible_steps.rb +9 -0
  163. data/lib/cukunity/cucumber/step_definitions/i18n/ja/touch_input_steps.rb +19 -0
  164. data/lib/cukunity/cucumber/step_definitions/i18n/ja.rb +9 -0
  165. data/lib/cukunity/cucumber/step_definitions/install_steps.rb +33 -0
  166. data/lib/cukunity/cucumber/step_definitions/keyboard_input_steps.rb +13 -0
  167. data/lib/cukunity/cucumber/step_definitions/load_level_command_steps.rb +17 -0
  168. data/lib/cukunity/cucumber/step_definitions/run_steps.rb +32 -0
  169. data/lib/cukunity/cucumber/step_definitions/select_visible_steps.rb +15 -0
  170. data/lib/cukunity/cucumber/step_definitions/touch_input_steps.rb +18 -0
  171. data/lib/cukunity/cucumber/support/platform_helpers.rb +105 -0
  172. data/lib/cukunity/cucumber/support/spec_helpers.rb +21 -0
  173. data/lib/cukunity/cucumber/support/transforms.rb +5 -0
  174. data/lib/cukunity/cucumber/support/unity_helpers.rb +35 -0
  175. data/lib/cukunity/cucumber.rb +28 -0
  176. data/lib/cukunity/drivers/android/app.rb +43 -0
  177. data/lib/cukunity/drivers/android/appkiller/.gitignore +9 -0
  178. data/lib/cukunity/drivers/android/appkiller/AndroidManifest.xml +17 -0
  179. data/lib/cukunity/drivers/android/appkiller/ant.properties +17 -0
  180. data/lib/cukunity/drivers/android/appkiller/build.xml +85 -0
  181. data/lib/cukunity/drivers/android/appkiller/proguard.cfg +40 -0
  182. data/lib/cukunity/drivers/android/appkiller/project.properties +11 -0
  183. data/lib/cukunity/drivers/android/appkiller/res/layout/main.xml +13 -0
  184. data/lib/cukunity/drivers/android/appkiller/res/values/strings.xml +4 -0
  185. data/lib/cukunity/drivers/android/appkiller/src/cukunity/appkiller/AppKiller.java +29 -0
  186. data/lib/cukunity/drivers/android/keyboard_input.rb +149 -0
  187. data/lib/cukunity/drivers/android/keycodes.rb +531 -0
  188. data/lib/cukunity/drivers/android/monkey_client.rb +65 -0
  189. data/lib/cukunity/drivers/android/process.rb +103 -0
  190. data/lib/cukunity/drivers/android/touch_input.rb +24 -0
  191. data/lib/cukunity/drivers/android/tunnel_client_methods.rb +22 -0
  192. data/lib/cukunity/drivers/android/unity_client.rb +27 -0
  193. data/lib/cukunity/drivers/android/utils.rb +50 -0
  194. data/lib/cukunity/drivers/android.rb +9 -0
  195. data/lib/cukunity/drivers/iOS/app.rb +32 -0
  196. data/lib/cukunity/drivers/iOS/keyboard_input.rb +104 -0
  197. data/lib/cukunity/drivers/iOS/process.rb +42 -0
  198. data/lib/cukunity/drivers/iOS/touch_input.rb +18 -0
  199. data/lib/cukunity/drivers/iOS/tunnel_client_methods.rb +27 -0
  200. data/lib/cukunity/drivers/iOS/uiautomation.rb +97 -0
  201. data/lib/cukunity/drivers/iOS/uiautomation_master.rb +152 -0
  202. data/lib/cukunity/drivers/iOS/uiautomation_slave.js +238 -0
  203. data/lib/cukunity/drivers/iOS/uiautomation_slave.rb +41 -0
  204. data/lib/cukunity/drivers/iOS/unity_client.rb +27 -0
  205. data/lib/cukunity/drivers/iOS/utils.rb +46 -0
  206. data/lib/cukunity/drivers/iOS.rb +9 -0
  207. data/lib/cukunity/exceptions.rb +24 -0
  208. data/lib/cukunity/keyboard_input_methods.rb +35 -0
  209. data/lib/cukunity/request_line_response_client_methods.rb +80 -0
  210. data/lib/cukunity/tasks/cucumber.rake +22 -0
  211. data/lib/cukunity/touch_input_methods.rb +19 -0
  212. data/lib/cukunity/unity/client_methods.rb +46 -0
  213. data/lib/cukunity/unity/command_facade.rb +67 -0
  214. data/lib/cukunity/unity/component.rb +39 -0
  215. data/lib/cukunity/unity/gameobject.rb +53 -0
  216. data/lib/cukunity/unity/hint.rb +27 -0
  217. data/lib/cukunity/unity/json_container.rb +48 -0
  218. data/lib/cukunity/unity/level.rb +6 -0
  219. data/lib/cukunity/unity/scene.rb +18 -0
  220. data/lib/cukunity/unity/screen.rb +45 -0
  221. data/lib/cukunity/unity/screen_hint_methods.rb +61 -0
  222. data/lib/cukunity/unity/text_hint_methods.rb +17 -0
  223. data/lib/cukunity/unity.rb +11 -0
  224. data/lib/cukunity/utils.rb +56 -0
  225. data/lib/cukunity.rb +19 -0
  226. 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