acouchi 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -13,6 +13,8 @@ Requirements
13
13
 
14
14
  * [Android SDK](http://developer.android.com/sdk/installing/index.html) (Make sure to add SDK/tools and SDK/platform-tools to your PATH)
15
15
  * [apktool](http://code.google.com/p/android-apktool/)
16
+ * [ant](http://ant.apache.org/manual/install.html)
17
+ * A running Android emulator, or a plugged in Android device
16
18
 
17
19
  The Build Process
18
20
  -----------------
@@ -38,6 +40,7 @@ Cucumber
38
40
 
39
41
  require "acouchi"
40
42
  require "acouchi/cucumber"
43
+ require "acouchi/rspec/matchers"
41
44
 
42
45
  configuration = Acouchi::Configuration.from_json(File.read("acouchi_configuration.json"))
43
46
  Acouchi::Cucumber.prepare(configuration)
@@ -70,3 +73,18 @@ Cucumber
70
73
  Cucumber::Rake::Task.new(:features) do |t|
71
74
  t.cucumber_opts = "features --format pretty"
72
75
  end
76
+
77
+ Troubleshooting
78
+ ---------------
79
+
80
+ ### Problems clicking on buttons and text
81
+
82
+ If it seems that buttons/text aren't being clicked properly, you need to add the following xml to your AndroidManifest.xml:
83
+
84
+ ```
85
+ <uses-sdk android:targetSdkVersion="SDK_VERSION" />
86
+ ```
87
+
88
+ Where SDK_VERSION is the version of the Android SDK you are using. Version numbers can be found [here](http://developer.android.com/reference/android/os/Build.VERSION_CODES.html)
89
+
90
+ For example, Android 4.0 uses version 14, Android 4.0.3 uses version 15 and Android 4.1 uses version 16.
@@ -18,4 +18,6 @@ Gem::Specification.new do |gem|
18
18
  gem.require_paths = ["lib"]
19
19
  gem.add_runtime_dependency "httparty"
20
20
  gem.add_runtime_dependency "childprocess"
21
+ gem.add_runtime_dependency "which"
22
+ gem.add_runtime_dependency "nokogiri"
21
23
  end
@@ -3,6 +3,9 @@
3
3
  package="com.acouchi.sample"
4
4
  android:versionCode="1"
5
5
  android:versionName="1.0">
6
+
7
+ <uses-sdk android:targetSdkVersion="16" />
8
+
6
9
  <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
7
10
  <activity android:name="StartupActivity"
8
11
  android:label="@string/app_name">
@@ -1,6 +1,7 @@
1
1
  require "cucumber"
2
2
  require "cucumber/rake/task"
3
3
  require "acouchi"
4
+ require "acouchi/rspec/matchers"
4
5
 
5
6
  desc "build project with Acouchi code included"
6
7
  task :build do
@@ -13,3 +13,23 @@ end
13
13
  Then /^I do not see "(.*?)"$/ do |text|
14
14
  page.should_not have_text text
15
15
  end
16
+
17
+ Then /^I see one button$/ do
18
+ page.buttons.size.should == 1
19
+ end
20
+
21
+ Then /^it has the text "(.*?)"$/ do |text|
22
+ page.should have_button text
23
+ end
24
+
25
+ When /^I click the text "(.*?)"$/ do |text|
26
+ page.click_text(text)
27
+ end
28
+
29
+ Then /^I see the content "(.*?)"$/ do |content|
30
+ page.should have_content content
31
+ end
32
+
33
+ Then /^I see all views$/ do
34
+ p page.views
35
+ end
@@ -1,5 +1,6 @@
1
1
  require "acouchi"
2
2
  require "acouchi/cucumber"
3
+ require "acouchi/rspec/matchers"
3
4
 
4
5
  configuration = Acouchi::Configuration.from_json(File.read("acouchi_configuration.json"))
5
6
  Acouchi::Cucumber.prepare(configuration)
@@ -5,3 +5,18 @@ Feature: Write Text
5
5
  Then I see "this is some text"
6
6
  When I clear the text
7
7
  Then I do not see "this is some text"
8
+
9
+ Scenario: Clickety Click
10
+ Then I see one button
11
+ And it has the text "clicky click"
12
+
13
+ Scenario: Click text
14
+ When I click the text "click this text"
15
+ And I click the text "WELL DONE"
16
+ Then I see "WELL DONE"
17
+
18
+ Scenario: Content
19
+ Then I see the content "click this text"
20
+
21
+ Scenario: Views
22
+ Then I see all views
@@ -8,5 +8,23 @@
8
8
  android:layout_width="fill_parent"
9
9
  android:layout_height="wrap_content"
10
10
  />
11
- </LinearLayout>
11
+ <Button
12
+ android:layout_width="fill_parent"
13
+ android:layout_height="wrap_content"
14
+ android:text="clicky click"
15
+ />
16
+
17
+ <TextView
18
+ android:id="@+id/clickThisText"
19
+ android:layout_width="fill_parent"
20
+ android:layout_height="wrap_content"
21
+ android:text="click this text"
22
+ />
12
23
 
24
+ <ImageView
25
+ android:id="@+id/clickThisImageView"
26
+ android:layout_width="fill_parent"
27
+ android:layout_height="wrap_content"
28
+ android:text="click this text"
29
+ />
30
+ </LinearLayout>
@@ -2,6 +2,10 @@ package com.acouchi.sample;
2
2
 
3
3
  import android.app.Activity;
4
4
  import android.os.Bundle;
5
+ import android.widget.TextView;
6
+ import android.view.View.OnClickListener;
7
+ import android.view.View;
8
+ import android.widget.ImageView;
5
9
 
6
10
  public class StartupActivity extends Activity
7
11
  {
@@ -11,5 +15,22 @@ public class StartupActivity extends Activity
11
15
  {
12
16
  super.onCreate(savedInstanceState);
13
17
  setContentView(R.layout.main);
18
+
19
+ TextView clickThis = (TextView)findViewById(R.id.clickThisText);
20
+ clickThis.setOnClickListener(new OnClickListener(){
21
+ public void onClick(View v) {
22
+ TextView clickThis = (TextView)findViewById(R.id.clickThisText);
23
+ clickThis.setText("WELL DONE");
24
+ }
25
+ });
26
+
27
+
28
+ ImageView imageView = (ImageView)findViewById(R.id.clickThisImageView);
29
+ imageView.setOnClickListener(new OnClickListener(){
30
+ public void onClick(View v) {
31
+ TextView clickThis = (TextView)findViewById(R.id.clickThisText);
32
+ clickThis.setText("You click an ImageView");
33
+ }
34
+ });
14
35
  }
15
36
  }
Binary file
@@ -5,3 +5,4 @@ require "acouchi/test_runner"
5
5
  require "acouchi/configuration"
6
6
  require "acouchi/apk_modifier"
7
7
  require "acouchi/process_launcher"
8
+ require "acouchi/which"
@@ -5,10 +5,9 @@ module Acouchi
5
5
  class ApkModifier
6
6
  def initialize apk
7
7
  @apk = apk
8
- @apk_tool = `which apktool`.strip
9
8
  @output_path = "#{Dir.tmpdir}/#{SecureRandom.uuid}/"
10
9
 
11
- if @apk_tool.empty?
10
+ unless apktool
12
11
  puts "Couldn't find a valid apktool. Please install apktool from http://code.google.com/p/android-apktool/"
13
12
  exit
14
13
  end
@@ -29,22 +28,37 @@ module Acouchi
29
28
  end
30
29
 
31
30
  private
32
- def apktool command
33
- puts "#{@apk_tool} #{command}"
34
- system "#{@apk_tool} #{command}"
31
+ def apktool
32
+ @apktool ||= Which.find_executable("apktool.bat", "apktool")
33
+ end
34
+
35
+ def execute_apktool command
36
+ ProcessLauncher.new(apktool, command)
35
37
  end
36
38
 
37
39
  def decompile_apk
38
- apktool "d #{@apk} #{@output_path}"
40
+ ProcessLauncher.new(apktool, "d", @apk, @output_path).start_and_crash_if_process_fails
39
41
  end
40
42
 
41
43
  def compile_apk
42
- apktool "b #{@output_path}"
44
+ ProcessLauncher.new(apktool, "b", @output_path).start_and_crash_if_process_fails
43
45
  end
44
46
 
45
47
  def sign_apk_in_debug_mode
46
48
  @new_apk = File.join(@output_path, "dist", File.basename(@apk))
47
- system "jarsigner -keystore ~/.android/debug.keystore -storepass android -keypass android #{@new_apk} androiddebugkey"
49
+ jarsigner = Which.which?("jarsigner.exe") || Which.which?("jarsigner")
50
+ debug_keystore = File.join(ENV["HOME"], ".android", "debug.keystore")
51
+ ProcessLauncher.new(
52
+ jarsigner,
53
+ "-keystore",
54
+ debug_keystore,
55
+ "-storepass",
56
+ "android",
57
+ "-keypass",
58
+ "android",
59
+ @new_apk,
60
+ "androiddebugkey"
61
+ ).start_and_crash_if_process_fails
48
62
  end
49
63
 
50
64
  def overwrite_original_apk
@@ -1,16 +1,29 @@
1
+ Before do
2
+ Acouchi::Cucumber.before
3
+ end
4
+
5
+ After do
6
+ Acouchi::Cucumber.after
7
+ end
8
+
1
9
  module Acouchi
2
10
  module Cucumber
3
11
  def self.page
4
12
  @page
5
13
  end
6
14
 
7
- def self.prepare configuration
8
- @test_runner = TestRunner.new(configuration)
15
+ def self.before
16
+ @test_runner = TestRunner.new(@configuration)
9
17
  @test_runner.start
10
18
  @page = Solo.new
11
- at_exit do
12
- @test_runner.stop
13
- end
19
+ end
20
+
21
+ def self.after
22
+ @test_runner.stop
23
+ end
24
+
25
+ def self.prepare configuration
26
+ @configuration = configuration
14
27
  end
15
28
  end
16
29
  end
@@ -7,15 +7,31 @@ module Acouchi
7
7
  end
8
8
 
9
9
  def start
10
+ write_out_arguments
10
11
  @process.start
11
12
  @process.wait
12
13
  end
13
14
 
15
+ def start_in_background
16
+ write_out_arguments
17
+ @process.start
18
+ end
19
+
20
+ def stop
21
+ @process.stop
22
+ end
23
+
14
24
  def start_and_crash_if_process_fails
15
25
  start
26
+
16
27
  if @process.crashed?
17
28
  raise "A process exited with a non-zero exit code.\nThe command executed was \"#{@arguments.join(" ")}\""
18
29
  end
19
30
  end
31
+
32
+ private
33
+ def write_out_arguments
34
+ p @arguments
35
+ end
20
36
  end
21
37
  end
@@ -76,15 +76,23 @@ module Acouchi
76
76
  end
77
77
  end
78
78
 
79
+ def adb
80
+ Which.which?("adb.exe") || Which.which?("adb")
81
+ end
82
+
83
+ def ant
84
+ Which.which?("ant.exe") || Which.which?("ant")
85
+ end
86
+
79
87
  def build_apk
80
88
  Dir.chdir configuration.project_path do
81
- ProcessLauncher.new("ant", "clean", "debug").start_and_crash_if_process_fails
89
+ ProcessLauncher.new(ant, "clean", "debug").start_and_crash_if_process_fails
82
90
  end
83
91
  end
84
92
 
85
93
  def install_apk apk_path
86
- ProcessLauncher.new("adb", "uninstall", configuration.target_package).start
87
- ProcessLauncher.new("adb", "install", apk_path).start_and_crash_if_process_fails
94
+ ProcessLauncher.new(adb, "uninstall", configuration.target_package).start
95
+ ProcessLauncher.new(adb, "install", apk_path).start_and_crash_if_process_fails
88
96
  end
89
97
  end
90
98
  end
@@ -0,0 +1,10 @@
1
+ require "rspec"
2
+
3
+ RSpec::Matchers.define :have_content do |expected|
4
+ match do |actual|
5
+ actual.content.include? expected
6
+ end
7
+ failure_message_for_should do |actual|
8
+ "expected \"#{actual.content.join("\n")}\" to include \"#{expected}\""
9
+ end
10
+ end
@@ -35,11 +35,67 @@ module Acouchi
35
35
  ])
36
36
  end
37
37
 
38
+ def buttons
39
+ call_method("getCurrentButtons")
40
+ end
41
+
42
+ def has_button? text
43
+ buttons.any? { |b| b["text"] == text }
44
+ end
45
+
46
+ def content
47
+ call_method("getCurrentContent")
48
+ end
49
+
50
+ def click_text text, options={}
51
+ options = {
52
+ :match => 1,
53
+ :auto_scroll => true
54
+ }.merge(options)
55
+
56
+ call_method("clickOnText", [
57
+ {:type => "java.lang.String", :value => text},
58
+ {:type => "int", :value => options[:match]},
59
+ {:type => "boolean", :value => options[:auto_scroll]}
60
+ ])
61
+ end
62
+
63
+ def views
64
+ call_method("getViews")
65
+ end
66
+
67
+ def click_view id
68
+ call_method("clickOnViewById", [
69
+ {:type => "int", :value => id},
70
+ ])
71
+ end
72
+
73
+ def scroll_up
74
+ call_method("scrollUp")
75
+ end
76
+
77
+ def scroll_down
78
+ call_method("scrollDown")
79
+ end
80
+
81
+ def scroll_up_list index=0
82
+ call_method("scrollUpList", [
83
+ {:type => "int", :value => index}
84
+ ])
85
+ end
86
+
87
+ def scroll_down_list index=0
88
+ call_method("scrollDownList", [
89
+ {:type => "int", :value => index}
90
+ ])
91
+ end
92
+
38
93
  private
39
- def call_method name, arguments
94
+ def call_method name, arguments = []
40
95
  options = { :body => {:parameters => arguments.to_json} }
41
- json = JSON.parse(HTTParty.post("http://127.0.0.1:7103/execute_method/#{name}", options).body)
42
- if json["emptyResult"]
96
+ response = HTTParty.post("http://127.0.0.1:7103/execute_method/#{name}", options)
97
+ json = JSON.parse(response.body, :max_nesting => 100)
98
+ if json.empty?
43
99
  nil
44
100
  else
45
101
  json["result"]
@@ -6,10 +6,15 @@ module Acouchi
6
6
  @configuration = configuration
7
7
  end
8
8
 
9
+ def adb
10
+ @adb ||= Which.find_executable("adb")
11
+ end
12
+
9
13
  def start
10
- system "adb forward tcp:7103 tcp:7103"
11
- @test_runner_process = ChildProcess.build("adb", "shell", "am", "instrument", "-w", "#{@configuration.target_package}/android.test.InstrumentationTestRunner")
12
- @test_runner_process.start
14
+ force_stop
15
+ ProcessLauncher.new(adb, "forward", "tcp:7103", "tcp:7103").start_and_crash_if_process_fails
16
+ @test_runner_process = ProcessLauncher.new(adb, "shell", "am", "instrument", "-w", "#{@configuration.target_package}/android.test.InstrumentationTestRunner")
17
+ @test_runner_process.start_in_background
13
18
 
14
19
  while ready? == false
15
20
  sleep 0.1
@@ -18,16 +23,16 @@ module Acouchi
18
23
 
19
24
  def stop
20
25
  HTTParty.get("http://127.0.0.1:7103/finish") rescue nil
21
- begin
22
- @test_runner_process.poll_for_exit 10
23
- rescue ChildProcess::TimeoutError
24
- @test_runner_process.stop
25
- end
26
+ @test_runner_process.stop
26
27
  end
27
28
 
28
29
  private
29
30
  def ready?
30
31
  HTTParty.get("http://127.0.0.1:7103/").body == "Acouchi" rescue false
31
32
  end
33
+
34
+ def force_stop
35
+ ChildProcess.build(adb, "shell", "am", "force-stop", @configuration.target_package).start.wait
36
+ end
32
37
  end
33
38
  end
@@ -1,3 +1,3 @@
1
1
  module Acouchi
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1,22 @@
1
+ module Acouchi
2
+ class Which
3
+ def self.which? command
4
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
5
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
6
+ exts.each { |ext|
7
+ exe = "#{path}/#{command}#{ext}"
8
+ return exe if File.executable? exe
9
+ }
10
+ end
11
+ return nil
12
+ end
13
+
14
+ def self.find_executable *aliases
15
+ if executable = aliases.find {|a| which? a}
16
+ executable
17
+ else
18
+ raise %{Couldn't find any matches for the aliases "#{aliases.join(", ")}"}
19
+ end
20
+ end
21
+ end
22
+ end
@@ -65,9 +65,9 @@ Methods
65
65
 
66
66
  void clickOnScreen(float x, float y) Clicks on a given coordinate on the screen.
67
67
 
68
- void clickOnText(String text) Clicks on a View displaying a given text.
69
- void clickOnText(String text, int match) Clicks on a View displaying a given text.
70
- void clickOnText(String text, int match, boolean scroll) Clicks on a View displaying a given text.
68
+ * void clickOnText(String text) Clicks on a View displaying a given text.
69
+ * void clickOnText(String text, int match) Clicks on a View displaying a given text.
70
+ * void clickOnText(String text, int match, boolean scroll) Clicks on a View displaying a given text.
71
71
 
72
72
  void clickOnToggleButton(String name) Clicks on a ToggleButton with a given text.
73
73
 
@@ -11,14 +11,22 @@ import java.util.concurrent.locks.ReentrantLock;
11
11
 
12
12
  import android.app.Instrumentation;
13
13
  import android.app.Activity;
14
+ import android.widget.TextView;
15
+ import android.view.View;
14
16
  import java.lang.reflect.Method;
17
+ import java.lang.reflect.InvocationTargetException;
15
18
  import org.json.JSONArray;
16
19
  import org.json.JSONObject;
17
20
  import org.json.JSONException;
18
21
  import java.util.ArrayList;
22
+ import java.util.Map;
23
+ import java.util.HashMap;
24
+ import java.util.Collection;
19
25
  import java.io.PrintWriter;
20
26
  import java.io.StringWriter;
21
27
  import java.io.Writer;
28
+ import com.google.gson.Gson;
29
+ import android.widget.Button;
22
30
 
23
31
  public class Acouchi extends NanoHTTPD
24
32
  {
@@ -64,16 +72,50 @@ public class Acouchi extends NanoHTTPD
64
72
  else if (uri.startsWith("/execute_method"))
65
73
  {
66
74
  String methodName = uri.replace("/execute_method/", "");
67
- return ExecuteMethod(methodName, params.getProperty("parameters"));
75
+ try {
76
+ return ExecuteMethod(methodName, params.getProperty("parameters"));
77
+ } catch (Throwable throwable) {
78
+ return showException(throwable);
79
+ }
68
80
  }
69
81
 
70
82
  return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, "Acouchi");
71
83
  }
72
84
 
73
- private NanoHTTPD.Response ExecuteMethod(String methodName, String json)
85
+ private NanoHTTPD.Response ExecuteMethod(String methodName, String json) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, JSONException, ClassNotFoundException
74
86
  {
75
- try {
76
- JSONArray jsonArray = new JSONArray(json);
87
+ if (methodName.equals("getCurrentContent"))
88
+ return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, getCurrentContentAsJson());
89
+
90
+ JSONArray jsonArray = new JSONArray(json);
91
+
92
+ if (methodName.equals("clickOnText")) {
93
+ String text = jsonArray.getJSONObject(0).getString("value");
94
+ int match = jsonArray.getJSONObject(1).getInt("value");
95
+ boolean scroll = jsonArray.getJSONObject(2).getBoolean("value");
96
+ try {
97
+ solo.clickOnText(text, match, scroll);
98
+ } catch (Exception exception) {
99
+ return showException(exception);
100
+ }
101
+ return displayMethodResultAsJson(methodName, null);
102
+ } else if (methodName.equals("scrollUpList")) {
103
+ int index = jsonArray.getJSONObject(0).getInt("value");
104
+ try {
105
+ solo.scrollUpList(index);
106
+ } catch (Exception exception) {
107
+ return showException(exception);
108
+ }
109
+ return displayMethodResultAsJson(methodName, null);
110
+ } else if (methodName.equals("scrollDownList")) {
111
+ int index = jsonArray.getJSONObject(0).getInt("value");
112
+ try {
113
+ solo.scrollDownList(index);
114
+ } catch (Exception exception) {
115
+ return showException(exception);
116
+ }
117
+ return displayMethodResultAsJson(methodName, null);
118
+ } else {
77
119
  Class[] parameterTypes = new Class[jsonArray.length()];
78
120
  Object[] parameters = new Object[jsonArray.length()];
79
121
 
@@ -83,24 +125,51 @@ public class Acouchi extends NanoHTTPD
83
125
  parameterTypes[i] = getClassType(jsonObject.getString("type"));
84
126
  parameters[i] = getConvertedValue(jsonObject.getString("type"), jsonObject.getString("value"));
85
127
  }
128
+ Object result = executeMethodOnSomeClass(methodName, parameterTypes, parameters);
129
+ return displayMethodResultAsJson(methodName, result);
130
+ }
131
+ }
132
+
133
+ private class MethodExecutor
134
+ {
135
+ private Solo solo;
136
+ private Object[] parameters;
86
137
 
138
+ public MethodExecutor(Solo solo, Object[] parameters)
139
+ {
140
+ this.solo = solo;
141
+ this.parameters = parameters;
142
+ }
143
+ }
144
+
145
+ private Object executeMethodOnSomeClass(String methodName, Class[] parameterTypes, Object[] parameters) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
146
+ {
147
+ try {
87
148
  Method method = solo.getClass().getMethod(methodName, parameterTypes);
88
- return displayMethodResultAsJson(method.invoke(solo, parameters));
149
+ return method.invoke(solo, parameters);
89
150
  } catch (Exception exception) {
90
- return showException(exception);
91
151
  }
152
+
153
+ Method method = this.getClass().getMethod(methodName, parameterTypes);
154
+ return method.invoke(this, parameters);
92
155
  }
93
156
 
94
- private NanoHTTPD.Response displayMethodResultAsJson(Object result)
157
+ private NanoHTTPD.Response displayMethodResultAsJson(String methodName, Object result)
95
158
  {
96
159
  try {
97
160
  JSONObject object = new JSONObject();
98
161
 
99
- if (result == null) {
100
- object.put("emptyResult", true);
101
- } else {
162
+ if (methodName.equals("getCurrentButtons"))
163
+ return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, getButtonsAsJson((ArrayList<Button>)result));
164
+
165
+ if (methodName.equals("getViews"))
166
+ return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, getViewsAsJson((ArrayList<View>)result));
167
+
168
+ if (methodName.equals("getView"))
169
+ return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, getViewAsJson((View)result));
170
+
171
+ if (result != null)
102
172
  object.put("result", result);
103
- }
104
173
 
