Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 1 | package unit |
| 2 | |
| 3 | import ( |
| 4 | "math" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | func floatEq(first, second float64) bool { |
| 9 | const maxPrecisionError = 0.00000001 |
| 10 | return math.Abs(float64(first)-float64(second)) <= maxPrecisionError |
| 11 | } |
| 12 | |
| 13 | func 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 | |
| 29 | func 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 | |
| 45 | func 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 | |
| 67 | func 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 | |
| 113 | func 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 | |
| 214 | func 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 | |
| 240 | func 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 | } |