dimensional 0.0.6 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/test/metric_test.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'test/unit'
2
2
  require 'dimensional/metric'
3
- require 'dimensional/unit'
4
3
  require 'rational'
5
4
 
6
5
  class MetricTest < Test::Unit::TestCase
@@ -12,88 +11,261 @@ class MetricTest < Test::Unit::TestCase
12
11
  Dimension.register('Force')
13
12
  System.register('British Admiralty', 'BA')
14
13
  System.register('United States Customary', 'US')
15
- cable = Unit.register('cable', System::BA, Dimension::L, {})
16
- fathom = Unit.register('fathom', System::BA, Dimension::L, {:reference_unit => cable, :reference_factor => Rational(1,10)})
17
- yard = Unit.register('yard', System::BA, Dimension::L, {:reference_unit => fathom, :reference_factor => Rational(1,6)})
18
- Unit.register('foot', System::BA, Dimension::L, {:reference_unit => yard, :reference_factor => Rational(1,3)})
19
- Unit.register('foot', System::US, Dimension::L, {:reference_unit => yard, :reference_factor => Rational(1,3)})
20
- Unit.register('pound', System::US, Dimension::M)
21
- Unit.register('pound', System::US, Dimension::F)
14
+ System.register('International System of Units', 'SI')
15
+ # Length Units - SI
16
+ @meter = Unit.register('meter', System::SI, Dimension::L, {:abbreviation => 'm'})
17
+ @kilometer = Unit.register('kilometer', System::SI, Dimension::L, {:reference_units => {@meter => 1}, :reference_factor => 1000, :abbreviation => 'km'})
18
+ @centimeter = Unit.register('centimeter', System::SI, Dimension::L, {:reference_units => {@meter => 1}, :reference_factor => Rational(1,100), :abbreviation => 'cm'})
19
+ # Length Units - US
20
+ @yard_us = Unit.register('yard', System::US, Dimension::L, {:reference_units => {@meter => 1}, :reference_factor => 0.9144, :abbreviation => 'yd'})
21
+ @foot_us = Unit.register('foot', System::US, Dimension::L, {:reference_units => {@yard_us => 1}, :reference_factor => Rational(1,3), :abbreviation => 'ft'})
22
+ @mile_us = Unit.register('mile', System::US, Dimension::L, {:reference_units => {@foot_us => 1}, :reference_factor => 5280, :abbreviation => 'mi'})
23
+ @inch_us = Unit.register('inch', System::US, Dimension::L, {:reference_units => {@foot_us => 1}, :reference_factor => Rational(1,12), :abbreviation => 'in'})
24
+ # Length Units - BA
25
+ @nautical_mile = Unit.register('mile', System::BA, Dimension::L, {:abbreviation => 'nm'})
26
+ @cable = Unit.register('cable', System::BA, Dimension::L, {:reference_units => {@nautical_mile => 1}, :reference_factor => Rational(1,10)})
27
+ @fathom = Unit.register('fathom', System::BA, Dimension::L, {:reference_units => {@cable => 1}, :reference_factor => Rational(1,10), :abbreviation => 'fm'})
28
+ @yard_ba = Unit.register('yard', System::BA, Dimension::L, {:reference_units => {@fathom => 1}, :reference_factor => Rational(1,6), :abbreviation => 'yd'})
29
+ @foot_ba = Unit.register('foot', System::BA, Dimension::L, {:reference_units => {@yard_ba => 1}, :reference_factor => Rational(1,3), :abbreviation => 'ft'})
30
+ @inch_ba = Unit.register('inch', System::BA, Dimension::L, {:reference_units => {@foot_ba => 1}, :reference_factor => Rational(1,12), :abbreviation => 'in'})
31
+ # Mass Units
32
+ @pound_mass = Unit.register('pound', System::US, Dimension::M, {:abbreviation => 'lb'})
33
+ # Force Units
34
+ @pound_force = Unit.register('pound', System::US, Dimension::F, {:abbreviation => 'ft'})
35
+ # Dimensionless Units
36
+ @each = Unit.register('each', System::US, nil, {:abbreviation => 'ea'})
37
+ @dozen = Unit.register('dozen', System::US, nil, {:reference_units => {@each => 0}, :reference_factor => 12, :abbreviation => 'dz'})
22
38
  end
