Skip to content

Commit 97c3bfd

Browse files
Added tests for MachOStructure dsl
Added tests for all field types and options. Also, fixed bug related to calculating MachoStructure#min_args.
1 parent 7f80b2d commit 97c3bfd

File tree

2 files changed

+115
-1
lines changed

2 files changed

+115
-1
lines changed

lib/macho/structure.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ module Fields
5252
# To add a new class append it here and add the init method to the def_class_reader method
5353
# @api private
5454
CLASS_LIST = %i[lcstr tool_entries two_level_hints_table].freeze
55+
56+
# A list of fields that don't require arguments
57+
# Used to calculate MachOStructure#min_args
58+
# @api private
59+
NO_ARGS_LIST = %i[two_level_hints_table].freeze
5560
end
5661

5762
# map of field names to indices
@@ -142,7 +147,7 @@ def field(name, type, **options)
142147
idx = if @field_idxs.key?(name)
143148
@field_idxs[name]
144149
else
145-
@min_args += 1 unless options.key?(:default)
150+
@min_args += 1 unless options.key?(:default) || Fields::NO_ARGS_LIST.include?(type)
146151
@field_idxs[name] = @field_idxs.size
147152
@size_list << nil
148153
@fmt_list << nil

test/test_structure_dsl.rb

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "helpers"
4+
5+
class MachOStructureTest < Minitest::Test
6+
# Test that every field type can be created and that
7+
# that information is reflected in the bytesize, min_args
8+
# and format.
9+
class AllFields < MachO::MachOStructure
10+
field :binary, :binary, :size => 16
11+
field :string, :string, :size => 32
12+
field :int32, :int32
13+
field :uint32, :uint32
14+
field :uint64, :uint64
15+
field :view, :view
16+
field :lcstr, :lcstr
17+
field :two_level_hints_table, :two_level_hints_table
18+
field :tool_entries, :tool_entries
19+
end
20+
21+
def test_all_field_types
22+
assert_includes AllFields.instance_methods, :binary
23+
assert_includes AllFields.instance_methods, :string
24+
assert_includes AllFields.instance_methods, :int32
25+
assert_includes AllFields.instance_methods, :uint32
26+
assert_includes AllFields.instance_methods, :uint64
27+
assert_includes AllFields.instance_methods, :view
28+
assert_includes AllFields.instance_methods, :lcstr
29+
assert_includes AllFields.instance_methods, :two_level_hints_table
30+
assert_includes AllFields.instance_methods, :tool_entries
31+
32+
assert_equal AllFields.bytesize, 72
33+
assert_equal AllFields.format, "a16Z32l=L=Q=L=L="
34+
assert_equal AllFields.min_args, 8
35+
end
36+
37+
# Test that fields already defined in the base class
38+
# are updated correctly when redefined in the
39+
# derived class.
40+
class BaseCmd < MachO::MachOStructure
41+
field :field1, :uint32
42+
field :field2, :uint32
43+
end
44+
45+
class DerivedCmd < BaseCmd
46+
field :field1, :uint64
47+
field :field2, :uint64
48+
end
49+
50+
def test_updating_fields
51+
assert_equal BaseCmd.bytesize, 8
52+
assert_equal BaseCmd.format, "L=L="
53+
assert_equal DerivedCmd.bytesize, 16
54+
assert_equal DerivedCmd.format, "Q=Q="
55+
end
56+
57+
# Tests that make sure that all of the options work
58+
# correctly (except for :size which is already tested above).
59+
class MaskCmd < MachO::MachOStructure
60+
field :mask_field, :uint32, :mask => 0xffff0000
61+
end
62+
63+
def test_mask_option
64+
mask_struct = MaskCmd.new(0xffffffff)
65+
assert_equal mask_struct.mask_field, 0x0000ffff
66+
end
67+
68+
class UnpackCmd < MachO::MachOStructure
69+
field :unpack_field, :binary, :size => 8, :unpack => "L>2"
70+
end
71+
72+
def test_unpack_option
73+
numbers = [42, 1337].freeze
74+
format_code = "L>2"
75+
packed_numbers = numbers.pack(format_code)
76+
77+
unpack_struct = UnpackCmd.new(packed_numbers)
78+
assert_equal unpack_struct.unpack_field, numbers
79+
end
80+
81+
class DefaultCmd < MachO::MachOStructure
82+
field :default_field, :uint64, :default => 0
83+
end
84+
85+
def test_default_option
86+
default_struct = DefaultCmd.new
87+
assert_equal default_struct.default_field, 0
88+
default_struct = DefaultCmd.new(4)
89+
assert_equal default_struct.default_field, 4
90+
end
91+
92+
class StringCmd < MachO::MachOStructure
93+
field :uint32, :uint32, :to_s => true
94+
end
95+
96+
def test_to_s_option
97+
string_cmd = StringCmd.new(10)
98+
assert_equal string_cmd.to_s, "10"
99+
end
100+
101+
class EndianCmd < MachO::MachOStructure
102+
field :uint32_big, :uint32, :endian => :big
103+
field :uint32_little, :uint32, :endian => :little
104+
end
105+
106+
def test_endian_option
107+
assert_equal EndianCmd.format, "L>L<"
108+
end
109+
end

0 commit comments

Comments
 (0)