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 }