ruby_android_apk 0.7.7.1
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.
- checksums.yaml +7 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +45 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +73 -0
- data/LICENSE.txt +22 -0
- data/README.md +157 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/lib/android/apk.rb +207 -0
- data/lib/android/axml_parser.rb +174 -0
- data/lib/android/dex.rb +92 -0
- data/lib/android/dex/access_flag.rb +74 -0
- data/lib/android/dex/dex_object.rb +475 -0
- data/lib/android/dex/info.rb +151 -0
- data/lib/android/dex/utils.rb +45 -0
- data/lib/android/layout.rb +43 -0
- data/lib/android/manifest.rb +249 -0
- data/lib/android/resource.rb +531 -0
- data/lib/android/utils.rb +55 -0
- data/lib/ruby_apk.rb +7 -0
- data/spec/apk_spec.rb +332 -0
- data/spec/axml_parser_spec.rb +67 -0
- data/spec/data/sample.apk +0 -0
- data/spec/data/sample_AndroidManifest.xml +0 -0
- data/spec/data/sample_classes.dex +0 -0
- data/spec/data/sample_resources.arsc +0 -0
- data/spec/data/sample_resources_utf16.arsc +0 -0
- data/spec/data/str_resources.arsc +0 -0
- data/spec/dex/access_flag_spec.rb +42 -0
- data/spec/dex/dex_object_spec.rb +118 -0
- data/spec/dex/info_spec.rb +121 -0
- data/spec/dex/utils_spec.rb +56 -0
- data/spec/dex_spec.rb +59 -0
- data/spec/layout_spec.rb +41 -0
- data/spec/manifest_spec.rb +228 -0
- data/spec/resource_spec.rb +170 -0
- data/spec/ruby_apk_spec.rb +4 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/utils_spec.rb +95 -0
- metadata +185 -0
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Android::Dex::AccessFlag do
|
4
|
+
let(:flag) { 0x01 }
|
5
|
+
subject { Android::Dex::AccessFlag.new(flag) }
|
6
|
+
its(:flag) { should eq flag }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Android::Dex::ClassAccessFlag do
|
10
|
+
let(:accessor) { Android::Dex::ClassAccessFlag.new(flags) }
|
11
|
+
|
12
|
+
subject { accessor.to_s }
|
13
|
+
context 'flags are 0x19(public static final)' do
|
14
|
+
let(:flags) { 0x19 }
|
15
|
+
it { should eq 'public static final' }
|
16
|
+
end
|
17
|
+
context 'flags are 0xc0(volatile transient)' do
|
18
|
+
let(:flags) { 0xc0 }
|
19
|
+
it { should eq 'volatile transient' }
|
20
|
+
end
|
21
|
+
context 'flags are 0x22(private synchronized)' do
|
22
|
+
let(:flags) { 0x22 }
|
23
|
+
it { should eq 'private synchronized' }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe Android::Dex::MethodAccessFlag do
|
28
|
+
let(:accessor) { Android::Dex::MethodAccessFlag.new(flags) }
|
29
|
+
subject { accessor.to_s }
|
30
|
+
context 'flags are 0x19(public static final)' do
|
31
|
+
let(:flags) { 0x19 }
|
32
|
+
it { should eq 'public static final' }
|
33
|
+
end
|
34
|
+
context 'flags are 0xc0(bridge varargs)' do
|
35
|
+
let(:flags) { 0xc0 }
|
36
|
+
it { should eq 'bridge varargs' }
|
37
|
+
end
|
38
|
+
context 'flags are 0x22(private synchronized)' do
|
39
|
+
let(:flags) { 0x22 }
|
40
|
+
it { should eq 'private synchronized' }
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Android::Dex do
|
4
|
+
describe Android::Dex::DexObject::Header do
|
5
|
+
let(:header_sample) {
|
6
|
+
sample =
|
7
|
+
"\x64\x65\x78\x0A\x30\x33\x35\x00\x3F\x14\x98\x2C\x25\x77\x9B\x8D" +
|
8
|
+
"\x7C\xF0\x0B\xFA\x4D\x7B\x03\xAD\x4C\x15\xBC\x31\x4F\xD3\x4B\x71" +
|
9
|
+
"\x58\x18\x00\x00\x70\x00\x00\x00\x78\x56\x34\x12\x00\x00\x00\x00" +
|
10
|
+
"\x00\x00\x00\x00\x88\x17\x00\x00\x7A\x00\x00\x00\x70\x00\x00\x00" +
|
11
|
+
"\x23\x00\x00\x00\x58\x02\x00\x00\x0E\x00\x00\x00\xE4\x02\x00\x00" +
|
12
|
+
"\x10\x00\x00\x00\x8C\x03\x00\x00\x2C\x00\x00\x00\x0C\x04\x00\x00" +
|
13
|
+
"\x0A\x00\x00\x00\x6C\x05\x00\x00\xAC\x11\x00\x00\xAC\x06\x00\x00"
|
14
|
+
sample.force_encoding(Encoding::ASCII_8BIT)
|
15
|
+
}
|
16
|
+
let(:header) { Android::Dex::DexObject::Header.new(header_sample) }
|
17
|
+
describe "#symbols" do
|
18
|
+
subject { header.symbols }
|
19
|
+
it { should be_kind_of(Array) }
|
20
|
+
it { should have(23).items }
|
21
|
+
it { should include(:magic) }
|
22
|
+
it { should include(:checksum) }
|
23
|
+
it { should include(:signature) }
|
24
|
+
it { should include(:file_size) }
|
25
|
+
it { should include(:header_size) }
|
26
|
+
it { should include(:endian_tag) }
|
27
|
+
it { should include(:link_size) }
|
28
|
+
it { should include(:link_off) }
|
29
|
+
it { should include(:map_off) }
|
30
|
+
it { should include(:string_ids_size) }
|
31
|
+
it { should include(:string_ids_off) }
|
32
|
+
it { should include(:type_ids_size) }
|
33
|
+
it { should include(:type_ids_off) }
|
34
|
+
it { should include(:proto_ids_size) }
|
35
|
+
it { should include(:proto_ids_off) }
|
36
|
+
it { should include(:field_ids_size) }
|
37
|
+
it { should include(:field_ids_off) }
|
38
|
+
it { should include(:method_ids_size) }
|
39
|
+
it { should include(:method_ids_off) }
|
40
|
+
it { should include(:class_defs_size) }
|
41
|
+
it { should include(:class_defs_off) }
|
42
|
+
it { should include(:data_size) }
|
43
|
+
it { should include(:data_off) }
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#[]" do
|
47
|
+
subject { header }
|
48
|
+
it ':magic should be "dex\n035\0"' do
|
49
|
+
subject[:magic].should == "dex\n035\0"
|
50
|
+
end
|
51
|
+
it ":checksum should be 748164159(this value depends on sample_classes.dex)" do
|
52
|
+
subject[:checksum].should == 748164159
|
53
|
+
end
|
54
|
+
it ":signature should be 20byte string" do
|
55
|
+
subject[:signature].should be_kind_of String
|
56
|
+
subject[:signature].length == 20
|
57
|
+
end
|
58
|
+
it ":file_size should be classes.dex file size" do
|
59
|
+
subject[:file_size].should == 6232
|
60
|
+
end
|
61
|
+
it ":header_size should be 0x70" do
|
62
|
+
subject[:header_size].should == 0x70
|
63
|
+
end
|
64
|
+
it "should have integer params" do
|
65
|
+
subject[:header_size].should == 0x70
|
66
|
+
end
|
67
|
+
context "with int symbols" do
|
68
|
+
before do
|
69
|
+
@params = [
|
70
|
+
:link_size,
|
71
|
+
:link_off,
|
72
|
+
:map_off,
|
73
|
+
:string_ids_size,
|
74
|
+
:string_ids_off,
|
75
|
+
:type_ids_size,
|
76
|
+
:type_ids_off,
|
77
|
+
:proto_ids_size,
|
78
|
+
:proto_ids_off,
|
79
|
+
:field_ids_size,
|
80
|
+
:field_ids_off,
|
81
|
+
:method_ids_size,
|
82
|
+
:method_ids_off,
|
83
|
+
:class_defs_size,
|
84
|
+
:class_defs_off,
|
85
|
+
:data_size,
|
86
|
+
:data_off,
|
87
|
+
]
|
88
|
+
end
|
89
|
+
it "should have integer value" do
|
90
|
+
@params.each do |sym|
|
91
|
+
subject[sym].should be_kind_of Integer
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
context "with unkown params" do
|
96
|
+
it { subject[:unkown].should be_nil }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#inspect" do
|
101
|
+
subject { header.inspect }
|
102
|
+
it { should match(/\A<Android::Dex::DexObject::Header.*>\Z/m) }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe Android::Dex::DexObject::StringDataItem do
|
107
|
+
let(:string_data_item_sample) {
|
108
|
+
sample = "\x0b\x61\x62\x63\xc0\x80\xc8\x85\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xed\xa0\x81\xed\xb0\x80\xc0\x80"
|
109
|
+
sample.force_encoding(Encoding::ASCII_8BIT)
|
110
|
+
}
|
111
|
+
let(:string_data_item) { Android::Dex::DexObject::StringDataItem.new(string_data_item_sample, 0) }
|
112
|
+
describe "#to_s" do
|
113
|
+
subject { string_data_item.to_s }
|
114
|
+
it { should == "abc\u{205}\u{3042}\u{3044}\u{3046}\u{10400}" }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -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
|
+
|
data/spec/layout_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
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
|
+
|
27
|
+
context 'with real new apk sample file' do
|
28
|
+
let(:apk_path){ File.expand_path(File.dirname(__FILE__) + '/data/sample_new.apk') }
|
29
|
+
let(:apk){ Android::Apk.new(apk_path) }
|
30
|
+
let(:layouts) { apk.layouts }
|
31
|
+
subject { layouts }
|
32
|
+
it { should be_a Hash }
|
33
|
+
it { should have_key "res/layout/activity_main.xml" }
|
34
|
+
it { should have(1).item }
|
35
|
+
context 'about first item' do
|
36
|
+
subject { layouts['res/layout/activity_main.xml'] }
|
37
|
+
it { should be_a Android::Layout }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|