23
39
 
24
40
  def teardown
25
41
  Dimension.reset!
26
42
  System.reset!
27
43
  Unit.reset!
28
- Metric.reset!
29
- end
30
-
31
- def test_create
32
- assert_instance_of Metric, m = Metric.new('draft', Dimension::L)
33
- assert_same Dimension::L, m.dimension
34
- assert_equal 'draft', m.name
35
- end
36
-
37
- def test_register
38
- assert_instance_of Metric, m = Metric.register('draft', Dimension::L)
39
- assert_same m, Metric[:draft]
40
- end
41
-
42
- def test_register_metric_with_parent
43
- parent = Metric.register('L', Dimension::L)
44
- child = Metric.register('depth', nil, parent)
45
- assert_same parent, child.parent
46
- end
47
-
48
- def test_register_dimensionless_metric
49
- assert_instance_of Metric, m = Metric.register('population', nil)
50
- assert_same m, Metric[:population]
51
- end
52
-
53
- def test_register_default_dimensionless_metric
54
- assert_instance_of Metric, m = Metric.register(nil, nil)
55
- assert_same m, Metric[nil]
56
- end
57
-
58
- def test_unit_preferences
59
- length = Metric.register('L', Dimension::L)
60
- draft = Metric.register('draft', Dimension::L, length)
61
- foot = Unit[Dimension::L, System::BA, 'foot']
62
- length.prefer(foot, {:detector => /(foot|ft)?s/})
63
- draft.prefer(foot, {:precision => Rational(1, 12)})
64
- assert_instance_of Hash, draft.preferences(foot)
65
- assert_equal Rational(1,12), draft.preferences(foot)[:precision]
66
- assert_equal /(foot|ft)?s/, draft.preferences(foot)[:detector]
67
- end
68
-
69
- def test_unit_membership
70
- length = Metric.register('L', Dimension::L)
71
- mass = Metric.register('M', Dimension::M)
72
- depth = Metric.register('depth', Dimension::L, length)
73
- foot = Unit[:L, :BA, 'foot']
74
- fathom = Unit[:L, :BA, 'fathom']
75
- pound = Unit[:M, :US, 'pound']
76
- length.prefer(foot)
77
- length.prefer(fathom)
78
- depth.prefer(fathom)
79
- mass.prefer(pound)
80
- assert_same fathom, depth.units.first # Units should be ordered with preferred units first...
81
- assert_same foot, depth.units.last # ...followed by remaining units from parent.
82
- assert_equal 2, depth.units.size # Units should be de-duped with parent
83
- end
84
-
85
- def test_enumerate
86
- length = Metric.register('L', Dimension::L)
87
- assert length.to_enum.kind_of?(Enumerable::Enumerator)
88
- assert_respond_to length, :map
89
- end
90
-
91
- def test_preferences_query_does_not_modify_preferences
92
- draft = Metric.register('draft', Dimension::L)
93
- foot = Unit[:L, :BA, 'foot']
94
- p0 = draft.preference(foot)
95
- draft.preferences(foot)
96
- p1 = draft.preference(foot)
97
- assert_equal p0, p1
44
+ end
45
+
46
+ def test_associated_units
47
+ beam = Class.new(Metric)
48
+ beam.dimension = Dimension::L
49
+ assert beam.units.include?(@cable)
50
+ assert !beam.units.include?(@pound_force)
51
+ end
52
+
53
+ def test_register_conflicting_unit
54
+ displacement = Class.new(Metric)
55
+ displacement.dimension = Dimension::M
56
+ assert_raises RuntimeError do
57
+ displacement.configure(@pound_force)
58
+ end
59
+ end
60
+
61
+ def test_create_new_measure
62
+ depth = Class.new(Metric)
63
+ depth.dimension = Dimension::L
64
+ assert m = depth.new(20, @fathom)
65
+ assert_equal 20, m
66
+ assert_equal @fathom, m.unit
67
+ end
68
+
69
+ def test_create_new_metric_with_default_unit
70
+ frontage = Class.new(Metric)
71
+ frontage.dimension = Dimension::L
72
+ frontage.default = Unit[:L, :US, :yard]
73
+ assert m = frontage.new(200)
74
+ assert_equal 200, m
75
+ assert_equal @yard_us, m.unit
76
+ end
77
+
78
+ def test_find_unit
79
+ depth = Class.new(Metric)
80
+ depth.dimension = Dimension::L
81
+ assert_same @foot_ba, depth.find_unit('foot', :BA)
82
+ assert_same @foot_us, depth.find_unit('foot', :US)
83
+ end
84
+
85
+ def test_parse
86
+ depth = Class.new(Metric)
87
+ depth.dimension = Dimension::L
88
+ assert m = depth.parse("15ft", :BA)
89
+ assert_same @foot_ba, m.unit
90
+ assert_equal 15, m
91
+ end
92
+
93
+ def test_parse_with_whitespace
94
+ depth = Class.new(Metric)
95
+ depth.dimension = Dimension::L
96
+ m = depth.parse("15 ft", :BA)
97
+ assert_same @foot_ba, m.unit
98
+ assert_equal 15, m
99
+ end
100
+
101
+ def test_parse_compound
102
+ depth = Class.new(Metric)
103
+ depth.dimension = Dimension::L
104
+ d = depth.parse("15ft11in", :US)
105
+ assert_in_delta(15 + Rational(11, 12), d, 0.000001)
106
+ assert_same @foot_us, d.unit
107
+ end
108
+
109
+ def test_parse_compound_with_whitespace
110
+ depth = Class.new(Metric)
111
+ depth.dimension = Dimension::L
112
+ d = depth.parse("1 ft 11 in", :US)
113
+ assert_same d.unit, @foot_us
114
+ assert_in_delta(1 + Rational(11, 12).to_f, d, 0.000001)
115
+ assert_same @foot_us, d.unit
116
+ end
117
+
118
+ def test_raise_on_parse_of_mixed_compound
119
+ depth = Class.new(Metric)
120
+ depth.dimension = Dimension::L
121
+ assert_raises ArgumentError do
122
+ depth.parse("1 foot 11cm", :L)
123
+ end
124
+ end
125
+
126
+ def test_parse_with_default_unit
127
+ depth = Class.new(Metric)
128
+ depth.dimension = Dimension::L
129
+ depth.default = @meter
130
+ assert_instance_of depth, m = depth.parse("10", :US)
131
+ assert_equal @meter, m.unit
132
+ end
133
+
134
+ def test_parse_dimensionless_units
135
+ count = Class.new(Metric)
136
+ assert m = count.parse('2 dozen')
137
+ assert_instance_of count, m
138
+ assert_equal 2, m
139
+ assert_equal @dozen, m.unit
140
+ assert_equal 12, m.unit.factor
141
+ assert_nil m.unit.dimension
142
+ end
143
+
144
+ def test_to_f
145
+ depth = Class.new(Metric)
146
+ depth.dimension = Dimension::L
147
+ d = depth.parse("1.85m", :SI)
148
+ assert_instance_of Float, d.to_f
149
+ end
150
+
151
+ def test_to_i
152
+ count = Class.new(Metric)
153
+ d = count.parse("1 each")
154
+ assert_instance_of Fixnum, d.to_i
155
+ end
156
+
157
+ def test_convert
158
+ depth = Class.new(Metric)
159
+ depth.dimension = Dimension::L
160
+ new = depth.new(1, @cable).convert(@fathom)
161
+ assert_in_delta(10, new, 0.000001)
162
+ assert_same @fathom, new.unit
163
+ end
164
+
165
+ def test_identity_conversion
166
+ depth = Class.new(Metric)
167
+ depth.dimension = Dimension::L
168
+ old_value = depth.new(12, @cable)
169
+ new_value = old_value.convert(@cable)
170
+ assert_equal old_value, new_value
171
+ end
172
+
173
+ # These system-conversion tests rely on very specific constants in the heuristics of #change_system
174
+ def test_change_system_yd
175
+ range = Class.new(Metric)
176
+ range.dimension = Dimension::L
177
+ m0 = range.new(1, @yard_us)
178
+ assert m1 = m0.change_system(:SI)
179
+ assert_same @meter, m1.unit
180
+ end
181
+
182
+ def test_change_system_with_oom_dominance
183
+ width = Class.new(Metric)
184
+ width.dimension = Dimension::L
185
+ m0 = width.new(1, @inch_us)
186
+ assert m1 = m0.change_system(:SI)
187
+ assert_same @centimeter, m1.unit
188
+ end
189
+
190
+ def test_change_system_ft
191
+ range = Class.new(Metric)
192
+ range.dimension = Dimension::L
193
+ m0 = range.new(1, @foot_us)
194
+ assert m1 = m0.change_system(:SI)
195
+ assert_same @meter, m1.unit
196
+ end
197
+
198
+ def test_change_system_mile
199
+ range = Class.new(Metric)
200
+ range.dimension = Dimension::L
201
+ m0 = range.new(1, @mile_us)
202
+ assert m1 = m0.change_system(:SI)
203
+ assert_same @kilometer, m1.unit
204
+ end
205
+
206
+ # These preferred tests rely on very specific constants in the heuristics of #prefer
207
+ def test_preferred_unit_with_only_oom
208
+ range = Class.new(Metric)
209
+ range.instance_eval do
210
+ self.dimension = Dimension::L
211
+ end
212
+ m0 = range.new(100000, @meter)
213
+ assert m1 = m0.preferred
214
+ assert_same @kilometer, m1.unit
215
+ end
216
+
217
+ # These preferred tests rely on very specific constants in the heuristics of #prefer
218
+ def test_preferred_unit_with_oom_and_preference
219
+ range = Class.new(Metric)
220
+ range.instance_eval do
221
+ self.dimension = Dimension::L
222
+ configure Unit[:L, :SI, :meter], {:preference => 3.01}
223
+ end
224
+ m0 = range.new(100000, Unit[:L, :SI, :meter])
225
+ assert m1 = m0.preferred
226
+ assert_same @meter, m1.unit
227
+ end
228
+
229
+ def test_convert_to_base
230
+ range = Class.new(Metric)
231
+ range.dimension = Dimension::L
232
+ range.base = @nautical_mile
233
+ b = range.new(1, @fathom).base
234
+ assert_in_delta(1e-2, b, 0.000001)
235
+ assert_same @nautical_mile, b.unit
236
+ end
237
+
238
+ def test_stringify_with_abbreviation
239
+ range = Class.new(Metric)
240
+ range.dimension = Dimension::L
241
+ assert_equal "1.85nm", range.parse('1.85 miles', :BA).to_s
242
+ end
243
+
244
+ def test_parse_gibberish_as_nil
245
+ beam = Class.new(Metric)
246
+ beam.dimension = Dimension::L
247
+ assert_nil beam.parse("gibberish", :L)
248
+ end
249
+
250
+ def test_format_output
251
+ depth = Class.new(Metric)
252
+ depth.dimension = Dimension::L
253
+ m = depth.parse("15ft3in", :BA)
254
+ assert_equal "15.25 (ft)", m.strfmeasure("%4.2f (%U)")
255
+ end
256
+
257
+ def test_format_output_with_multiple_substitutions
258
+ depth = Class.new(Metric)
259
+ depth.dimension = Dimension::L
260
+ m = depth.parse("15ft4in", :BA)
261
+ assert_equal "15.33 (ft)\t%\t<15.3333333ft>", m.strfmeasure("%4.2f (%U)\t%%\t<%10.7f%U>")
262
+ end
263
+
264
+ def test_precision_recognition
265
+ distance = Class.new(Metric)
266
+ distance.dimension = Dimension::L
267
+ distance.configure(@nautical_mile, :precision => -2)
268
+ assert_equal "1.8600nm", distance.parse('1.8565454 nm', :BA).strfmeasure("%.4f%U")
269
+ assert_equal "1.86", distance.parse('1.8565454 nm', :BA).strfmeasure("%s")
98
270
  end
