4 using System
.Data
.SqlClient
;
5 using System
.Collections
.Generic
;
11 // Several mono bugs worked around in this test fixture are filed as mono bug
14 public class VersaplexTester
: IDisposable
16 // A file full of "lorem ipsum dolor" text
17 private const string lipsum_file
= "lipsum.txt";
19 private const string unicode_file
= "UTF-8-demo.txt";
20 // A random file of binary goop
21 private const string goop_file
= "random.bin";
23 private const string image_file
= "thtbacs.tiff";
28 public VersaplexTester()
30 // Places to look for the config file.
31 string [] searchfiles
=
34 Path
.Combine("..", "versaplexd.ini")
37 string cfgfile
= null;
38 foreach (string searchfile
in searchfiles
)
39 if (File
.Exists(searchfile
))
43 throw new Exception("Cannot locate versaplexd.ini.");
45 WvIni cfg
= new WvIni(cfgfile
);
47 string uname
= Mono
.Unix
.UnixUserInfo
.GetRealUser().UserName
;
48 string dbname
= cfg
.get("User Map", uname
);
50 dbname
= cfg
.get("User Map", "*");
52 throw new Exception(String
.Format(
53 "User '{0}' (and '*') missing from config.", uname
));
55 string cfgval
= cfg
.get("Connections", dbname
);
57 throw new Exception(String
.Format(
58 "Connection string for '{0}' missing from config.", dbname
));
60 dbi
= WvDbi
.create(cfgval
);
62 if (Address
.Session
== null)
63 throw new Exception ("DBUS_SESSION_BUS_ADDRESS not set");
64 AddressEntry aent
= AddressEntry
.Parse(Address
.Session
);
65 DodgyTransport trans
= new DodgyTransport();
76 public static string GetTempDir()
78 WvLog log
= new WvLog("GetTempDir");
79 string t
= Path
.Combine(Path
.GetTempPath(), Path
.GetRandomFileName());
80 log
.print("Using temporary directory " + t
+ "\n");
85 internal bool Exec(string query
)
87 Console
.WriteLine(" + Exec SQL Query: {0}", query
);
92 internal bool Scalar(string query
, out object result
)
94 Console
.WriteLine(" + Scalar SQL Query: {0}", query
);
95 result
= dbi
.select_one(query
).inner
;
99 internal WvSqlRows
Reader(string query
)
101 Console
.WriteLine(" + Reader SQL Query: {0}", query
);
102 return dbi
.select(query
);
105 internal bool VxExec(string query
)
107 Console
.WriteLine(" + VxExec SQL Query: {0}", query
);
109 Message call
= VxDbusUtils
.CreateMethodCall(bus
, "ExecRecordset", "s");
111 MessageWriter mw
= new MessageWriter(Connection
.NativeEndianness
);
112 mw
.Write(typeof(string), query
);
114 call
.Body
= mw
.ToArray();
116 Message reply
= bus
.SendWithReplyAndBlock(call
);
118 switch (reply
.Header
.MessageType
) {
119 case MessageType
.MethodReturn
:
121 case MessageType
.Error
:
124 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.ErrorName
,
126 throw new Exception("D-Bus error received but no error name "
130 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.Signature
,
131 out errsig
) || errsig
.ToString() != "s")
132 throw new DbusError(errname
.ToString());
134 MessageReader mr
= new MessageReader(reply
);
136 string errmsg
= mr
.ReadString();
138 throw new DbusError(errname
.ToString() + ": " + errmsg
.ToString());
141 throw new Exception("D-Bus response was not a method return or "
146 internal bool VxScalar(string query
, out object result
)
148 Console
.WriteLine(" + VxScalar SQL Query: {0}", query
);
150 Message call
= VxDbusUtils
.CreateMethodCall(bus
, "ExecScalar", "s");
152 MessageWriter mw
= new MessageWriter(Connection
.NativeEndianness
);
153 mw
.Write(typeof(string), query
);
155 call
.Body
= mw
.ToArray();
157 Message reply
= bus
.SendWithReplyAndBlock(call
);
159 switch (reply
.Header
.MessageType
) {
160 case MessageType
.MethodReturn
:
163 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.Signature
,
165 throw new Exception("D-Bus reply had no signature");
167 if (replysig
== null || replysig
.ToString() != "v")
168 throw new Exception("D-Bus reply had invalid signature");
170 MessageReader reader
= new MessageReader(reply
);
171 result
= reader
.ReadVariant();
175 case MessageType
.Error
:
178 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.ErrorName
,
180 throw new Exception("D-Bus error received but no error name "
184 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.Signature
,
185 out errsig
) || errsig
.ToString() != "s")
186 throw new DbusError(errname
.ToString());
188 MessageReader mr
= new MessageReader(reply
);
190 object errmsg
= mr
.ReadString();
192 throw new DbusError(errname
.ToString() + ": " + errmsg
.ToString());
195 throw new Exception("D-Bus response was not a method return or "
200 // Read the standard issnny signature for column information. We can't
201 // just read a VxColumnInfo[] straight from the reader any more, as the
202 // format of VxColumnInfo differs from the format on the wire.
203 internal VxColumnInfo
[] ReadColInfo(MessageReader reader
)
205 List
<VxColumnInfo
> colinfolist
= new List
<VxColumnInfo
>();
207 int colinfosize
= reader
.ReadInt32();
208 int endpos
= reader
.Position
+ colinfosize
;
209 while (reader
.Position
< endpos
)
212 int size
= reader
.ReadInt32();
213 string colname
= reader
.ReadString();
214 string coltype_str
= reader
.ReadString();
215 short precision
= reader
.ReadInt16();
216 short scale
= reader
.ReadInt16();
217 byte nullable
= reader
.ReadByte();
219 VxColumnType coltype
= (VxColumnType
)Enum
.Parse(
220 typeof(VxColumnType
), coltype_str
, true);
222 Console
.WriteLine("Read colname={0}, coltype={1}, nullable={2}, " +
223 "size={3}, precision={4}, scale={5}",
224 colname
, coltype
.ToString(), nullable
, size
, precision
, scale
);
226 colinfolist
.Add(new VxColumnInfo(colname
, coltype
, nullable
> 0,
227 size
, precision
, scale
));
230 return colinfolist
.ToArray();
233 internal bool VxChunkRecordset(string query
, out VxColumnInfo
[] colinfo
,
234 out object[][]data
, out bool[][] nullity
)
236 Console
.WriteLine(" + VxChunkRecordset SQL Query: {0}", query
);
238 Message call
= VxDbusUtils
.CreateMethodCall(bus
, "ExecChunkRecordset", "s");
239 //call.Header.Flags = HeaderFlag.NoReplyExpected | HeaderFlag.NoAutoStart;
241 MessageWriter mw
= new MessageWriter(Connection
.NativeEndianness
);
242 mw
.Write(typeof(string), query
);
244 call
.Body
= mw
.ToArray();
249 List
<object[]> rowlist
= new List
<object[]>();
250 List
<bool[]> rownulllist
= new List
<bool[]>();
255 Message tmp
= bus
.GetNext();
256 if (tmp
.Header
.MessageType
== MessageType
.Signal
)
258 RecordsetWorker(tmp
, out colinfo
, out tdata
, out tnullity
);
259 rowlist
.AddRange(tdata
);
260 rownulllist
.AddRange(tnullity
);
262 else if (tmp
.Header
.MessageType
== MessageType
.Error
)
265 if (!tmp
.Header
.Fields
.TryGetValue(FieldCode
.ErrorName
,
267 throw new Exception("D-Bus error received but no error "
271 if (!tmp
.Header
.Fields
.TryGetValue(FieldCode
.Signature
,
272 out errsig
) || errsig
.ToString() != "s")
273 throw new DbusError(errname
.ToString());
275 MessageReader mr
= new MessageReader(tmp
);
277 string errmsg
= mr
.ReadString();
278 throw new DbusError(errname
.ToString() + ": " + errmsg
);
284 if (!tmp
.Header
.Fields
.TryGetValue(FieldCode
.Signature
,
285 out retsig
) || retsig
.ToString() != "s")
286 throw new DbusError("Garbled response for ExecChunkRecordSet");
287 //otherwise, we presume it's our method return response
288 data
= rowlist
.ToArray();
289 nullity
= rownulllist
.ToArray();
297 internal bool RecordsetWorker(Message reply
, out VxColumnInfo
[] colinfo
,
298 out object[][] data
, out bool[][] nullity
)
301 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.Signature
,
303 throw new Exception("D-Bus reply had no signature");
305 if (replysig
== null || !replysig
.ToString().StartsWith("a(issnny)vaay"))
306 throw new Exception("D-Bus reply had invalid signature");
308 MessageReader reader
= new MessageReader(reply
);
310 // Read the column information
311 colinfo
= ReadColInfo(reader
);
313 reader
.ReadSignature();
315 // TODO: Check that sig matches colinfo
316 // Sig should be of the form a(...)
318 int arraysz
= reader
.ReadInt32();
320 // The header is 8-byte aligned
322 int endpos
= reader
.Position
+ arraysz
;
324 List
<object[]> results
= new List
<object[]>();
325 while (reader
.Position
< endpos
) {
326 object[] row
= new object[colinfo
.Length
];
328 // Each structure element is 8-byte aligned
331 for (int i
=0; i
< row
.Length
; i
++) {
332 switch (colinfo
[i
].VxColumnType
) {
333 case VxColumnType
.Int64
:
335 Console
.WriteLine("Reading Int64 from pos {0}",
337 long cell
= reader
.ReadInt64();
341 case VxColumnType
.Int32
:
343 Console
.WriteLine("Reading Int32 from pos {0}",
345 int cell
= reader
.ReadInt32();
349 case VxColumnType
.Int16
:
351 Console
.WriteLine("Reading Int16 from pos {0}",
353 short cell
= reader
.ReadInt16();
357 case VxColumnType
.UInt8
:
359 Console
.WriteLine("Reading UInt8 from pos {0}",
361 byte cell
= reader
.ReadByte();
365 case VxColumnType
.Bool
:
367 Console
.WriteLine("Reading Bool from pos {0}",
369 bool cell
= reader
.ReadBoolean();
373 case VxColumnType
.Double
:
375 Console
.WriteLine("Reading Double from pos {0}",
377 double cell
= reader
.ReadDouble();
381 case VxColumnType
.Uuid
:
383 Console
.WriteLine("Reading UUID from pos {0}",
385 string cell
= reader
.ReadString();
390 row
[i
] = new Guid(cell
);
394 case VxColumnType
.Binary
:
396 Console
.WriteLine("Reading Binary from pos {0}",
398 object cell
= reader
.ReadArray
<byte>();
402 case VxColumnType
.String
:
404 Console
.WriteLine("Reading string from pos {0}",
406 string cell
= reader
.ReadString();
410 case VxColumnType
.DateTime
:
412 Console
.WriteLine("Reading DateTime from pos {0}",
415 long seconds
= reader
.ReadInt64();
416 int microseconds
= reader
.ReadInt32();
418 VxDbusDateTime dt
= new VxDbusDateTime();
419 dt
.seconds
= seconds
;
420 dt
.microseconds
= microseconds
;
425 case VxColumnType
.Decimal
:
427 Console
.WriteLine("Reading Decimal from pos {0}",
429 string cell
= reader
.ReadString();
432 row
[i
] = new Decimal();
434 row
[i
] = Decimal
.Parse(cell
);
439 throw new Exception("Invalid column type received");
446 WVPASSEQ(reader
.Position
, endpos
);
447 if (reader
.Position
!= endpos
)
448 throw new Exception("Position mismatch after reading data");
450 data
= results
.ToArray();
452 object rawnulls
= reader
.ReadArray
<byte[]>();
454 byte[][] rawnulls_typed
= (byte[][])rawnulls
;
456 nullity
= new bool[rawnulls_typed
.Length
][];
458 for (int i
=0; i
< rawnulls_typed
.Length
; i
++) {
459 nullity
[i
] = new bool[rawnulls_typed
[i
].Length
];
461 for (int j
=0; j
< rawnulls_typed
[i
].Length
; j
++) {
462 nullity
[i
][j
] = (rawnulls_typed
[i
][j
] == 0) ? false : true;
469 internal bool VxRecordset(string query
, out VxColumnInfo
[] colinfo
,
470 out object[][] data
, out bool[][] nullity
)
472 Console
.WriteLine(" + VxReader SQL Query: {0}", query
);
474 Message call
= VxDbusUtils
.CreateMethodCall(bus
, "ExecRecordset", "s");
476 MessageWriter mw
= new MessageWriter(Connection
.NativeEndianness
);
477 mw
.Write(typeof(string), query
);
479 call
.Body
= mw
.ToArray();
481 Message reply
= bus
.SendWithReplyAndBlock(call
);
483 switch (reply
.Header
.MessageType
) {
484 case MessageType
.MethodReturn
:
486 return RecordsetWorker(reply
, out colinfo
, out data
, out nullity
);
488 case MessageType
.Error
:
491 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.ErrorName
,
493 throw new Exception("D-Bus error received but no error name "
497 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.Signature
,
498 out errsig
) || errsig
.ToString() != "s")
499 throw new DbusError(errname
.ToString());
501 MessageReader mr
= new MessageReader(reply
);
503 string errmsg
= mr
.ReadString();
505 throw new DbusError(errname
.ToString() + ": " + errmsg
.ToString());
508 throw new Exception("D-Bus response was not a method return or "
513 internal bool Insert(string table
, params object [] param
)
515 Console
.WriteLine(" + Insert to {0} ({1})", table
, String
.Join(", ",
516 wv
.stringify(param
)));
518 var query
= new StringBuilder();
519 query
.AppendFormat("INSERT INTO [{0}] VALUES (",
520 table
.Replace("]","]]"));
522 for (int i
=0; i
< param
.Length
; i
++)
527 if (param
[i
] is DBNull
)
528 query
.Append("NULL");
530 query
.AppendFormat("@col{0}", i
);
535 Console
.WriteLine(" ++ ({0})", query
.ToString());
537 dbi
.exec(query
.ToString(), param
);
541 internal string read_lipsum()
543 WVASSERT(File
.Exists(lipsum_file
));
545 using (StreamReader sr
= new StreamReader(lipsum_file
)) {
546 return sr
.ReadToEnd();
550 internal string read_unicode()
552 WVASSERT(File
.Exists(unicode_file
));
554 using (StreamReader sr
= new StreamReader(unicode_file
)) {
555 return sr
.ReadToEnd();
559 internal Byte
[] read_goop()
561 WVASSERT(File
.Exists(goop_file
));
563 using (FileStream f
= new FileStream(goop_file
, FileMode
.Open
,
565 using (BinaryReader sr
= new BinaryReader(f
)) {
566 return sr
.ReadBytes((int)Math
.Min(f
.Length
, Int32
.MaxValue
));
570 internal Byte
[] read_image()
572 WVASSERT(File
.Exists(image_file
));
574 using (FileStream f
= new FileStream(image_file
, FileMode
.Open
,
576 using (BinaryReader sr
= new BinaryReader(f
)) {
577 return sr
.ReadBytes((int)Math
.Min(f
.Length
, Int32
.MaxValue
));
581 internal long GetInt64(SqlDataReader reader
, int colnum
) {
582 // For some reason, it won't just up-convert int32 to int64
583 if (reader
.GetFieldType(colnum
) == typeof(System
.Int32
)) {
584 return reader
.GetInt32(colnum
);
585 } else if (reader
.GetFieldType(colnum
) == typeof(System
.Int64
)) {
586 return reader
.GetInt64(colnum
);
587 } else if (reader
.GetFieldType(colnum
) == typeof(System
.Decimal
)) {
588 return (long)reader
.GetDecimal(colnum
);
591 bool unknown_type_in_result
= true;
592 WVFAIL(unknown_type_in_result
);