105
174
  return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, object.toString());
106
175
  } catch (JSONException e) {
@@ -108,6 +177,77 @@ public class Acouchi extends NanoHTTPD
108
177
  }
109
178
  }
110
179
 
180
+ public void clickOnViewById(int id)
181
+ {
182
+ solo.clickOnView(solo.getView(id));
183
+ }
184
+
185
+ private String getViewsAsJson(ArrayList<View> views)
186
+ {
187
+ ArrayList<JsonView> jsonViews = new ArrayList<JsonView>();
188
+ for(int index = 0; index < views.size(); index++)
189
+ jsonViews.add(new JsonView(views.get(index)));
190
+ JsonResult result = new JsonResult(jsonViews);
191
+ return new Gson().toJson(result);
192
+ }
193
+
194
+ private String getViewAsJson(View view)
195
+ {
196
+ JsonResult result = new JsonResult(new JsonView(view));
197
+ return new Gson().toJson(result);
198
+ }
199
+
200
+ private class JsonView
201
+ {
202
+ private int id;
203
+ private String className;
204
+
205
+ public JsonView(View view)
206
+ {
207
+ id = view.getId();
208
+ className = view.getClass().toString();
209
+ }
210
+ }
211
+
212
+ private String getButtonsAsJson(ArrayList<Button> buttons)
213
+ {
214
+ ArrayList<JsonButton> jsonButtons = new ArrayList<JsonButton>();
215
+ for(int index = 0; index < buttons.size(); index++)
216
+ jsonButtons.add(new JsonButton(buttons.get(index)));
217
+ JsonResult result = new JsonResult(jsonButtons);
218
+ return new Gson().toJson(result);
219
+ }
220
+
221
+ private String getCurrentContentAsJson()
222
+ {
223
+ ArrayList<TextView> currentTextViews = solo.getCurrentTextViews(null);
224
+ ArrayList<String> content = new ArrayList<String>();
225
+ for (TextView textView: currentTextViews)
226
+ content.add(textView.getText().toString());
227
+ JsonResult result = new JsonResult(content);
228
+ return new Gson().toJson(result);
229
+ }
230
+
231
+ private class JsonResult
232
+ {
233
+ private Object result;
234
+
235
+ public JsonResult(Object result)
236
+ {
237
+ this.result = result;
238
+ }
239
+ }
240
+
241
+ private class JsonButton
242
+ {
243
+ private String text;
244
+
245
+ public JsonButton(Button button)
246
+ {
247
+ text = button.getText().toString();
248
+ }
249
+ }
250
+
111
251
  private Class getClassType(String name) throws java.lang.ClassNotFoundException
