1 module ut.pynih.python.conv;
2 
3 
4 import unit_threaded;
5 import python.conv;
6 import std.datetime: Date, DateTime;
7 import std.typecons: tuple;
8 
9 
10 struct IntString {
11     int i;
12     string s;
13 }
14 
15 
16 // helper function to test that converting to python and back yields the same value
17 private void backAndForth(T)
18                          (auto ref T value, in string file = __FILE__, in size_t line = __LINE__)
19 {
20     value.toPython.to!(typeof(value)).shouldEqual(value, file, line);
21 }
22 
23 
24 @Types!(
25     bool,
26     byte, ubyte, short, ushort, int, uint, long, ulong,  // integral
27     float, double,
28 )
29 void testBackAndForthNoGc(T)()
30 {
31     check!((T d) @nogc => d.toPython.to!T == d);
32 }
33 
34 
35 @Types!(
36     int[], double[],
37 )
38 void testBackAndForthGc(T)()
39 {
40     check!((T d) => d.toPython.to!T == d);
41 }
42 
43 
44 @("DateTime")
45 unittest {
46     backAndForth(DateTime(2018, 1, 2, 3, 4, 5));
47 }
48 
49 
50 @("Date")
51 unittest {
52     backAndForth(Date(2018, 1, 2));
53 }
54 
55 
56 @("TimeOfDay")
57 unittest {
58     import std.datetime : TimeOfDay;
59     backAndForth(TimeOfDay(15, 49, 34));
60 }
61 
62 
63 // FIXME - crashes dmd 2.100.1
64 // @Values("foobar", "quux")
65 // @("string.ascii")
66 // unittest {
67 //     const value = getValue!string;
68 //     backAndForth(value);
69 // }
70 
71 
72 // FIXME - crashes dmd 2.100.1
73 // @ShouldFail
74 // @Values("café", "açaí")
75 // @("string.unicode")
76 // unittest {
77 //     const value = getValue!string;
78 //     backAndForth(value);
79 // }
80 
81 @("stringz")
82 unittest {
83     import std.string: toStringz, fromStringz;
84     const value = "foobar".toStringz;
85     value.toPython.to!(typeof(value)).fromStringz.should == "foobar";
86 }
87 
88 @("array.static.int")
89 unittest {
90     int[2] value = [1, 2];
91     backAndForth(value);
92 }
93 
94 
95 @("array.static.double")
96 unittest {
97     double[3] value = [11.1, 22.2, 33.3];
98     backAndForth(value);
99 }
100 
101 
102 @("array.static.immutable(int)")
103 unittest {
104     immutable(int)[3] ints = [1, 2, 3];
105     backAndForth(ints);
106 }
107 
108 
109 @("array.slice.char")
110 unittest {
111     auto chars = ['a', 'b', 'c'];
112     backAndForth(chars);
113 }
114 
115 
116 @("array.slice.immutable(int)")
117 unittest {
118     immutable(int)[] ints = [1, 2, 3];
119     backAndForth(ints);
120 }
121 
122 @("aa.int.string")
123 unittest {
124     backAndForth([1: "foo", 2: "bar"]);
125 }
126 
127 
128 @("aa.string.int")
129 unittest {
130     backAndForth(["foo": 1, "bar": 2]);
131 }
132 
133 
134 @("tuple.int.int.int")
135 unittest {
136     backAndForth(tuple(3, 5, 7));
137 }
138 
139 
140 @("tuple.int.string")
141 unittest {
142     backAndForth(tuple(42, "foo"));
143 }
144 
145 
146 @("udt.intstring.val")
147 unittest {
148     backAndForth(IntString(42, "quux"));
149 }
150 
151 @("udt.intstring.ptr")
152 unittest {
153     const value = new IntString(42, "quux");
154     const back = value.toPython.to!(typeof(value));
155     (*back).should == *value;
156 }
157 
158 
159 @("udt.class")
160 unittest {
161 
162     static class Class {
163         int i;
164         this(int i) { this.i = i; }
165         int twice() @safe @nogc pure nothrow const { return i * 2; }
166         override string toString() @safe pure const {
167             import std.conv: text;
168             return text("Class(", i, ")");
169         }
170     }
171 
172     backAndForth(new Class(42));
173 }
174 
175 
176 @("udt.class.baseref")
177 unittest {
178     import std.conv: text;
179     import std.string: fromStringz;
180 
181     static class Base {
182         int i;
183         this() {}
184         this(int i) { this.i = i; }
185         int twice() @safe @nogc pure nothrow const { return i * 2; }
186         override string toString() @safe pure const {
187             import std.conv: text;
188             return text("Base(", i, ")");
189         }
190     }
191 
192     static class Child: Base {
193         this() {}
194         this(int i) { super(i); }
195         override string toString() @safe pure const {
196             import std.conv: text;
197             return text("Child(", i, ")");
198         }
199     }
200 
201 
202     auto child = new Child(42);
203     child.toString.should == "Child(42)";
204     auto python = child.toPython;
205     auto back = python.to!Base;
206     assert(typeid(back) == typeid(Child), text("Expected Child but got ", typeid(back)));
207     back.toString.should == "Child(42)";
208 }
209 
210 
211 @("udt.struct.ptr.uncopiable")
212 unittest {
213     static struct Uncopiable {
214         @disable this(this);
215         int i;
216     }
217     const value = new Uncopiable(42);
218     const back = value.toPython.to!(typeof(value));
219 }
220 
221 
222 @("udt.struct.char")
223 unittest {
224     static struct Char {
225         char c;
226     }
227     backAndForth(Char('a'));
228 }
229 
230 
231 @("udt.struct.charptr")
232 unittest {
233     static struct CharPtr {
234         char* ptr;
235         bool opEquals(in CharPtr other) @safe @nogc pure nothrow const {
236             if(ptr  is null  && other.ptr  is null) return true;
237             if(ptr  is null  && other.ptr !is null) return false;
238             if(ptr !is null  && other.ptr  is null) return false;
239             return *ptr == *other.ptr;
240         }
241     }
242     backAndForth(CharPtr(new char('a')));
243 }
244 
245 
246 // Similar to issue with std.bitmanip.BitArray.
247 // The presence of `opCast` makes the conversion to `const(T)` fail
248 // since there's no available cast to `const(T)`.
249 @("udt.struct.opCast")
250 unittest {
251     static struct Struct {
252 
253         void[] opCast(T: void[])() @safe @nogc pure nothrow {
254             return null;
255         }
256 
257         size_t[] opCast(T: size_t[])() @safe @nogc pure nothrow {
258             return null;
259         }
260     }
261 
262     const s = Struct();
263     backAndForth(s);
264 }
265 
266 
267 // FIXME - causing linker errors
268 // @ShouldFail("D function pointers not yet supported")
269 // @("function")
270 // unittest {
271 //     static string fun(int i, double d) {
272 //         import std.conv: text;
273 //         return text("i: ", i, " d: ", d);
274 //     }
275 //     const back = (&fun).toPython.to!(typeof(&fun));
276 // }
277 
278 
279 @("duration")
280 unittest {
281     import core.time: seconds;
282     backAndForth(1.seconds);
283 }
284 
285 
286 @("enum.int")
287 unittest {
288     static enum Enum {
289         foo,
290         bar,
291         baz,
292     }
293 
294     backAndForth(Enum.bar);
295 }
296 
297 
298 @("enum.char")
299 unittest {
300     static enum Char: char {
301         a = 'a',
302         b = 'b',
303     }
304 
305     backAndForth(Char.b);
306 }
307 
308 
309 @("dchar")
310 unittest {
311     const str = "foobar"d;
312     backAndForth(str[0]);
313 }
314 
315 
316 @("range.infinite")
317 unittest {
318 
319     import std.range.primitives: isForwardRange;
320 
321     // infinite range
322     static struct FortyTwos {
323         int i;
324         enum empty = false;
325         int front() { return 42; }
326         void popFront() {}
327         auto save() { return this; }
328         string toString() {
329             import std.conv: text;
330             return i.text;
331         }
332     }
333 
334     static assert(isForwardRange!FortyTwos);
335     auto py = FortyTwos(77).toPython;
336     auto d = py.to!FortyTwos;
337     d.i.should == 77;
338 }
339 
340 
341 @("exception.msg")
342 unittest {
343     static class MyException: Exception {
344         // These members make no sense, but that's what
345         // std.xml.CheckException does
346         this(string msg) { super(msg); }
347         string msg;
348         size_t line;
349     }
350 
351     // FIXME - cannot deal with repeated field names
352     // auto py = (new MyException("oops")).toPython;
353 }
354 
355 
356 @("Nullable!Date")
357 unittest {
358     import std.typecons: Nullable;
359     import std.datetime: Date;
360     auto py = Nullable!Date.init.toPython;
361 }
362 
363 
364 @("method.shared")
365 unittest {
366     static struct Oops {
367         int func() shared { return 42; }
368     }
369 
370     auto py = (shared Oops()).toPython;
371 }
372 
373 
374 @("member.pointer")
375 unittest {
376 
377     static struct Uncopiable {
378         @disable this(this);
379         @disable void opAssign(Uncopiable);
380         size_t count;
381     }
382 
383     static struct Outer {
384         Uncopiable inner;
385     }
386 
387     // this was a bug because to!Outer didn't compile
388     const back = Outer(Uncopiable(42)).toPython.to!Outer;
389     back.inner.count.should == 42;
390 }