ruby_android 0.0.2 → 0.7.2

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 (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