112
252
  {
113
253
  if (name.equals("int")) return int.class;
@@ -130,15 +270,11 @@ public class Acouchi extends NanoHTTPD
130
270
  return value;
131
271
  }
132
272
 
133
- private NanoHTTPD.Response showException(Exception exception)
273
+ private NanoHTTPD.Response showException(Throwable throwable)
134
274
  {
135
- return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, "exception: " + exception.toString() + "\n" + getStackTrace(exception));
136
- }
137
-
138
- private static String getStackTrace(Throwable aThrowable) {
139
275
  final Writer result = new StringWriter();
140
276
  final PrintWriter printWriter = new PrintWriter(result);
141
- aThrowable.printStackTrace(printWriter);
142
- return result.toString();
277
+ throwable.printStackTrace(printWriter);
278
+ return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, "error: " + throwable.getMessage() + "\n" + result.toString());
143
279
  }
144
280
  }
@@ -2,11 +2,9 @@ package com.acouchi;
2
2
 
3
3
  import android.test.ActivityInstrumentationTestCase2;
4
4
  import com.jayway.android.robotium.solo.Solo;
5
-
6
5
  import android.app.Activity;
7
- // import com.example.android.notepad.NotesList;
8
-
9
6
  import com.jayway.android.robotium.solo.Solo;