99
271
  end
data/test/unit_test.rb CHANGED
@@ -11,6 +11,7 @@ class UnitTest < Test::Unit::TestCase
11
11
  Dimension.register('Length')
12
12
  Dimension.register('Mass')
13
13
  Dimension.register('Force')
14
+ Dimension.register('Area', 'A', {Dimension::L => 4})
14
15
  end
15
16
 
16
17
  def teardown
@@ -24,72 +25,135 @@ class UnitTest < Test::Unit::TestCase
24
25
  assert_same System::BA, u.system
25
26
  assert_same Dimension::L, u.dimension
26
27
  assert u.base?
27
- assert_same u, u.base
28
+ assert_equal({u => 1}, u.base)
28
29
  assert_same 1, u.factor
29
30
  end
30
-
31
+
32
+ def test_enumerability
33
+ u = Unit.register('each', System::BA, nil, {})
34
+ assert Unit.to_a.include?(u)
35
+ end
36
+
31
37
  def test_create_new_dimensionless_unit
32
38
  assert_instance_of Unit, u = Unit.new('each', System::BA, nil, {})
33
39
  assert_nil u.dimension
34
40
  end
35
-
41
+
36
42
  def test_create_new_derived_unit
37
43
  cable = Unit.new('cable', System::BA, Dimension::L, {})
