ruby_android 0.0.2 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.idea/.name +1 -0
  3. data/.idea/.rakeTasks +7 -0
  4. data/.idea/encodings.xml +5 -0
  5. data/.idea/misc.xml +5 -0
  6. data/.idea/modules.xml +9 -0
  7. data/.idea/ruby_apk.iml +51 -0
  8. data/.idea/scopes/scope_settings.xml +5 -0
  9. data/.idea/vcs.xml +7 -0
  10. data/.idea/workspace.xml +508 -0
  11. data/.travis.yml +4 -0
  12. data/CHANGELOG.md +51 -0
  13. data/Gemfile +3 -3
  14. data/Gemfile.lock +73 -0
  15. data/LICENSE.txt +2 -2
  16. data/Rakefile +42 -1
  17. data/VERSION +1 -0
  18. data/lib/android/apk.rb +207 -0
  19. data/lib/android/axml_parser.rb +173 -0
  20. data/lib/android/dex/access_flag.rb +74 -0
  21. data/lib/android/dex/dex_object.rb +475 -0
  22. data/lib/android/dex/info.rb +151 -0
  23. data/lib/android/dex/utils.rb +45 -0
  24. data/lib/android/dex.rb +92 -0
  25. data/lib/android/layout.rb +44 -0
  26. data/lib/android/manifest.rb +249 -0
  27. data/lib/android/resource.rb +529 -0
  28. data/lib/android/utils.rb +55 -0
  29. data/lib/ruby_apk.rb +7 -0
  30. data/ruby_android.gemspec +103 -17
  31. data/spec/apk_spec.rb +301 -0
  32. data/spec/axml_parser_spec.rb +67 -0
  33. data/spec/data/sample.apk +0 -0
  34. data/spec/data/sample_AndroidManifest.xml +0 -0
  35. data/spec/data/sample_classes.dex +0 -0
  36. data/spec/data/sample_resources.arsc +0 -0
  37. data/spec/data/sample_resources_utf16.arsc +0 -0
  38. data/spec/data/str_resources.arsc +0 -0
  39. data/spec/dex/access_flag_spec.rb +42 -0
  40. data/spec/dex/dex_object_spec.rb +118 -0
  41. data/spec/dex/info_spec.rb +121 -0
  42. data/spec/dex/utils_spec.rb +56 -0
  43. data/spec/dex_spec.rb +59 -0
  44. data/spec/layout_spec.rb +27 -0
  45. data/spec/manifest_spec.rb +221 -0
  46. data/spec/resource_spec.rb +170 -0
  47. data/spec/ruby_apk_spec.rb +4 -0
  48. data/spec/spec_helper.rb +17 -0
  49. data/spec/utils_spec.rb +90 -0
  50. metadata +112 -27
  51. data/.gitignore +0 -14
  52. data/lib/ruby_android/version.rb +0 -3
  53. data/lib/ruby_android.rb +0 -7