7
+
10
8
  public class TestCase extends ActivityInstrumentationTestCase2 {
11
9
  private Solo solo;
12
10
  private Acouchi acouchi;
@@ -14,7 +12,6 @@ public class TestCase extends ActivityInstrumentationTestCase2 {
14
12
  public TestCase()
15
13
  {
16
14
  super(ACTIVITY_UNDER_TEST.class);
17
- // super("com.example.android.notepad", NotesList.class);
18
15
  }
19
16
 
20
17
  @Override
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acouchi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-04 00:00:00.000000000 Z
12
+ date: 2012-10-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty
@@ -43,6 +43,38 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: which
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: nokogiri
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
46
78
  description: ''
47
79
  email:
48
80
  - andrew.vos@gmail.com
@@ -73,6 +105,7 @@ files:
73
105
  - examples/AcouchiSample/res/layout/main.xml
74
106
  - examples/AcouchiSample/res/values/strings.xml
75
107
  - examples/AcouchiSample/src/com/acouchi/sample/StartupActivity.java
108
+ - jars/gson-2.2.2.jar
76
109
  - jars/robotium-solo-3.4.1.jar
77
110
  - lib/acouchi.rb
78
111
  - lib/acouchi/apk_modifier.rb
@@ -80,9 +113,11 @@ files:
80
113
  - lib/acouchi/cucumber.rb
81
114
  - lib/acouchi/process_launcher.rb
82
115
  - lib/acouchi/project_builder.rb
116
+ - lib/acouchi/rspec/matchers.rb
83
117
  - lib/acouchi/solo.rb
84
118
  - lib/acouchi/test_runner.rb
85
119
  - lib/acouchi/version.rb
120
+ - lib/acouchi/which.rb
86
121
  - not-yet-implemented.md
87
122
  - src/com/acouchi/Acouchi.java
88
123
  - src/com/acouchi/NanoHTTPD.java