38
- assert_instance_of Unit, u = Unit.new('fathom', System['BA'], Dimension['L'], :reference_factor => 1E-1, :reference_unit => cable)
44
+ assert_instance_of Unit, u = Unit.new('fathom', System['BA'], Dimension['L'], :reference_factor => 1E-1, :reference_units => {cable => 1})
39
45
  assert !u.base?
40
- assert_same cable, u.base
46
+ t = u.base
47
+ assert_equal({cable => 1}, t)
41
48
  assert_equal 1E-1, u.factor
42
49
  end
43
50
 
44
51
  def test_create_new_combined_unit
45
52
  meter = Unit.new('meter', System::SI, Dimension::L, {})
46
- assert_instance_of Unit, u = Unit.new('square meter', System::SI, Dimension::L, :reference_factor => 1, :reference_unit => [meter, meter])
53
+ assert_instance_of Unit, u = Unit.new('square meter', System::SI, Dimension::L, :reference_factor => 1, :reference_units => {meter => 2})
47
54
  assert !u.base?
48
- assert_equal [meter, meter], u.base
55
+ assert_equal({meter => 2}, u.base)
49
56
  assert_equal 1, u.factor
50
57
  end
51
58
 
59
+ def test_create_new_combined_with_derived_unit_and_big_exponents
60
+ meter = Unit.new('meter', System::SI, Dimension::L, {})
61
+ assert_instance_of Unit, yard = Unit.new('yard', System::US, Dimension::L, :reference_factor => 0.9144, :reference_units => {meter => 1})
62
+ assert_instance_of Unit, yard2 = Unit.new('square yard', System::SI, Dimension::A, :reference_factor => 1, :reference_units => {yard => 2})
63
+ assert !yard2.base?
64
+ assert_equal({meter => 2}, yard2.base)
65
+ assert_equal 0.83612736, yard2.factor
66
+ end
67
+
52
68
  def test_regsiter_new_unit