@@ -0,0 +1,121 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ shared_context 'with sample_classes.dex', with: :sample_dex do
4
+ let(:dex_path){ File.expand_path(File.dirname(__FILE__) + '/../data/sample_classes.dex') }
5
+ let(:dex_bin){ File.open(dex_path, 'rb') {|f| f.read } }
6
+ let(:dex){ Android::Dex.new(dex_bin) }
7
+ let(:last_class) { dex.classes.last }
8
+ end
9
+ describe Android::Dex::ClassInfo do
10
+ include_context 'with sample_classes.dex'
11
+ context 'about the last class in Dex#classes with sample_classes.dex' do
12
+ let(:last_class) { dex.classes.last }
13
+ its(:name){ should eq 'Lexample/app/sample/SampleCode;' }
14
+ its(:access_flags){ should be_instance_of Android::Dex::ClassAccessFlag }
15
+ its(:super_class){ should eq 'Ljava/lang/Object;' }
16
+ its(:class_data){ should be_instance_of Android::Dex::DexObject::ClassDataItem }
17
+ its(:class_def){ should be_instance_of Android::Dex::DexObject::ClassDefItem }
18
+ its(:definition) { should eq 'public class Lexample/app/sample/SampleCode; extends Ljava/lang/Object;' }
19
+
20
+ subject { last_class }
21
+ describe '#static_fields' do
22
+ subject { last_class.static_fields }
23
+ it { should have(1).item }
24
+ specify { subject[0].should be_instance_of Android::Dex::FieldInfo }
25
+ end
26
+ describe '#instance_fields' do
27
+ subject { last_class.instance_fields }
28
+ it { should have(1).item }
29
+ specify { subject[0].should be_instance_of Android::Dex::FieldInfo }
30
+ end
31
+ describe '#direct_methods' do
32
+ subject { last_class.direct_methods }
33
+ it { should have(3).items }
34
+ specify { subject[0].should be_instance_of Android::Dex::MethodInfo }
35
+ end
36
+ describe '#virtual_methods' do
37
+ subject { last_class.virtual_methods }
38
+ it { should have(18).items }
39
+ specify { subject[0].should be_instance_of Android::Dex::MethodInfo }
40
+ end
41
+ end
42
+ context 'when class_data_item is nil' do
43
+ let(:mock_cls_def) {
44
+ s = double(Android::Dex::DexObject::ClassDefItem)
45
+ s.stub(:'[]').with(anything()).and_return(0)
46
+ s.stub(:class_data_item).and_return(nil)
47
+ s
48
+ }
49
+ let(:class_info) { Android::Dex::ClassInfo.new(mock_cls_def, nil) }
50
+ describe '#static_fields' do
51
+ subject { class_info.static_fields }
52
+ it { should be_kind_of Array }
53
+ it { should be_empty }
54
+ end
55
+ describe '#instance_fields' do
56
+ subject { class_info.instance_fields }
57
+ it { should be_kind_of Array }
58
+ it { should be_empty }
59
+ end
60
+ describe '#direct_methods' do
61
+ subject { class_info.direct_methods }
62
+ it { should be_kind_of Array }
63
+ it { should be_empty }
64
+ end
65
+ describe '#virtual_methods' do
66
+ subject { class_info.virtual_methods }
67
+ it { should be_kind_of Array }
68
+ it { should be_empty }
69
+ end
70
+ end
71
+ end
72
+
73
+ describe Android::Dex::FieldInfo do
74
+ include_context 'with sample_classes.dex'
75
+ context 'about the first static field of the last class with sample_classes.dex'do
76
+ let(:first_static_field) { last_class.static_fields.first }
77
+ subject { first_static_field }
78
+ its(:name) { should eq 'TAG' }
79
+ its(:type) { should eq 'Ljava/lang/String;' }
80
+ describe '#access_flags' do
81
+ subject { first_static_field.access_flags }
82
+ it { should be_instance_of Android::Dex::ClassAccessFlag }
83
+ specify { subject.to_s.should eq 'private static final' }
84
+ end
85
+ describe '#definition' do
86
+ subject { first_static_field.definition }
87
+ it { should eq 'private static final Ljava/lang/String; TAG' }
88
+ end
89
+ end
90
+ end
91
+
92
+ describe Android::Dex::MethodInfo do
93
+ include_context 'with sample_classes.dex'
94
+ context 'about the first direct method of the last class' do
95
+ let(:first_direct_method) { last_class.direct_methods.first }
96
+ subject { first_direct_method }
97
+ its(:name) { should eq '<init>' }
98
+ end
99
+ context 'about the first virtual method of the last class' do
100
+ let(:first_virtual_method) { last_class.virtual_methods.first }
101
+ subject { first_virtual_method }
102
+ its(:name) { should eq 'processDoWhile' }
103
+ its(:code_item) { should be_instance_of Android::Dex::DexObject::CodeItem }
104
+ describe '#parameters' do
105
+ subject { first_virtual_method.parameters }
106
+ it { should have(1).item }
107
+ it { should include('int') }
108
+ end
109
+ end
110
+ context 'about the 12th virtual method(processTryCatch) of the last class' do
111
+ let(:a_virtual_method) { last_class.virtual_methods[12]}
112
+ subject { a_virtual_method }
113
+ its(:name) { should eq 'processTryCatch' }
114
+ describe '#code_item' do
115
+ subject { a_virtual_method.code_item }
116
+ it { should be_instance_of Android::Dex::DexObject::CodeItem }
117
+ its(:debug_info_item) { should be_instance_of Android::Dex::DexObject::DebugInfoItem }
118
+ end
119
+ end
120
+ end
121
+
@@ -0,0 +1,56 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Android::Dex do
4
+
5
+ describe ".uleb128" do
6
+ # @see http://en.wikipedia.org/wiki/LEB128
7
+ it "[0x00] should be 0" do
8
+ Android::Dex.uleb128("\x00").should == [0,1]
9
+ end
10
+ it "[0x01] should be 1" do
11
+ Android::Dex.uleb128("\x01").should == [1,1]
12
+ end
13
+ it "[0x7f] should be 127" do
14
+ Android::Dex.uleb128("\x7f").should == [127,1]
15
+ end
16
+ it "[0x80,0x7f] should be 16256" do
17
+ Android::Dex.uleb128("\x80\x7f").should == [16256,2]
18
+ end
19
+ it "[0xe5,0x8e,0x26] should be 624485" do
20
+ Android::Dex.uleb128("\xe5\x8e\x26").should == [624485,3]
21
+ end
22
+ end
23
+
24
+ describe ".uleb128p1" do
25
+ it "[0x00] should be -1" do
26
+ Android::Dex.uleb128p1("\x00").should == [-1,1]
27
+ end
28
+ it "[0x01] should be 0" do
29
+ Android::Dex.uleb128p1("\x01").should == [0,1]
30
+ end
31
+ it "[0x7f] should be 126" do
32
+ Android::Dex.uleb128p1("\x7f").should == [126,1]
33
+ end
34
+ it "[0x80,0x7f] should be 16255" do
35
+ Android::Dex.uleb128p1("\x80\x7f").should == [16255,2]
36
+ end
37
+ it "[0xe5,0x8e,0x26] should be 624485" do
38
+ Android::Dex.uleb128("\xe5\x8e\x26").should == [624485,3]
39
+ end
40
+ end
41
+ describe '.sleb128' do
42
+ it "[0x00] should be 0" do
43
+ Android::Dex.sleb128("\x00").should == [0,1]
44
+ end
45
+ it "[0x01] should be 1" do
46
+ Android::Dex.sleb128("\x01").should == [1,1]
47
+ end
48
+ it "[0x7f] should be -1" do
49
+ Android::Dex.sleb128("\x7f").should == [-1,1]
50
+ end
51
+ it "[0x80,0x7f] should be 127" do
52
+ Android::Dex.sleb128("\x80\x7f").should == [-128,2]
53
+ end
54
+ end
55
+ end
56
+
data/spec/dex_spec.rb ADDED
@@ -0,0 +1,59 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Android::Dex do
4
+ let(:dex_path){ File.expand_path(File.dirname(__FILE__) + '/data/sample_classes.dex') }
5
+ let(:dex_bin){ File.open(dex_path, 'rb') {|f| f.read } }
6
+ let(:dex){ Android::Dex.new(dex_bin) }
7
+
8
+
9
+ describe '#initialize' do
10
+ subject { dex }
11
+ context 'with valid dex data' do
12
+ it { should be_instance_of(Android::Dex) }
13
+ end
14
+ context 'with nil data' do
15
+ let(:dex_bin) { nil }
16
+ specify { expect{ subject }.to raise_error }
17
+ end
18
+ end
19
+
20
+ describe '#data' do
21
+ subject { dex.data }
22
+ it { should be_instance_of String }
23
+ specify { subject.encoding.should eq Encoding::ASCII_8BIT }
24
+ end
25
+
26
+ describe '#strings' do
27
+ let(:num_str) { dex.header[:string_ids_size] }
28
+ subject { dex.strings }
29
+ it { should be_instance_of Array }
30
+ it 'should have string_ids_size items' do
31
+ should have(num_str).items
32
+ end
33
+ it "should be the particular string(depends on sample_classes.dex)" do
34
+ subject[0].should eq "%d"
35
+ end
36
+ it "should be the particular string(depends on sample_classes.dex)" do
37
+ subject[1].should eq "<init>"
38
+ end
39
+ it "should be the particular string(depends on sample_classes.dex)" do
40
+ subject[2].should eq "BuildConfig.java"
41
+ end
42
+ end
43
+ describe '#inspect' do
44
+ subject { dex.inspect }
45
+ it { should match(/\A<Android::Dex.*>\Z/m) }
46
+ end
47
+
48
+ describe '#classes' do
49
+ subject { dex.classes }
50
+ let(:num_classes) { dex.header[:class_defs_size] }
51
+ it{ should be_instance_of Array }
52
+ it{ should have(num_classes).items }
53
+ describe 'first item' do
54
+ subject { dex.classes.first }
55
+ it{ should be_instance_of Android::Dex::ClassInfo }
56
+ end
57
+ end
58
+ end
59
+
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Android::Layout do
4
+ context 'with real apk sample file' do
5
+ let(:apk_path){ File.expand_path(File.dirname(__FILE__) + '/data/sample.apk') }
6
+ let(:apk){ Android::Apk.new(apk_path) }
7
+ let(:layouts) { apk.layouts }
8
+ subject { layouts }
9
+ it { should be_a Hash }
10
+ it { should have_key "res/layout/main.xml" }
11
+ it { should have(1).item }
12
+ context 'about first item' do
13
+ subject { layouts['res/layout/main.xml'] }
14
+ it { should be_a Android::Layout }
15
+ describe '#path' do
16
+ it { subject.path.should eq 'res/layout/main.xml' }
17
+ end
18
+ describe '#doc' do
19
+ it { subject.doc.should be_a REXML::Document }
20
+ end
21
+ describe '#to_xml' do
22
+ it { subject.to_xml.should be_a String }
23
+ end
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,221 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Android::Manifest do
4
+ describe Android::Manifest::Component do
5
+ describe "self.valid?" do
6
+ let(:elem) { REXML::Element.new('service') }
7
+ subject { Android::Manifest::Component.valid?(elem) }
8
+ context "with valid component element" do
9
+ it { should be_true }
10
+ end
11
+ context "with invalid component element" do
12
+ let(:elem) { REXML::Element.new('invalid-name') }
13
+ it { should be_false }
14
+ end
15
+ context "when some exception occurs in REXML::Element object" do
16
+ let(:elem) {
17
+ elem = stub(REXML::Element)
18
+ elem.stub(:name).and_raise(StandardError)
19
+ elem
20
+ }
21
+ it { should be_false }
22
+ end
23
+ end
24
+ describe '#metas' do
25
+ subject { Android::Manifest::Component.new(elem).metas }
26
+ context 'with valid component element has 2 meta elements' do
27
+ let(:elem) {
28
+ elem = REXML::Element.new('service')
29
+ elem << REXML::Element.new('meta-data')
30
+ elem << REXML::Element.new('meta-data')
31
+ elem
32
+ }
33
+ it { should have(2).item }
34
+ end
35
+ end
36
+ describe '#elem' do
37
+ subject { Android::Manifest::Component.new(elem).elem }
38
+ let(:elem) { REXML::Element.new('service') }
39
+ it { should eq elem }
40
+ end
41
+
42
+ describe Android::Manifest::Meta do
43
+ let(:elem) do
44
+ attrs = { 'name' => 'meta name', 'resource' => 'res', 'value' => 'val' }
45
+ elem = stub(REXML::Element, :attributes => attrs)
46
+ elem
47
+ end
48
+ subject { Android::Manifest::Meta.new(elem) }
49
+ its(:name) { should eq 'meta name' }
50
+ its(:resource) { should eq 'res' }
51
+ its(:value) { should eq 'val' }
52
+ end
53
+ end
54
+
55
+ describe Android::Manifest::IntentFilter do
56
+ describe '.parse' do
57
+ subject { Android::Manifest::IntentFilter.parse(elem) }
58
+ context 'assings "action" element' do
59
+ let(:elem) { REXML::Element.new('action') }
60
+ it { should be_instance_of Android::Manifest::IntentFilter::Action }
61
+ end
62
+ context 'assings "category" element' do
63
+ let(:elem) { REXML::Element.new('category') }
64
+ it { should be_instance_of Android::Manifest::IntentFilter::Category }
65
+ end
66
+ context 'assings "data" element' do
67
+ let(:elem) { REXML::Element.new('data') }
68
+ it { should be_instance_of Android::Manifest::IntentFilter::Data }
69
+ end
70
+ context 'assings unknown element' do
71
+ let(:elem) { REXML::Element.new('unknown') }
72
+ it { should be_nil }
73
+ end
74
+ end
75
+ end
76
+
77
+ context "with stub AXMLParser" do
78
+ let(:dummy_xml) {
79
+ xml = REXML::Document.new
80
+ xml << REXML::Element.new('manifest')
81
+ }
82
+ let(:manifest) { Android::Manifest.new('mock data') }
83
+
84
+ before do
85
+ parser = stub(Android::AXMLParser, :parse => dummy_xml)
86
+ Android::AXMLParser.stub(:new).and_return(parser)
87
+ end
88
+
89
+ describe "#use_parmissions" do
90
+ subject { manifest.use_permissions }
91
+ context "with valid 3 parmission elements" do
92
+ before do
93
+ 3.times do |i|
94
+ elem = REXML::Element.new("uses-permission")
95
+ elem.add_attribute 'name', "permission#{i}"
96
+ dummy_xml.root << elem
97
+ end
98
+ end
99
+ it { subject.should have(3).items }
100
+ it "should have permissions" do
101
+ subject.should include("permission0")
102
+ subject.should include("permission1")
103
+ subject.should include("permission2")
104
+ end
105
+ end
106
+ context "with no parmissions" do
107
+ it { should be_empty }
108
+ end
109
+ end
110
+
111
+ describe "#components" do
112
+ subject { manifest.components }
113
+ context "with valid parmission element" do
114
+ before do
115
+ app = REXML::Element.new('application')
116
+ activity = REXML::Element.new('activity')
117
+ app << activity
118
+ dummy_xml.root << app
119
+ end
120
+ it "should have components" do
121
+ subject.should have(1).items
122
+ end
123
+ it "should returns Component object" do
124
+ subject[0].should be_kind_of Android::Manifest::Component
125
+ end
126
+ end
127
+ context "with no components" do
128
+ it { should be_empty }
129
+ end
130
+ context 'with text element in intent-filter element. (issue #3)' do
131
+ before do
132
+ app = REXML::Element.new('application')
133
+ activity = REXML::Element.new('activity')
134
+ intent_filter = REXML::Element.new('intent-filter')
135
+ text = REXML::Text.new('sample')
136
+
137
+ intent_filter << text
138
+ activity << intent_filter
139
+ app << activity
140
+ dummy_xml.root << app
141
+ end
142
+ it "should have components" do
143
+ subject.should have(1).items
144
+ end
145
+ it { expect { subject }.to_not raise_error }
146
+ end
147
+ end
148
+ end
149
+
150
+ context "with real sample_AndroidManifest.xml data" do
151
+ let(:bin_xml_path){ File.expand_path(File.dirname(__FILE__) + '/data/sample_AndroidManifest.xml') }
152
+ let(:bin_xml){ File.open(bin_xml_path, 'rb') {|f| f.read } }
153
+ let(:manifest){ Android::Manifest.new(bin_xml) }
154
+
155
+ describe "#components" do
156
+ subject { manifest.components }
157
+ it { should be_kind_of Array }
158
+ it { subject[0].should be_kind_of Android::Manifest::Component }
159
+ end
160
+ describe "#package_name" do
161
+ subject { manifest.package_name }
162
+ it { should == "example.app.sample" }
163
+ end
164
+ describe "#version_code" do
165
+ subject { manifest.version_code}
166
+ it { should == 101 }
167
+ end
168
+ describe "#version_name" do
169
+ subject { manifest.version_name}
170
+ it { should == "1.0.1-malware2" }
171
+ end
172
+ describe "#min_sdk_ver" do
173
+ subject { manifest.min_sdk_ver}
174
+ it { should == 10 }
175
+ end
176
+ describe "#label" do
177
+ subject { manifest.label }
178
+ it { should == "@0x7f040001" }
179
+
180
+ context "with real apk file" do
181
+ let(:tmp_path){ File.expand_path(File.dirname(__FILE__) + '/data/sample.apk') }
182
+ let(:apk) { Android::Apk.new(tmp_path) }
183
+ let(:manifest){ apk.manifest }
184
+ subject { manifest.label }
185
+ it { should eq 'Sample' }
186
+ context 'when assign lang code' do
187
+ subject { manifest.label('ja') }
188
+ it { should eq 'Sample' }
189
+ end
190
+ end
191
+ end
192
+ describe "#doc" do
193
+ subject { manifest.doc }
194
+ it { should be_instance_of REXML::Document }
195
+ end
196
+ describe "#to_xml" do
197
+ let(:raw_xml){ str = <<EOS
198
+ <manifest xmlns:android='http://schemas.android.com/apk/res/android' android:versionCode='101' android:versionName='1.0.1-malware2' package='example.app.sample'>
199
+ <uses-sdk android:minSdkVersion='10'/>
200
+ <uses-permission android:name='android.permission.INTERNET'/>
201
+ <uses-permission android:name='android.permission.WRITE_EXTERNAL_STORAGE'/>
202
+ <application android:label='@0x7f040001' android:icon='@0x7f020000' android:debuggable='true'>
203
+ <activity android:label='@0x7f040001' android:name='example.app.sample.SampleActivity'>
204
+ <intent-filter>
205
+ <action android:name='android.intent.action.MAIN'/>
206
+ <category android:name='android.intent.category.LAUNCHER'/>
207
+ </intent-filter>
208
+ </activity>
209
+ </application>
210
+ </manifest>
211
+ EOS
212
+ str.strip
213
+ }
214
+
215
+ subject { manifest.to_xml }
216
+ it "should return correct xml string" do
217
+ subject.should == raw_xml
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ describe Android::Resource do
5
+ let(:res_data) { File.read(res_path) }
6
+ let(:resource) { Android::Resource.new(res_data) }
7
+
8
+ shared_examples_for 'a valid Android::Resource' do
9
+ subject { resource }
10
+ describe '#initialize' do
11
+ context 'assigns resources.arsc data' do
12
+ it { should be_instance_of Android::Resource }
13
+ end
14
+ end
15
+ its(:package_count) {should eq 1 }
16
+ describe '#strings' do
17
+ subject { resource.strings }
18
+ it { should have(7).items } # depends on sample_resources.arsc
19
+ it { should include 'Sample' }
20
+ it { should include 'Hello World, SampleActivity!' }
21
+ it { should include '日本語リソース' }
22
+ end
23
+ end
24
+ context 'with sample_resources.arsc data' do
25
+ let(:res_path) { File.expand_path(File.dirname(__FILE__) + '/data/sample_resources.arsc') }
26
+ it_behaves_like 'a valid Android::Resource'
27
+
28
+ end
29
+ context 'with sample_resources_utf16.arsc data' do
30
+ let(:res_path) { File.expand_path(File.dirname(__FILE__) + '/data/sample_resources_utf16.arsc') }
31
+ it_behaves_like 'a valid Android::Resource'
32
+ end
33
+
34
+ describe Android::Resource::ChunkHeader do
35
+ shared_examples_for 'a chunk header' do
36
+ its(:type) { should eq 20 }
37
+ its(:header_size) { should eq 244 }
38
+ its(:size) { should eq 1000 }
39
+ end
40
+ subject { Android::Resource::ChunkHeader.new(data, offset) }
41
+ context 'with no offset' do
42
+ let(:data) { "\x14\x00\xF4\x00\xE8\x03\x00\x00" } # [20, 244, 1000].pack('vvV')
43
+ let(:offset) { 0 }
44
+ it_behaves_like 'a chunk header'
45
+ end
46
+ context 'with 10byte offset' do
47
+ let(:data) { "\x00"*10 + "\x14\x00\xF4\x00\xE8\x03\x00\x00" } # [20, 244, 1000].pack('vvV')
48
+ let(:offset) { 10 }
49
+ it_behaves_like 'a chunk header'
50
+ end
51
+ end
52
+
53
+ describe Android::Resource::ResStringPool do
54
+ describe '.utf8_len' do
55
+ subject { Android::Resource::ResStringPool.utf8_len(data) }
56
+ context 'assigns 0x7F' do
57
+ let(:data) { "\x7f" }
58
+ it { should eq [0x7f, 1] }
59
+ end
60
+ context 'assigns x81x01' do
61
+ let(:data) { "\x81\x01" }
62
+ it { should eq [0x0101, 2] }
63
+ end
64
+ context 'assigns xffxff' do
65
+ let(:data) { "\xff\xff" }
66
+ it { should eq [0x7fff, 2] }
67
+ end
68
+ end
69
+ describe '#utf16_len' do
70
+ subject { Android::Resource::ResStringPool.utf16_len(data) }
71
+ context 'assigns x7fff' do
72
+ let(:data) { "\xff\x7f" }
73
+ it { should eq [0x7fff, 2] }
74
+ end
75
+ context 'assigns x8001,x0001' do
76
+ let(:data) { "\x01\x80\x01\x00" }
77
+ it { should eq [0x10001, 4] }
78
+ end
79
+ context 'assigns xffff,xffff' do
80
+ let(:data) { "\xff\xff\xff\xff" }
81
+ it 'should eq 0x7fff 0xffff' do
82
+ should eq [0x7fffffff, 4]
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'with str_resources.arsc data' do
88
+ let(:res_data) { File.read(File.expand_path(File.dirname(__FILE__) + '/data/str_resources.arsc')) }
89
+ subject { resource }
90
+ describe 'about drawable resource' do
91
+ it 'hoge' do
92
+ table = resource.packages.first[1]
93
+ p table
94
+ table.type_strings.each_with_index do |type, id|
95
+ puts "[0x#{(id+1).to_s(16)}] #{type}"
96
+ end
97
+ puts "readable id:" + table.res_readable_id('@0x7f020000')
98
+ end
99
+ end
100
+ end
101
+ context 'with str_resources.arsc data' do
102
+ let(:res_data) { File.read(File.expand_path(File.dirname(__FILE__) + '/data/str_resources.arsc')) }
103
+ subject { resource }
104
+ describe '#packages' do
105
+ subject {resource.packages}
106
+ it { should be_instance_of Hash}
107
+ it { subject.size.should eq 1 }
108
+ end
109
+ describe 'ResTablePackage' do
110
+ subject { resource.packages.first[1] }
111
+ it { subject.type(1).should eq 'attr' }
112
+ it { subject.type(4).should eq 'string' }
113
+ it { subject.name.should eq 'com.example.sample.ruby_apk' }
114
+ end
115
+ describe '#find' do
116
+ it '@0x7f040000 should return "sample.ruby_apk"' do
117
+ subject.find('@0x7f040000').should eq 'sample application'
118
+ end
119
+ it '@string/app_name should return "sample.ruby_apk"' do
120
+ subject.find('@string/app_name').should eq 'sample application'
121
+ end
122
+ it '@string/hello_world should return "Hello world!"' do
123
+ subject.find('@string/hello_world').should eq 'Hello world!'
124
+ end
125
+ it '@string/app_name should return "sample.ruby_apk"' do
126
+ subject.find('@string/app_name').should eq 'sample application'
127
+ end
128
+ it '@string/app_name with {:lang => "ja"} should return "サンプルアプリ"' do
129
+ subject.find('@string/app_name', :lang => 'ja').should eq 'サンプルアプリ'
130
+ end
131
+ it '@string/hello_world with {:lang => "ja"} should return nil' do
132
+ subject.find('@string/hello_world', :lang => 'ja').should be_nil
133
+ end
134
+ context 'assigns not exist string resource id' do
135
+ it { expect {subject.find('@string/not_exist') }.to raise_error Android::NotFoundError }
136
+ it { expect {subject.find('@0x7f040033') }.to raise_error Android::NotFoundError }
137
+ end
138
+ context 'assigns not string resource id' do
139
+ it { subject.find('@layout/activity_main').should be_nil }
140
+ end
141
+ context 'assigns invalid format id' do
142
+ it '"@xxyyxxyy" should raise ArgumentError' do
143
+ expect{ subject.find('@xxyyxxyy') }.to raise_error(ArgumentError)
144
+ end
145
+ it '"@0xff112233445566" should raise ArgumentError' do
146
+ expect{ subject.find('@0xff112233445566') }.to raise_error(ArgumentError)
147
+ end
148
+ end
149
+ end
150
+ describe '#res_readable_id' do
151
+ it { subject.res_readable_id('@0x7f040000').should eq '@string/app_name' }
152
+ context 'assigns invalid type' do
153
+ it { expect{subject.res_readable_id('@0x7f0f0000')}.to raise_error Android::NotFoundError }
154
+ end
155
+ context 'assigns invalid key' do
156
+ it { expect{subject.res_readable_id('@0x7f040033')}.to raise_error Android::NotFoundError }
157
+ end
158
+ end
159
+ describe '#res_hex_id' do
160
+ it { subject.res_hex_id('@string/app_name').should eq '@0x7f040000' }
161
+ context 'assigns invalid type' do
162
+ it { expect{subject.res_readable_id('@not_exist/xxxx')}.to raise_error Android::NotFoundError }
163
+ end
164
+ context 'assigns invalid key' do
165
+ it { expect{subject.res_readable_id('@string/not_exist')}.to raise_error Android::NotFoundError }
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "RubyApk" do
4
+ end
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter "/spec"
6
+ end
7
+ require 'rspec'
8
+ require 'ruby_android'
9
+
10
+
11
+ # Requires supporting files with custom matchers and macros, etc,
12
+ # in ./support/ and its subdirectories.
13
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
14
+
15
+ RSpec.configure do |config|
16
+
17
+ end