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 }