53
69
  assert_instance_of Unit, u = Unit.register('fathom', System::BA, Dimension::L, {:abbreviation => 'fm'})
54
70
  assert_same u, Unit[Dimension::L, System::BA, 'fathom']
55
71
  assert_same u, Unit[Dimension::L, System::BA, 'fm']
56
72
  end
57
-
73
+
58
74
  def test_regsiter_new_dimensionless_unit
59
75
  assert_instance_of Unit, u = Unit.register('each', System::BA, nil, {:abbreviation => 'ea'})
60
76
  assert_same u, Unit[nil, System::BA, 'each']
61
77
  assert_same u, Unit[nil, System::BA, 'ea']
62
78
  end
63
-
79
+
64
80
  def test_lookup_unit_with_symbols
65
81
  u = Unit.register('fathom', System::BA, Dimension::L, {:abbreviation => 'fm'})
66
- assert_nil Unit[:L, :SI, 'fathom']
67
- assert_nil Unit[:M, :BA, 'fathom']
68
- assert_nil Unit[:L, :BA, 'somethingelse']
69
82
  assert_same u, Unit[:L, :BA, 'fathom']
70
83
  end
71
-
84
+
85
+ def test_lookup_failure
86
+ assert_raises ArgumentError do
87
+ Unit[:L, :SI, 'fathom']
88
+ end
89
+ assert_raises ArgumentError do
90
+ Unit[:M, :BA, 'fathom']
91
+ end
92
+ assert_raises ArgumentError do
93
+ Unit[:L, :BA, 'somethingelse']
94
+ end
95
+ end
96
+
72
97
  def test_convert
