1 module 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                          (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 @Values("foobar", "quux")
57 @("string.ascii")
58 unittest {
59     const value = getValue!string;
60     backAndForth(value);
61 }
62 
63 
64 @ShouldFail
65 @Values("café", "açaí")
66 @("string.unicode")
67 unittest {
68     const value = getValue!string;
69     backAndForth(value);
70 }
71 
72 @("stringz")
73 unittest {
74     import std.string: toStringz, fromStringz;
75     const value = "foobar".toStringz;
76     value.toPython.to!(typeof(value)).fromStringz.should == "foobar";
77 }
78 
79 @("array.static.int")
80 unittest {
81     int[2] value = [1, 2];
82     backAndForth(value);
83 }
84 
85 
86 @("array.static.double")
87 unittest {
88     double[3] value = [11.1, 22.2, 33.3];
89     backAndForth(value);
90 }
91 
92 
93 @("array.static.immutable(int)")
94 unittest {
95     immutable(int)[3] ints = [1, 2, 3];
96     backAndForth(ints);
97 }
98 
99 
100 @("array.slice.char")
101 unittest {
102     auto chars = ['a', 'b', 'c'];
103     backAndForth(chars);
104 }
105 
106 
107 @("array.slice.immutable(int)")
108 unittest {
109     immutable(int)[] ints = [1, 2, 3];
110     backAndForth(ints);
111 }
112 
113 @("aa.int.string")
114 unittest {
115     backAndForth([1: "foo", 2: "bar"]);
116 }
117 
118 
119 @("aa.string.int")
120 unittest {
121     backAndForth(["foo": 1, "bar": 2]);
122 }
123 
124 
125 @("tuple.int.int.int")
126 unittest {
127     backAndForth(tuple(3, 5, 7));
128 }
129 
130 
131 @("tuple.int.string")
132 unittest {
133     backAndForth(tuple(42, "foo"));
134 }
135 
136 
137 @("udt.intstring.val")
138 unittest {
139     backAndForth(IntString(42, "quux"));
140 }
141 
142 @("udt.intstring.ptr")
143 unittest {
144     const value = new IntString(42, "quux");
145     const back = value.toPython.to!(typeof(value));
146     (*back).should == *value;
147 }
148 
149 
150 @("udt.class")
151 unittest {
152 
153     static class Class {
154         int i;
155         this(int i) { this.i = i; }
156         int twice() @safe @nogc pure nothrow const { return i * 2; }
157         override string toString() @safe pure const {
158             import std.conv: text;
159             return text("Class(", i, ")");
160         }
161     }
162 
163     backAndForth(new Class(42));
164 }
165 
166 
167 @("udt.class.baseref")
168 unittest {
169     import std.conv: text;
170     import std.string: fromStringz;
171 
172     static class Base {
173         int i;
174         this() {}
175         this(int i) { this.i = i; }
176         int twice() @safe @nogc pure nothrow const { return i * 2; }
177         override string toString() @safe pure const {
178             import std.conv: text;
179             return text("Base(", i, ")");
180         }
181     }
182 
183     static class Child: Base {
184         this() {}
185         this(int i) { super(i); }
186         override string toString() @safe pure const {
187             import std.conv: text;
188             return text("Child(", i, ")");
189         }
190     }
191 
192 
193     auto child = new Child(42);
194     child.toString.should == "Child(42)";
195     auto python = child.toPython;
196     auto back = python.to!Base;
197     assert(typeid(back) == typeid(Child), text("Expected Child but got ", typeid(back)));
198     back.toString.should == "Child(42)";
199 }
200 
201 
202 @("udt.struct.ptr.uncopiable")
203 unittest {
204     static struct Uncopiable {
205         @disable this(this);
206         int i;
207     }
208     const value = new Uncopiable(42);
209     const back = value.toPython.to!(typeof(value));
210 }
211 
212 
213 @("udt.struct.char")
214 unittest {
215     static struct Char {
216         char c;
217     }
218     backAndForth(Char('a'));
219 }
220 
221 
222 @("udt.struct.charptr")
223 unittest {
224     static struct CharPtr {
225         char* ptr;
226         bool opEquals(in CharPtr other) @safe @nogc pure nothrow const {
227             if(ptr  is null  && other.ptr  is null) return true;
228             if(ptr  is null  && other.ptr !is null) return false;
229             if(ptr !is null  && other.ptr  is null) return false;
230             return *ptr == *other.ptr;
231         }
232     }
233     backAndForth(CharPtr(new char('a')));
234 }
235 
236 
237 // Similar to issue with std.bitmanip.BitArray.
238 // The presence of `opCast` makes the conversion to `const(T)` fail
239 // since there's no available cast to `const(T)`.
240 @("udt.struct.opCast")
241 unittest {
242     static struct Struct {
243 
244         void[] opCast(T: void[])() @safe @nogc pure nothrow {
245             return null;
246         }
247 
248         size_t[] opCast(T: size_t[])() @safe @nogc pure nothrow {
249             return null;
250         }
251     }
252 
253     const s = Struct();
254     backAndForth(s);
255 }
256 
257 
258 @ShouldFail("D function pointers not yet supported")
259 @("function")
260 unittest {
261     static string fun(int i, double d) {
262         import std.conv: text;
263         return text("i: ", i, " d: ", d);
264     }
265     const back = (&fun).toPython.to!(typeof(&fun));
266 }
267 
268 
269 @("Duration")
270 unittest {
271     import core.time: seconds;
272     backAndForth(1.seconds);
273 }
274 
275 
276 @("enum.int")
277 unittest {
278     static enum Enum {
279         foo,
280         bar,
281         baz,
282     }
283 
284     backAndForth(Enum.bar);
285 }
286 
287 
288 @("enum.char")
289 unittest {
290     static enum Char: char {
291         a = 'a',
292         b = 'b',
293     }
294 
295     backAndForth(Char.b);
296 }
297 
298 
299 @("dchar")
300 unittest {
301     const str = "foobar"d;
302     backAndForth(str[0]);
303 }
304 
305 
306 @("range.infinite")
307 unittest {
308 
309     import std.range.primitives: isForwardRange;
310 
311     // infinite range
312     static struct FortyTwos {
313         int i;
314         enum empty = false;
315         int front() { return 42; }
316         void popFront() {}
317         auto save() { return this; }
318         string toString() {
319             import std.conv: text;
320             return i.text;
321         }
322     }
323 
324     static assert(isForwardRange!FortyTwos);
325     auto py = FortyTwos(77).toPython;
326     auto d = py.to!FortyTwos;
327     d.i.should == 77;
328 }
329 
330 
331 @("exception.msg")
332 unittest {
333     static class MyException: Exception {
334         // These members make no sense, but that's what
335         // std.xml.CheckException does
336         this(string msg) { super(msg); }
337         string msg;
338         size_t line;
339     }
340 
341     // FIXME - cannot deal with repeated field names
342     // auto py = (new MyException("oops")).toPython;
343 }