blob: d0521ea476afa835fed4bbaac0250b321a1c6561 [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package unit
2
3import (
4 "math"
5 "testing"
6)
7
8func floatEq(first, second float64) bool {
9 const maxPrecisionError = 0.00000001
10 return math.Abs(float64(first)-float64(second)) <= maxPrecisionError
11}
12
13func TestDistanceAccessorsMetric(t *testing.T) {
14 d := 1 * Kilometer
15 if !floatEq(d.Kilometers(), 1) {
16 t.Errorf("kilometers error: have %v, want %v", d.Kilometers(), 1)
17 }
18 if !floatEq(d.Meters(), 1000) {
19 t.Errorf("meters error: have %v, want %v", d.Meters(), 1000)
20 }
21 if !floatEq(d.Centimeters(), 100000) {
22 t.Errorf("centimeters error: have %v, want %v", d.Centimeters(), 100000)
23 }
24 if !floatEq(d.Millimeters(), 1000000) {
25 t.Errorf("millimeters error: have %v, want %v", d.Millimeters(), 1000000)
26 }
27}
28
29func TestDistanceAccessorsImperial(t *testing.T) {
30 d := 1 * Mile
31 if !floatEq(d.Miles(), 1) {
32 t.Errorf("miles error: have %v, want %v", d.Miles(), 1)
33 }
34 if !floatEq(d.Yards(), 1760) {
35 t.Errorf("yards error: have %v, want %v", d.Yards(), float64(5280)/3)
36 }
37 if !floatEq(d.Feet(), 5280) {
38 t.Errorf("feet error: have %v, want %v", d.Feet(), 5280)
39 }
40 if !floatEq(d.Inches(), 63360) {
41 t.Errorf("inches error: have %v, want %v", d.Inches(), 63360)
42 }
43}
44
45func TestDistanceUnitConversions(t *testing.T) {
46 d := 100.9 * Meter
47 if !floatEq(d.Miles(), d.Feet()/5280) {
48 t.Error("wrong miles <-> feet conversion")
49 }
50 if !floatEq(d.Kilometers(), d.Meters()/1000) {
51 t.Error("wrong kilometers <-> meters conversion")
52 }
53 if !floatEq(d.Meters(), d.Centimeters()/100) {
54 t.Error("wrong meters <-> centimeters conversion")
55 }
56 if !floatEq(d.Feet(), d.Meters()/0.3048) {
57 t.Error("wrong feet <-> meters conversion")
58 }
59 if !floatEq(d.Feet(), d.Inches()/12) {
60 t.Error("wrong feet -> inches conversion.")
61 }
62 if !floatEq(d.Centimeters(), d.Millimeters()/10) {
63 t.Error("wrong centimeters -> millimeters conversion")
64 }
65}
66
67func TestDistanceParseAgainstConstants(t *testing.T) {
68 testcases := []struct {
69 string string
70 expected Distance
71 }{
72 {"1mi", 1 * Mile},
73 {"1km", 1 * Kilometer},
74 {"1m", 1 * Meter},
75 {"1yd", 1 * Yard},
76 {"1ft", 1 * Foot},
77 {"1in", 1 * Inch},
78 {"1cm", 1 * Centimeter},
79 {"1mm", 1 * Millimeter},
80 {"0.1mi", 0.1 * Mile},
81 {"0.1km", 0.1 * Kilometer},
82 {"0.1m", 0.1 * Meter},
83 {"0.1yd", 0.1 * Yard},
84 {"0.1ft", 0.1 * Foot},
85 {"0.1in", 0.1 * Inch},
86 {"0.1cm", 0.1 * Centimeter},
87 {"0.1mm", 0.1 * Millimeter},
88 {"-18.2mi", -18.2 * Mile},
89 {"-18.2km", -18.2 * Kilometer},
90 {"-18.2m", -18.2 * Meter},
91 {"-18.2yd", -18.2 * Yard},
92 {"-18.2ft", -18.2 * Foot},
93 {"-18.2in", -18.2 * Inch},
94 {"-18.2cm", -18.2 * Centimeter},
95 {"-18.2mm", -18.2 * Millimeter},
96 {"10mi9m", 10*Mile + 9*Meter},
97 {"10km9m8cm", 10*Kilometer + 9*Meter + 8*Centimeter},
98 {"10m9m8m", 27 * Meter},
99 {"-1cm100cm20cm2m", -1 * (2*Meter + 121*Centimeter)},
100 }
101 for _, tc := range testcases {
102 d, err := ParseDistance(tc.string)
103 if err != nil {
104 t.Errorf("ParseDistance(%q) returned error: %v", tc.string, err)
105 continue
106 }
107 if !floatEq(d.Millimeters(), tc.expected.Millimeters()) {
108 t.Errorf("ParseDistance(%q).Millimeters() = %v; want %v", tc.string, d.Millimeters(), tc.expected.Millimeters())
109 }
110 }
111}
112
113func TestDistanceParseAgainstParse(t *testing.T) {
114 testcases := []struct {
115 first, second string
116 }{
117 // Singles.
118 {"1mi", "5280ft"},
119 {"1km", "1000m"},
120 {"1m", "100cm"},
121 {"1yd", "3ft"},
122 {"1ft", "12in"},
123 {"1in", "2.54cm"},
124 {"1cm", "10mm"},
125 {"-1cm", "-10mm"},
126 {"10mi", "52800ft"},
127 {"10km", "10000m"},
128 {"10m", "1000cm"},
129 {"10yd", "30ft"},
130 {"10ft", "120in"},
131 {"10in", "25.4cm"},
132 {"10cm", "100mm"},
133 {"-10cm", "-100mm"},
134 {"0.1mi", "528ft"},
135 {"0.1km", "100m"},
136 {"0.1m", "10cm"},
137 {"0.1yd", "0.3ft"},
138 {"0.1ft", "1.2in"},
139 {"0.1in", "0.254cm"},
140 {"0.1cm", "1mm"},
141 {"-0.1cm", "-1mm"},
142 // Pairs.
143 {"1mi1km", "1mi1000m"},
144 {"1mi1m", "1mi100cm"},
145 {"1mi1yd", "1mi3ft"},
146 {"1mi1ft", "5281ft"},
147 {"1mi1in", "5280ft1in"},
148 {"1mi1cm", "5280ft10mm"},
149 {"1mi1mm", "5280ft1mm"},
150 {"-1mi1mm", "-5280ft1mm"},
151 {"1km1m", "1001m"},
152 {"1km1yd", "1000m3ft"},
153 {"1km1ft", "1000m12in"},
154 {"1km1in", "1000m2.54cm"},
155 {"1km1cm", "1000m10mm"},
156 {"1km1mm", "1000m0.1cm"},
157 {"-1km1mm", "-1000m0.1cm"},
158 {"1m1yd", "3ft100cm"},
159 {"1m1ft", "12in100cm"},
160 {"1m1in", "102.54cm"},
161 {"1m1cm", "101cm"},
162 {"1m1mm", "1001mm"},
163 {"-1m1mm", "-1001mm"},
164 {"1yd1ft", "4ft"},
165 {"1yd1in", "3ft1in"},
166 {"1yd1cm", "3ft10mm"},
167 {"1yd1mm", "3ft0.1cm"},
168 {"-1yd1mm", "-3ft0.1cm"},
169 {"1ft1in", "13in"},
170 {"1ft1cm", "12in10mm"},
171 {"1ft1mm", "12in0.1cm"},
172 {"-1ft1mm", "-12in0.1cm"},
173 {"1in1cm", "1in10mm"},
174 {"1in1mm", "1in0.1cm"},
175 {"-1in1mm", "-1in0.1cm"},
176 {"1cm1mm", "11mm"},
177 // Complex.
178 {"1mi1km1m1yd1ft1in1cm1mm", "1001m5284ft1in11mm"},
179 {"-1mi1km1m1yd1ft1in1cm1mm", "-1001m5284ft1in11mm"},
180 // CAPS.
181 {"1Mi", "1mI"},
182 {"1mi", "1MI"},
183 {"1Km", "1kM"},
184 {"1km", "1KM"},
185 {"1M", "1m"},
186 {"1Yd", "1yD"},
187 {"1yd", "1YD"},
188 {"1Ft", "1fT"},
189 {"1ft", "1FT"},
190 {"1In", "1iN"},
191 {"1in", "1IN"},
192 {"1Cm", "1cM"},
193 {"1cm", "1CM"},
194 {"1Mm", "1mM"},
195 {"1mm", "1MM"},
196 }
197 for _, tc := range testcases {
198 first, err := ParseDistance(tc.first)
199 if err != nil {
200 t.Errorf("ParseDistance(%q) returned error: %v", tc.first, err)
201 continue
202 }
203 second, err := ParseDistance(tc.second)
204 if err != nil {
205 t.Errorf("ParseDistance(%q) returned error: err", tc.second, err)
206 continue
207 }
208 if !floatEq(first.Millimeters(), second.Millimeters()) {
209 t.Errorf("ParseDistance(%q).Millimeters() = %v; want %v = ParseDistance(%q).Millimeters()", tc.first, first.Millimeters(), second.Millimeters(), tc.second)
210 }
211 }
212}
213
214func TestDistanceString(t *testing.T) {
215 testcases := []struct {
216 d Distance
217 expected string
218 }{
219 {1 * Kilometer, "1km0m0mm"},
220 {1 * Meter, "1m0mm"},
221 {1 * Millimeter, "1mm"},
222 {1200 * Meter, "1km200m0mm"},
223 {1.3 * Kilometer, "1km300m0mm"},
224 {-2.7 * Kilometer, "-2km700m0mm"},
225 {5.33 * Millimeter, "5.33mm"},
226 {3.0000000000001 * Millimeter, "3.0000000000001mm"},
227 {9*Kilometer + 12*Meter, "9km12m0mm"},
228 {9*Kilometer - 12*Meter, "8km988m0mm"},
229 {12*Meter - 9*Kilometer, "-8km988m0mm"},
230 {9*Kilometer + 2222*Meter + 1024.5*Millimeter, "11km223m24.5mm"},
231 {-1*Kilometer - 3200.3*Meter - 22*Millimeter, "-4km200m322mm"},
232 }
233 for _, tc := range testcases {
234 if tc.d.String() != tc.expected {
235 t.Errorf("Got string %q; want %q", tc.d, tc.expected)
236 }
237 }
238}
239
240func TestDistanceParseError(t *testing.T) {
241 testcases := []string{
242 "1kf",
243 "1fm",
244 "1kmm",
245 "m",
246 "cm",
247 "1ft 1mi",
248 " 1ft 1mi",
249 "1ft 1mi ",
250 "1ft 1mi",
251 "1mi-4km",
252 "-3km-4m",
253 }
254 for _, ds := range testcases {
255 if d, err := ParseDistance(ds); err == nil {
256 t.Errorf("ParseDistance(%q) = %q; want error", ds, d)
257 }
258 }
259}