73
98
  cable = Unit.new('cable', System::BA, Dimension::L, {})
74
- fathom = Unit.new('fathom', System::BA, Dimension::L, :reference_factor => 1E-1, :reference_unit => cable)
99
+ fathom = Unit.new('fathom', System::BA, Dimension::L, :reference_factor => 1E-1, :reference_unit => cable)
75
100
  assert_equal 10, cable.convert(fathom)
76
101
  assert_equal 1E-1, fathom.convert(cable)
77
102
  assert_equal 1, fathom.convert(fathom)
78
103
  end
79
-
104
+
80
105
  def test_identify_commensurable_units
81
- u0 = Unit.new('mile', System::BA, Dimension::L, :detector => /\A(nm|nmi)\Z/, :abbreviation => 'nm')
82
- u1 = Unit.new('cable', System::BA, Dimension::L, :detector => /\A(cables?|cbls?)\Z/, :reference_factor => 1E-1, :reference_unit => u0)
106
+ u0 = Unit.new('mile', System::BA, Dimension::L, :abbreviation => 'nm')
107
+ u1 = Unit.new('cable', System::BA, Dimension::L, :reference_factor => 1E-1, :reference_unit => u0)
83
108
  u2 = Unit.new('ton', System::BA, Dimension::M, :abbreviation => 't')
84
109
  assert u0.commensurable?(u1)
85
110
  assert !u0.commensurable?(u2)
86
111
  end
87
112
 
88
113
  def test_identify_commensurable_composed_units
89
- u0 = Unit.new('mile', System::BA, Dimension::L, :detector => /\A(nm|nmi)\Z/, :abbreviation => 'nm')
90
- u1 = Unit.new('cable', System::BA, Dimension::L, :detector => /\A(cables?|cbls?)\Z/, :reference_factor => 1E-1, :reference_unit => u0)
114
+ u0 = Unit.new('mile', System::BA, Dimension::L, :abbreviation => 'nm')
115
+ u1 = Unit.new('cable', System::BA, Dimension::L, :reference_factor => 1E-1, :reference_unit => u0)
91
116
  u2 = Unit.new('ton', System::BA, Dimension::M, :abbreviation => 't')
92
117
  assert u0.commensurable?(u1)
93
118
  assert !u0.commensurable?(u2)
94
119
  end
120
+
121
+ def test_identity
122
+ u0 = Unit.new('mile', System::BA, Dimension::L)
123
+ u1 = Unit.new('mile', System::BA, Dimension::L)
124
+ u2 = Unit.new('statute mile', System::BA, Dimension::L)
125
+ # u0 and u1 are effectively identical and should collide in hashes
126
+ assert_same u0.hash, u1.hash
127
+ assert u0.eql?(u1)
128
+ # u0 and u2 are distinct and should not collide in hashes
129
+ assert_not_same u0.hash, u2.hash
130
+ assert !u0.eql?(u2)
131
+ end
132
+
133
+ def test_equality
134
+ u0 = Unit.new('mile', System::BA, Dimension::L)
135
+ u1 = Unit.new('sea mile', System::BA, Dimension::L, :reference_factor => 1, :reference_units => {u0 => 1})
136
+ u2 = Unit.new('mile', System::BA, Dimension::L, :reference_factor => 0.93, :reference_units => {u0 => 1}) # modern approximation
137
+ # u0 and u1 have the same value but different identities
138
+ assert_equal u0, u1
139
+ # u0 and u2 have the same identity but different values
140
+ assert_not_equal u0, u2
141
+ end
142
+
143
+ def test_default_detector
144
+ u0 = Unit.new('mile', System::BA, Dimension::L, :abbreviation => 'nm')
145
+ assert_match u0.detector, 'mile'
146
+ assert_match u0.detector, 'nm'
147
+ end
148
+
149
+ def test_default_format
150
+ u0 = Unit.new('mile', System::BA, Dimension::L, :abbreviation => 'nm')
151
+ assert_match /%.*s/, u0.format
152
+ assert_match /%.*U/i, u0.format
153
+ end
154
+
155
+ def test_default_preference
156
+ u0 = Unit.new('mile', System::BA, Dimension::L, :abbreviation => 'nm')
157
+ assert_equal 0, u0.preference
158
+ end
95
159
  end