1 module tests.qvariant_tests; 2 3 import quantities; 4 import quantities.internal.dimensions; 5 import std.exception; 6 import std.math : approxEqual; 7 8 enum meter = unit!double("L"); 9 enum second = unit!double("T"); 10 enum radian = unit!double(null); 11 12 void checkIncompatibleDimensions(E)(lazy E expression, QVariant!double lhs, QVariant!double rhs) 13 { 14 auto e = collectException!DimensionException(expression()); 15 assert(e, "No DimensionException was thrown"); 16 assert(e.thisDim == lhs.dimensions); 17 assert(e.otherDim == rhs.dimensions); 18 assert(e.msg == "Incompatible dimensions"); 19 } 20 21 void checkNotDimensionless(E)(lazy E expression, QVariant!double operand) 22 { 23 auto e = collectException!DimensionException(expression()); 24 assert(e, "No DimensionException was thrown"); 25 assert(e.thisDim == operand.dimensions); 26 assert(e.otherDim == Dimensions.init); 27 assert(e.msg == "Not dimensionless"); 28 } 29 30 @("this()") 31 @safe pure nothrow unittest 32 { 33 auto distance = 2 * meter; 34 auto angle = 3.14 * radian; 35 // No actual test 36 } 37 38 @("get/alias this for dimensionless values") 39 @safe pure unittest 40 { 41 double scalar = radian; 42 assert(scalar == 1); 43 } 44 45 @("value(Q)") 46 @safe pure unittest 47 { 48 auto distance = meter; 49 assert(distance.value(meter) == 1); 50 } 51 52 @("isDimensionless") 53 @safe pure nothrow unittest 54 { 55 assert(!meter.isDimensionless); 56 assert(radian.isDimensionless); 57 } 58 59 @("isConsistentWith") 60 @safe pure unittest 61 { 62 assert(meter.isConsistentWith(meter)); 63 assert(!meter.isConsistentWith(second)); 64 } 65 66 @("opCast") 67 @safe pure unittest 68 { 69 auto value = cast(double) radian; 70 checkNotDimensionless(cast(double) meter, meter); 71 } 72 73 @("opAssign Q") 74 @safe pure unittest 75 { 76 auto l1 = meter; 77 // QVariant allows assignment to a quantity with different dimensions 78 l1 = second; 79 } 80 81 @("opAssign T") 82 @safe pure unittest 83 { 84 auto angle = 1; 85 } 86 87 @("opUnary + -") 88 @safe pure unittest 89 { 90 auto plus = +meter; 91 assert(plus.value(meter) == 1); 92 auto minus = -meter; 93 assert(minus.value(meter) == -1); 94 } 95 96 @("opUnary ++ --") 97 @safe pure unittest 98 { 99 auto len = meter; 100 ++len; 101 assert(len.value(meter).approxEqual(2)); 102 assert((len++).value(meter).approxEqual(2)); 103 assert(len.value(meter).approxEqual(3)); 104 --len; 105 assert(len.value(meter).approxEqual(2)); 106 assert((len--).value(meter).approxEqual(2)); 107 assert(len.value(meter).approxEqual(1)); 108 } 109 110 @("opBinary Q+Q Q-Q") 111 @safe pure unittest 112 { 113 auto plus = meter + meter; 114 assert(plus.value(meter) == 2); 115 auto minus = meter - meter; 116 assert(minus.value(meter) == 0); 117 checkIncompatibleDimensions(meter + second, meter, second); 118 checkIncompatibleDimensions(meter - second, meter, second); 119 } 120 121 @("opBinary Q+N N+Q Q-N N-Q") 122 @safe pure unittest 123 { 124 auto a1 = radian + 10; 125 assert(a1.value(radian).approxEqual(11)); 126 auto a2 = radian - 10; 127 assert(a2.value(radian).approxEqual(-9)); 128 129 auto a3 = 10 + radian; 130 assert(a3.value(radian).approxEqual(11)); 131 auto a4 = 10 - radian; 132 assert(a4.value(radian).approxEqual(9)); 133 134 checkNotDimensionless(meter + 1, meter); 135 checkNotDimensionless(meter - 1, meter); 136 checkNotDimensionless(1 + meter, meter); 137 checkNotDimensionless(1 - meter, meter); 138 } 139 140 @("opBinary Q*N, N*Q, Q/N, N/Q, Q%N, N%Q") 141 @safe pure unittest 142 { 143 auto m1 = meter * 10; 144 assert(m1.value(meter).approxEqual(10)); 145 auto m2 = 10 * meter; 146 assert(m2.value(meter).approxEqual(10)); 147 auto m3 = meter / 10; 148 assert(m3.value(meter).approxEqual(0.1)); 149 auto m4 = 10 / meter; 150 assert(m4.dimensions == ~meter.dimensions); 151 assert(m4.value(1 / meter).approxEqual(10)); 152 auto m5 = m1 % 2; 153 assert(m5.value(meter).approxEqual(0)); 154 auto m6 = 10 % (2 * radian); 155 assert(m6.value(radian).approxEqual(0)); 156 } 157 158 @("opBinary Q*Q, Q/Q, Q%Q") 159 @safe pure unittest 160 { 161 auto surface = (10 * meter) * (10 * meter); 162 assert(surface.value(meter * meter).approxEqual(100)); 163 assert(surface.dimensions == meter.dimensions.pow(2)); 164 165 auto speed = (10 * meter) / (5 * second); 166 assert(speed.value(meter / second).approxEqual(2)); 167 assert(speed.dimensions == meter.dimensions / second.dimensions); 168 169 auto surfaceMod10 = surface % (10 * meter * meter); 170 assert(surfaceMod10.value(meter * meter).approxEqual(0)); 171 assert(surfaceMod10.dimensions == surface.dimensions); 172 173 checkIncompatibleDimensions(meter % second, meter, second); 174 } 175 176 @("opBinary Q^^I Q^^R") 177 @safe pure unittest 178 { 179 auto x = 2 * meter; 180 assert((x ^^ 3).value(meter * meter * meter).approxEqual(8)); 181 assert((x ^^ Rational(3)).value(meter * meter * meter).approxEqual(8)); 182 } 183 184 @("opOpAssign Q+=Q Q-=Q") 185 @safe pure unittest 186 { 187 auto time = 10 * second; 188 time += 50 * second; 189 assert(time.value(second).approxEqual(60)); 190 time -= 40 * second; 191 assert(time.value(second).approxEqual(20)); 192 } 193 194 @("opOpAssign Q*=N Q/=N Q%=N") 195 @safe pure unittest 196 { 197 auto time = 20 * second; 198 time *= 2; 199 assert(time.value(second).approxEqual(40)); 200 time /= 4; 201 assert(time.value(second).approxEqual(10)); 202 203 auto angle = 2 * radian; 204 angle += 4; 205 assert(angle.value(radian).approxEqual(6)); 206 angle -= 1; 207 assert(angle.value(radian).approxEqual(5)); 208 angle %= 2; 209 assert(angle.value(radian).approxEqual(1)); 210 211 checkNotDimensionless(time %= 3, time); 212 } 213 214 @("opOpAssign Q*=Q Q/=Q Q%=Q") 215 @safe pure unittest 216 { 217 auto angle = 2 * radian; 218 angle *= 2 * radian; 219 assert(angle.value(radian).approxEqual(4)); 220 angle /= 2 * radian; 221 assert(angle.value(radian).approxEqual(2)); 222 223 auto qty = 100 * meter; 224 qty *= second; 225 qty /= 20 * second; 226 qty %= 5 * second; 227 assert(qty.value(meter / second).approxEqual(0)); 228 } 229 230 @("opEquals Q==Q Q==N") 231 @safe pure unittest 232 { 233 auto minute = 60 * second; 234 assert(minute == 60 * second); 235 assert(radian == 1); 236 237 checkIncompatibleDimensions(meter == second, meter, second); 238 checkNotDimensionless(meter == 1, meter); 239 } 240 241 @("opCmp Q<Q") 242 @safe pure unittest 243 { 244 auto minute = 60 * second; 245 auto hour = 60 * minute; 246 assert(second < minute); 247 assert(minute <= minute); 248 assert(hour > minute); 249 assert(hour >= hour); 250 251 checkIncompatibleDimensions(meter < second, meter, second); 252 } 253 254 @("opCmp Q<N") 255 @safe pure unittest 256 { 257 auto angle = 2 * radian; 258 assert(angle < 4); 259 assert(angle <= 2); 260 assert(angle > 1); 261 assert(angle >= 2); 262 263 checkNotDimensionless(meter < 1, meter); 264 } 265 266 @("toString") 267 unittest 268 { 269 import std.conv : text; 270 271 auto length = 12 * meter; 272 assert(length.text == "12 [L]", length.text); 273 } 274 275 @("immutable") 276 @safe pure unittest 277 { 278 immutable inch = 0.0254 * meter; 279 immutable minute = 60 * second; 280 immutable speed = inch / minute; 281 } 282 283 @("square/sqrt") 284 @safe unittest 285 { 286 auto m2 = square(3 * meter); 287 assert(m2.value(meter * meter).approxEqual(9)); 288 auto m = sqrt(m2); 289 assert(m.value(meter).approxEqual(3)); 290 } 291 292 @("cubic/cbrt") 293 @safe unittest 294 { 295 auto m3 = cubic(2 * meter); 296 assert(m3.value(meter * meter * meter).approxEqual(8)); 297 298 // Doesn't work at compile time 299 auto m = cbrt(m3); 300 assert(m.value(meter).approxEqual(2)); 301 } 302 303 @("pow/nthRoot") 304 @safe unittest 305 { 306 auto m5 = pow(2 * meter, 5); 307 assert(m5.value(meter * meter * meter * meter * meter).approxEqual(2 ^^ 5)); 308 309 auto m = nthRoot(m5, 5); 310 assert(m.value(meter).approxEqual(2)); 311 } 312 313 @("abs") 314 @safe unittest 315 { 316 assert(abs(-meter) == meter); 317 } 318 319 @("parseSI string") 320 unittest 321 { 322 import quantities.si; 323 324 auto resistance = parseSI("1000 m^2 kg s^-3 A^-2"); 325 assert(resistance == 1000 * ohm); 326 } 327 328 @("parseSI wstring") 329 unittest 330 { 331 import quantities.si; 332 333 auto resistance = parseSI("1000 m^2 kg s^-3 A^-2"w); 334 assert(resistance == 1000 * ohm); 335 } 336 337 @("parseSI dstring") 338 unittest 339 { 340 import quantities.si; 341 342 auto resistance = parseSI("1000 m^2 kg s^-3 A^-2"d); 343 assert(resistance == 1000 * ohm); 344 }